An OpenID Connect server is just an OAuth 2.0 server on steroids. What it does it to return the ID Token, which contains information about the authentication event for the user at the door, in addition to the Access Token. This article explains how to write a simple OpenID Connect server using an OAuth 2.0 server as the basis.
1. Add OpenID Connect request / response handling to Authorization Endpoint
Assuming you have OAuth 2.0 server code, what you need to add on top of it is code to handle a few extra parameters used for user authentication.
When a client wants to authenticate the user, it will pass the following information (the green ones are required for OAuth and the red portion is specific to OpenID Connect):
- client_id: the client id of the application;
- redirect_uri: https endpoint to which the user should be returned;
- scope: openid;
-
The scope can also have email, profile, address, phone, offline_access values.
-
- response_type: one of “id_token“, “token id_token“, “code“;
The client may also pass these.
- prompt: Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent. Defined values are none, login, consent, select_account.
- max_age: Specifies the allowable elapsed time in seconds since the last time the End-User was actively authenticated.
On the server side, you have to do the following upon receipt. In addition to the normal OAuth processing:
- Check if scope includes openid. If it does not, just proceed with normal OAuth processing;
- Find out what response_type the request was using;
- Create the id_token as follows:
- Make the JSON with following parameters:
- iss: https URI that indicates the issuer;
- sub: identifier of the user at the issuer;
- aud: client_id of the requesting client;
- nonce: the nonce parameter value received from the client;
- exp: expiration time of this token;
- iat: time when this token was issued;
- auth_time: time the authentication happened;
- at_hash: the first half of a hash of the access token;
-
Example: { "iss": "https://server.example.com", "sub": "alice", "aud": "clinet-id-received", "nonce": "n-0S6_WzA2Mj", "exp": 1311281970, "iat": 1311280970, "auth_time": 1311280969, "at_hash": "MTIzNDU2Nzg5MDEyMzQ1Ng" }
- Sign it with JSON Web Signature (JWS) using your RSA private key. You should use a library for JWS. The result is the ID Token to be returned.
- Make the JSON with following parameters:
- If the response type is ‘code‘:
- Save the id_token and send the code back as usual in OAuth.
- Else if the response type is ‘id_token‘:
- Send the id_token back to the redirect_uri in the fragment.
- Else if the response type is ‘token id_token‘:
- Send the Access Token and ID Token back to the redirect_uri in the fragment.
- If an error occurs: You have already implemented OAuth error responses, have not you?
Unless the response type was ‘code’, you are basically done here.
2. Make Token Endpoint capable of returning ID Token
To handle a request with the response_type ‘code’, you need to add functionality to the Token Endpoint, which returns the previously created ID Token.
- When you received the ‘code’, check if it is associated with a previously created ID Token. It could be that ‘code’ has a special strtucture that indicates it or you might pull it from the database you created.
- Pull the associated ID Token out from wherever you have stored it and return it in the JSON response with the name “id_token”, e.g.:
{ "access_token": "SlAV32hkKG", "token_type": "Bearer", "refresh_token": "8xLOxBtZp8", "expires_in": 3600, "id_token": "previously.created.id_token" }
The only addition to the normal OAuth 2.0 is this single parameter, id_token.
- refresh_token should be returned if the scope included offline_access.
- If an error occurs: this is just yet another OAuth 2.0 error response that you have already implemented.
3. (Optional) A protected resource: UserInfo Endpoint
You can actually return all the requested claims in the ID Token, but since you might want it to be small for one reason or another, you might want to return most of them from a protected resource. We have standardized resource as the UserInfo Endpoint. It is an OAuth 2.0 protected resource. Most servers will probably implement this but it is not strictly required.
The client accesses the UserInfo endpoint using the Access Token returned to it. What server needs to do is:
- Verify that the Access Token is valid;
- Pull the claims about the user associated with the Access Token out of its database;
- The claim set will be one of email, profile, address, phone or a combination of them. The scope elements above map to the following sets of claims:
- email: email and email_verified;
- profile: name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.
- address: address, which is a structure with the following members:
- formatted, street_address, locality, region, postal_code, country;
- phone: phone_number and phone_number_verified;
- Return them as JSON, e.g.:
{ "sub": "alice", "name": "Alice de Wonderland", "given_name": "Alice", "family_name": "de Wonderland", "email": "alice@example.com", "picture": "http://example.com/alice/me.jpg" }
- If an error occurs, just return the OAuth 2.0 error.
That’s it.
There are additional features in OpenID Connect, but these are the minimal things that you need to implement as a server.
Why is there an ID Token?
As you can see, it is nearly as simple as it could be. One alternative design would be to get rid of the ID Token and create another endpoint.
We actually have started off there. We had a check_id endpoint at the beginning that returned the content of what we have as ID Token now. We moved to ID Token model later for the following reasons:
- Some social network clients could not tolerate the user experience degradation caused by the round trip time required to use the check_id endpoint;
- Some clients needed a token that can be used to create a session. The ID Token serves this purpose.
- Some clients needed to have an integrity check on access_token; we could implement it using the ID Token.
Also, we could have the returned id_token as the access token, many implementers opposed that; they wanted to keep doing what they were already doing with their access tokens.
Thus, the ID Token was the most straightforward solution. Indeed, we have made it as simple as possible, but no simpler. 😉
I have registrered in order to get a password but after 1 day, I don’t have any password to read the post . Should I ask for a password in any other place ? Thanks
Sorry, this article is still in the draft status. When I publish it, you will be able to see it.
This is awesome…. but if you want a slightly more comprehensive OpenID Connect server that also supports the UMA profile of OAuth, you may want to look at ox. See http://gluu.org for more info.
Nice article. I was wondering on what grant type the authorization requests are based. Would that be Resource Owner Password Credentials or Client Credentials?
The above article is based on the code grant type. We have also defined a profile for implicit grant type as well.
You need to save what is to be in ID Token somewhere until you send the ID Token out.
Somewhere can be on the server where “code” is being managed, or inside the “code” itself, in which case the server can be more-or-less stateless.
Once the server send the ID Token out, it does not need to save it anywhere.
Thanks for sharing your knowledge Nat. This article help when I was starting with OpenID Connect, building https://github.com/juanifioren/django-oidc-provider
I think what you wrote was very reasonable. But, think about
this, what if you were to write a awesome headline? I ain’t suggesting your
information is not good, however what if you added something that
makes people want more? I mean Write an OpenID Connect server in three simple
steps | .Nat Zone is kinda plain. You ought to peek at Yahoo’s home page and note how they
create post titles to get viewers to click. You might add a
video or a related pic or two to get readers interested about everything’ve written. Just my opinion, it might
bring your website a little livelier.
Thank you for every other magnificent article. The place else may
just anybody get that kind of information in such a perfect approach of writing?
I have a presentation next week, and I am on the look for such
information.
Thanks for your compliment 😀
I know this if off topic but I’m looking into starting
my own weblog and was curious what all is required to get set up?
I’m assuming having a blog like yours would cost a
pretty penny? I’m not very web smart so I’m
not 100% positive. Any suggestions or advice would be greatly
appreciated. Thanks
It does cost a bit more than a penny but that is the price for being self-sovereign, I guess.
I am now using AWS for this site, but I am using DigitalOcean for other sites and they provide quite an attractive price.
Hello, I think your site might be having browser compatibility issues.
When I look at your website in Opera, it looks fine but when opening in Internet
Explorer, it has some overlapping. I just wanted to give you a quick heads up!
Other then that, superb blog!
Thanks for letting me know. No, I have not tested on IE. Is not that a forbidden browser to be used on the internet now? 😉
Good blog post. I certainly love this site. Keep it
up!
I need to to thank you for this wonderful read!! I definitely enjoyed every little bit
of it. I have you book-marked to look at new stuff
you post…
Having read this I believed it was very informative. I appreciate
you finding the time and energy to put this content together.
I once again find myself personally spending a significant amount of time both reading and leaving comments.
But so what, it was still worthwhile!
I’m not that much of a qq online (Enriqueta) reader to be honest but your sites really nice,
keep it up! I’ll go ahead and bookmark your site to
come back later. All the best
Thanks!
?
I blօg quite often and Ӏ serіously thank you for your information. Your article has
truly рeaked my interest. I am ɡoing to boоk mark yoսr website and keеp checking fߋr new details about once a week.
I sսbscribed to your Feed as well.
Thanks so much!
Do you have any video of that? I’d like to find out some additional information.
Unfortunately not… Maybe I should make one.