When you read the OpenID Connect Specifications, you might feel a little bit intimidated. That’s because they are written in “spec language” and they deal with corner cases, etc. Yet when you translate them into normal English and just concentrate on a “simple case”, it becomes quite simple. So, here we go! (OK, much of the text is the same as the original proposal written by David Recordon. It was mostly just a matter of tweaking some of the parameter names, etc.)
Making an OpenID Connect request
In order for the client to make an OpenID Connect request, it needs to have the following information about the server:
client identifier – An unique identifier issued to the client to identify itself to the authorization server.
client secret – A shared secret established between the authorization server and client used for signing requests.
end-user authorization endpoint – The authorization server’s HTTP endpoint capable of authenticating the end-user and obtaining authorization.
token endpoint – The authorization server’s HTTP endpoint capable of issuing access tokens.
user info endpoint – A protected resource that, when presented with a token by the client, returns authorized information about the current user.
(Deleted 2012/3/3 : it may come back as a generic OAuth token introspection endpoint)
check id endpoint – A protected resource that, when presented with an id token by the client, checks the signature and returns information about the user’s session.
This information is either obtained by the client developer, having read the server’s documentation and pre-registered their application, or by performing Discovery and Dynamic Registration.
The client constructs a regular OAuth 2.0 request to obtain an access token.
To turn an OAuth 2.0 request into an OpenID Connect request, simply include
openid as one of the requested scopes. The
openid scope means that the client is requesting an identifier for the user as well as the authentication context. If you want user’s profile URL, name, and picture etc., you can add the additional scope:
profile. The server (and user) may choose to make more or less profile information available to the client. If the client also wants the user’s email address, it should include the scope
email. The same goes for address (
address) and phone (
While pre-registering your client with servers is not required, it’s likely that servers will have varying policies and requirements placed upon clients when it comes to accessing user information.
Receiving an OpenID Connect response
Assuming the user authorized the client’s request, the client will obtain an access token. The OAuth 2.0 access token response will typically include two parameters:
id_token. The following information is encoded as a JSON object in the
aud (audience) – REQUIRED. The client_id that this id_token is intended for.
exp (expiration) – REQUIRED. The time after which this token must not be accepted.
sub – REQUIRED. A locally unique and never reassigned identifier for the user (subject). e.g. “24400320” or “AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4”.
iss (issuer) – REQUIRED. A https: URI specifying the fully qualified host name of the issuer, which when paired with the
user_id, creates a globally unique and never reassigned identifier.
e.g. “https://aol.com”, “https://google.com”, or “https://sakimura.org”.
nonce – REQUIRED. nonce value sent in the request.
id_token parameter is a simple way to make sure that the data received by the client through the User-Agent flow (or other untrusted channels) has not been modified. It is signed by the server using the client secret that was previously established over a trusted channel. It is the concatenation of a base64url encoded signature description (the algorithm), a period (.), a base64url encoded JSON object, a period (.), and a signature string. This encoding is called a JSON Web Token (JWT). Note that base64url encoding uses two different characters from base64 and no padding.
An authorization server MUST only issue assertions about user identifiers within its domain. The client MUST verify that the
aud matches its
iss matches the domain (including sub-domain) of the issuer of the
client_id. The authorization server is responsible for managing its own local namespace and enforcing that each
user_id is locally unique and never reassigned.
When the client stores the user identifier, it MUST store the tuple of the
user_id MUST NOT be over 255 ASCII characters in length.
The client SHOULD verify the signature. If the client does not verify the signature, it MUST make a call to the check id endpoint to verify it.
ALL OF WHAT FOLLOWS ARE OPTIONAL.
Accessing user information
The user info endpoint is a regular OAuth 2.0 resource that returns a JSON document when fetched via HTTP and passed an access token. The client constructs an HTTPS “GET” request to the user info endpoint and includes the access token as a parameter.
The response is a JSON object that contains some (or all) of the following reserved keys:
sub – e.g. “AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4”.
profile – URL of the End-User’s profile page
name – display name of the user, e.g. “Nat Sakimura”.
given_name – e.g. “Nat”.
family_name – e.g. “Sakimura”.
email – e.g. “firstname.lastname@example.org”.
picture – e.g. “http://graph.facebook.com/sakimura/picture”.
The server is free to add additional data to this response (such as Portable Contacts) so long as they do not change the reserved OpenID Connect keys. (Note: there are more defined keys, but for the sake of brevity, I am omitting them.)
When using OpenID Connect, it’s likely that the client will both have buttons for popular servers as well as a text field for user entry of an email address or URL. (OpenID Connect does not directly solve the “NASCAR” problem.)
The goal of the discovery and registration phase is for the client to obtain the server’s authorization endpoint URL, token endpoint URL, a client identifier, a client secret, and the user info API endpoint URL. If the client has pre-registered with the server, then this information will already be known. Otherwise the client will have to discover them through Discovery.
- The user clicks a button on the client to select a server. In this case the client will have selected a set of preferred servers and thus already knows their authorization endpoint URLs (among possibly other things). The client may or may not already be pre-registered.
- The user (or a User-Agent acting on their behalf) enters a URL or email address. In this case, the client needs to perform discovery and determine if there is a valid server endpoint URLs.
1) Parse the user input to find out if it is an email address or a URL. If it is email address, do nothing. If no scheme, assume
2) Create a canonicalized identifier called “principal” by reconstructing the various parts. For example:
https://joe.example.com -> https://joe.example.com/
example.com -> https://example.com/
email@example.com -> firstname.lastname@example.org
3) Extract the domain and make a Webfinger call over TLS/SSL.
HTTP/1.1 200 OK
4) To obtain the specific endpoint URL etc., the client appends “/.well-known/openid-configuration” to the issuer, and GETs the issuer’s configuration file over TLS/SSL as follows:
GET /.well-known/openid-configuration HTTP/1.1
The response is a JSON object that includes endpoint and other information. For example:
"issuer" : "https://server.example.com",
"token_endpoint_auth_types_supported": ["client_secret_basic", "private_key_jwt"],
Unregistered clients and dynamic registrations
Regardless of the discovery mechanism used, the client may or may not already be registered with the server. Servers may have different policies about what data clients can access based on whether they’ve pre-registered (which generally includes agreeing to ToS) versus using a dynamic registration.
If the client does not have a valid client identifier and secret, it can make the following HTTPS “POST” request to the server’s registration endpoint URL (see Discovery) with the following REQUIRED parameters in JSON format as the POST body:
redirect_uris – The array of URLs the client is registering with the server for receiving OpenID responses.
POST /connect/register HTTP/1.1
Before responding, the server should check to see if the redirect URL is pre-registered outside of this OpenID flow. If so, an error response should be sent. Servers will need to develop a policy to handle what happens when a redirect URL is pre-registered by a developer but has already been used to create dynamic registrations. This might mean, for instance, that new dynamic registrations with that redirect URI will result in an error but requests using existing dynamic registrations continue working until they expire.
To issue a dynamic association, the server includes the following response parameters as JSON:
client_id – The client identifier. This could change with each response, which is up to the server.
client_secret – The client secret. This should change with each response.
expires_at – The number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the
client_secret will expire or 0 if they do not expire.
registration_client_uri – The uri to operate on this registration data.
registration_access_token – The access token to be used to access the registration_client_uri.
The client should store their dynamic registrations based off of the server’s token endpoint URL. With each dynamic registration, the client will store the client identifier, client secret, expiration time, user endpoint URL, supported flows, and user info API endpoint URL. The expiration time should be stored as an absolute time or an indication that it lasts forever.
As you can see, the basic web client flows for OpenID Connect are quite straightforward, and nearly as simple as those that were originally proposed. While additional functionality can be used, such as requesting specific sets of claims rather than the default set, having these additional capabilities available for when they’re needed don’t make simple interactions any more complex for clients, which will vastly outnumber the number of OpenID Providers.
- 2012-01-20 Initial version
- 2013-01-31 Changed
user_id to to reflect the change in the specs.
- 2013-03-03 Deleted check_id endpoint. Updated discovery and registration to match with the current draft.