API Docs & Tools Blog Help
Benefits of SSO and how it works, indepth - Intuit Developer Community Blog

April 18, 2016 | admin

Benefits of SSO and how it works, indepth

It was a crisp summer morning and I was on a stroll down the alley to meet an old friend of mine. After an exchange of pleasantries, Jane wanted to finish up a quick task before heading out for coffee. Jane runs a small business and is pretty busy when it comes to running it. So, I nodded and sat down to watch the show. She was using two different websites. She had to create identity in both the websites independently. Because each site had its own policies around passwords, she had to remember multiple passwords (which she kept forgetting, btw), and all the things that come with independent identity management.

The only thing I could tell her was “Man … you need SSO.”

Incidentally, Jane also uses QuickBooks as her accounting software. So when she told me that later in the afternoon she would have to create a Hubdoc account and somehow connect that with QuickBooks, again my reaction was “Why don’t you just use SSO?” From within QuickBooks, she could simply click on the Hubdoc app from the Apps tab, and that’s it — no further sign-up required. She would be logged into Hubdoc. And finally we had our time for coffee!

What is SSO?

SSO, or Single Sign On, is a mechanism to help create the feeling of a single ecosystem across multiple services for the end user by sharing key elements of an identity.

For customers, it is a delight. For the app, it is a conversion rather than a lead, without any friction. It is a win-win for all stakeholders.

Now that we’ve established that SSO is a way to do touchless sign-up and sign-in to an app’s site, let’s look at the cost savings.

HubDoc try-buy from QuikBooks Online Apps Tab

Hubdoc subscribe from QuickBooks Online Apps Tab

Cost of user registration

As an app developer, you might ask “What is so complicated about implementing a user registration system?” After all, that is the most common interview question. So let’s take a look at the cost.

  • Password management: This includes the cost of coming up with algorithms around what constitutes a “strong” password.  If your app also happens to be global, then how the algorithms vary across various UTF-8 characters.
  • Multi-factor authentication (MFA): Consider how, these days, even large organizations are being hacked easily. It is imperative that we no longer depend solely on a simple username and password. This also assumes that you have things like “Captcha” implemented, to make sure it is not a bot, but a human, that is registering.
  • Account recovery: Assuming you have things like “Captcha” implemented to ensure the user is not a bot, you also need to implement security checks and measures to ensure only the rightful user can regain access to the account if they forget their password, etc.
  • Lead-to-sale conversion: For every action that you require your customer to do before they can get started using your app, there is a potential of drop-off.  Try to optimize the account setup and integration flow to keep the customer to the end of the process, and have the best chance of converting that lead to a sale.

Add all that up, and you will see what I mean by expensive.

OpenID as SSO

There are various SSO frameworks. In this post, I’m only talking about OpenID 2.0. I will touch upon the future of login OpenID Connect so you know it is a different protocol, even though there is similarity in the name.

How OpenID 2.0 works at Intuit

First, let’s get the terminology clear. The “relying party” (the app) relies on the OpenID provider (Intuit) to provide the single sign-on experience.

Relying Party (RP) – The app

OpenID Provider (OP) – Intuit

I will walk us through an example and we’ll talk about the various aspects of it. If you also want to read the spec, it is documented here.

Follow these steps:

openid-app-intuit

  1. App wants the user to sign-up / sign-in; obviously, the app has to do something. This is the “request authentication” step.
  2. The app and Intuit will use the UserAgent (usually the browser) as a means of communication with the user in context. This is the “indirect communication” mechanism in the OpenID spec.
  3. But before the app can redirect to Intuit, the app should go to Intuit and get a temporary identifier for this request. This is to verify that a specific request is coming to Intuit from a known app. This is called “association handle“.
  4. Then the app can redirect the end user to Intuit with that handle and the requested parameters using “extension types“.
  5. Intuit will then have to get consent from the user.
  6. After gaining user consent, the requested parameters are returned along with the handle and a primary key called “openid_claimed_id”.
  7. The app can then redirect to this “openid_claimed_id” URL to get a valid XRDS document.

 

Here is how it looks on the wire

First, the setup:

http://localhost:8011 – Where the sample app is (the RP (relying party)). Source code for this is here on git.

https://vm-openid.intuit.com – Where the OpenID server is, exactly similar to openid.intuit.com (the OP, OpenId Provider (Intuit)).

 

Step 1: App gets an associate handle

