OpenID Connect

OpenID Connect (OIDC) is a specification built on top of OAuth 2.0 that allows applications to authenticate users via third-party Identity Providers.

Identity Providers can be certified if their service conforms to the OpenID Connect specification.

Authentication Flow

There are two types of authentication flow: "code", where communication with the Identity Provider happens via a server, and "implicit", where the client (e.g. a single-page application running in a web browser) communicates directly with the Identity Provider. Only the "implicit" flow is discussed here.

At the start of the implicit authentication flow, the client sends the user to an authorization endpoint, e.g. https://accounts.google.com/o/oauth2/v2/auth. If it's not already known, that endpoint can be found via a discovery document, e.g. https://accounts.google.com/.well-known/openid-configuration, and there's even a way to discover that discovery document via webfinger, but that part of the process isn't essential here.

The request includes some query parameters:

const params = new URLSearchParams()
params.set('scope', 'openid') // use the OIDC protocol
params.set('client_id', CLIENT_ID) // the client identifier
params.set('redirect_uri', REDIRECT_URI) // redirect to this URL 
params.set('response_type', 'id_token') // return the id_token in a URL fragment
params.set('nonce', randomString()) // a random string to be included in the id_token
params.set('prompt', 'none') // don't prompt unless necessary

location.href = AUTHORIZATION_ENDPOINT + '?' + params.toString()

The Identity Provider must verify that this client_id and redirect_uri pair have previously been registered, so the id_token will only be sent to the application which the user has approved.

If the user declines, they're sent back to the redirect_uri with a URL fragment containing an error.

If the user approves, they're sent back to the redirect_uri with a URL fragment containing an id_token, which the client then stores. The client must check that the nonce value in the id_token matches the nonce value that was sent in the original request, so that an id_token can't be forced upon the user unless it was requested.

The client then includes the id_token in the HTTP headers of requests to an API that requires authentication (the Relying Party), as Authorization: Bearer ${id_token}.

JSON Web Token

The id_token is a JSON Web Token (JWT), which is signed but not encrypted, so you can read the contents.

The id_token has 3 parts, separated by a . character:

The payload

The payload contains several important values, which the server must verify:

The unique identifier of the user within the issuer's domain (i.e. their account ID) will be included in the id_token as the sub value.

If the client has asked for other information to be included, e.g. by adding email or profile to the scope parameter in the original request, those values will also be included in the id_token.

Expiry

When the token expires, the client can get a new token using "silent authentication": open the same authorization endpoint as before, but in a hidden iframe. If the user's approval is still valid (their session with the Identity Provider will be stored in a cookie which the authorization endpoint in the iframe, being on the Identity Provider's domain, can read), the iframe will redirect back to the redirect_uri for the new id_token to be read from the URL fragment and stored as before.

Further reading