Done by the “initialize” function: here in the sample app.

Request:

POST https://vm-openid.intuit.com/OpenId/Provider HTTP/1.1
Content-Length: 355
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: vm-openid.intuit.com
Connection: Keep-Alive
openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=associate&openid.session_type=DH-SHA256&openid.assoc_type=HMAC-SHA256&openid.dh_consumer_public=db5AEcO7V%2BQHrCHe%2B4u693uUaC9CrVP6v8Kadkz6zb%2BtBhcZL6bFUFsJT9BXn4vNzCrxYNDARYhr8nsSY2CQkO%2FqicvsHnCdMBSV%2BADiNNjGIyfBBmGVrstcC7%2Bo7dSVt%2BnXN4rcbwKegDDm%2F%2FSkk%2F5RjmCCQ17njCiPqHdsD7s%3D

Response:

HTTP/1.1 200 OK
Cache-Control: private, s-maxage=0
Pragma: no-cache
Content-Type: application/x-openid-kvf
path=/
Access-Control-Allow-Origin: *.intuit.com
Date: Mon, 11 Apr 2016 23:33:06 GMT
Content-Length: 507

dh_server_public:AKPzQ4bQ2NPhffJ+l4Mu3Wk5UVozrQjzklACgO43cq/UKOB3gmVmQ1VBHGDGst2VjmEDRgqXdqT6hkERYRUn0nFew+2xqGuXamjPtmK6Fgi0v0FzCuDVHEuF3+R9Rbm/ey4wENo0M3t9yRsGyyhK9/b5vJi74VhcpK2OFNYWYd6k
enc_mac_key:bpSnSYfYQgoK5aqbOxZvZhMggtPfSLV4RQEpNErA8UY=
assoc_handle:EP0i!IAAAAB68uD0k0zW2ySHYPVsvjCC-g8VcuEmmxcy472UZoaOxQQAAAAFvg6JpHlxBnq5x1udeXl4940HcHZVMmjJ5Af0LEMNSSaI_7anMoCTU9Ne5rX44ZN92izQmTjRHNCcBjRI3Uc9e
assoc_type:HMAC-SHA256
session_type:DH-SHA256
expires_in:1209598
ns:http://specs.openid.net/auth/2.0

Step 2: App redirects to OP with the associate handle

Done in the “initialize” function: here in the sample app.

Request:

This is a 302 to Intuit OpenID end point.

GET https://vm-openid.intuit.com/OpenId/Provider?openid.ns=http://specs.openid.net/auth/2.0&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://localhost:8011/verifyopenid.htm&openid.realm=http://localhost:8011/verifyopenid.htm&openid.assoc_handle=EP0i!IAAAAB68uD0k0zW2ySHYPVsvjCC-g8VcuEmmxcy472UZoaOxQQAAAAFvg6JpHlxBnq5x1udeXl4940HcHZVMmjJ5Af0LEMNSSaI_7anMoCTU9Ne5rX44ZN92izQmTjRHNCcBjRI3Uc9e&openid.mode=checkid_setup&openid.ns.ext1=http://openid.net/srv/ax/1.0&openid.ext1.mode=fetch_request&openid.ext1.type.FirstName=http://axschema.org/namePerson/first&openid.ext1.type.LastName=http://axschema.org/namePerson/last&openid.ext1.type.Email=http://axschema.org/contact/email&openid.ext1.type.RealmId=http://axschema.org/intuit/realmId&openid.ext1.required=FirstName,LastName,Email,RealmId&openid.ext1.count.Email=3 HTTP/1.1
Host: vm-openid.intuit.com
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36
Referer: http://localhost:8011/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: …
Cookie: …

Response:

This is a 302 back to the app end point Intuit OpenID end point mentioned in openid.return_to parameter

HTTP/1.1 302 Found
Cache-Control: private, s-maxage=0
Pragma: no-cache
Content-Length: 1525
Content-Type: text/html; charset=utf-8
Location: http://localhost:8011/verifyopenid.htm?openid.claimed_id=https%3A%2F%2Fvm-openid.intuit.com%2FIdentity-03d6580f-7fa0-4480-8aff-15cbeae3c270&openid.identity=https%3A%2F%2Fvm-openid.intuit.com%2FIdentity-03d6580f-7fa0-4480-8aff-15cbeae3c270&openid.sig=XZJHFdZsZRms12a9Vux6zr0gm%2BJDB4uXQgoGgUFFe0I%3D&openid.signed=claimed_id%2Cidentity%2Cassoc_handle%2Cop_endpoint%2Creturn_to%2Cresponse_nonce%2Cns.alias3%2Calias3.mode%2Calias3.type.alias1%2Calias3.value.alias1%2Calias3.type.alias2%2Calias3.value.alias2%2Calias3.type.alias3%2Calias3.value.alias3&openid.assoc_handle=EP0i%21IAAAAB68uD0k0zW2ySHYPVsvjCC-g8VcuEmmxcy472UZoaOxQQAAAAFvg6JpHlxBnq5x1udeXl4940HcHZVMmjJ5Af0LEMNSSaI_7anMoCTU9Ne5rX44ZN92izQmTjRHNCcBjRI3Uc9e&openid.op_endpoint=https%3A%2F%2Fvm-openid.intuit.com%2FOpenId%2FProvider&openid.return_to=http%3A%2F%2Flocalhost%3A8011%2Fverifyopenid.htm&openid.response_nonce=2016-04-11T23%3A33%3A48ZshWIZDhf&openid.mode=id_res&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.ns.alias3=http%3A%2F%2Fopenid.net%2Fsrv%2Fax%2F1.0&openid.alias3.mode=fetch_response&openid.alias3.type.alias1=http%3A%2F%2Faxschema.org%2FnamePerson%2Ffirst&openid.alias3.value.alias1=Bob&openid.alias3.type.alias2=http%3A%2F%2Faxschema.org%2FnamePerson%2Flast&openid.alias3.value.alias2=Smith&openid.alias3.type.alias3=http%3A%2F%2Faxschema.org%2Fcontact%2Femail&openid.alias3.value.alias3=kjonnala%2Beacct1%40gmail.com

Step 3: App validates the response claimed identity

This is where the XRDS document is returned from Intuit as a last-mile verification document.

Done by the “verifyOpenIDFromIntuit” function: here in the sample app

Request:

GET https://vm-openid.intuit.com/Identity-03d6580f-7fa0-4480-8aff-15cbeae3c270 HTTP/1.1

Accept: text/html; q=0.3, application/xhtml+xml; q=0.5, application/xrds+xml
Host: vm-openid.intuit.com
Connection: Keep-Alive

Response:

HTTP/1.1 200 OK
Cache-Control: private, s-maxage=0
Content-Type: application/xrds+xml; charset=utf-8
Content-Length: 685

<xrds:XRDS
	xmlns:xrds="xri://$xrds"
	xmlns:openid="http://openid.net/xmlns/1.0"
	xmlns="xri://$xrd*($v*2.0)">
	<XRD>
		<Service priority="10">

			<Type>http://specs.openid.net/auth/2.0/signon</Type>

			<Type>http://openid.net/extensions/sreg/1.1</Type>
			<Type>http://axschema.org/contact/email</Type>
			
			
			<URI>https://vm-openid.intuit.com/OpenId/Provider</URI>
		</Service>

		<Service priority="20">
			<Type>http://openid.net/signon/1.0</Type>
			<Type>http://openid.net/extensions/sreg/1.1</Type>
			<Type>http://axschema.org/contact/email</Type>
			<URI>https://vm-openid.intuit.com/OpenId/Provider</URI>
		</Service>

	</XRD>
</xrds:XRDS>

OpenID Connect vs OpenID 2.0

For completion sake, as the industry moved on to newer “authentication” standards, people realized that identity establishment and authentication can stay closer together than how OAUTH 1.0a and OpenID 2.0 were originally implemented. They didn’t need each other. Therefore, when OAUTH 2.0 came out, OpenID Connect was established as part of that flow as an add-on feature where user identity will be asserted and claims exchanged. The lingo there is a little different. The user information is usually returned as an encoded token meeting the JWT standards. The “sub” entity is the unique ID equivalent to the openid.claimed_id in OpenID 2.0.

Conclusion

I hope it is much clearer now what happens on the wire during an OpenID 2.0 handshake. If I scared you, then don’t worry — you don’t have to write these bits! There are OpenID libraries that live and breathe OpenID. For example, the sample code that I mentioned earlier is using the “openid4java” library. This post is intended to give you some insight into what is going on under the hood.

References

OpenID 2.0 spec

Intuit Developer OpenID docs

Sample Intuit App

Comments

View all
Load more comments