OAuth and OpenID Connect
OAuth (Open Authorization) is an open standard for API access delegation. Put simply, it’s a secure authorization protocols used to grant applications access to protected resources without exposing credentials.
OpenID Connect (OIDC) is an authentication layer (i.e. an identity layer) on top of OAuth 2.0. Client applications can use it to verify the identity of a subject (usually a user) based on the authentication performed by an authorization Server. It also provides basic profile information.O
Authentication
Authentication is the process of identifying an individual e.g. using a username and password. This basically involves checking whether a user exists and determining who this user is i.e. associating credentials with an identity. Or in other words authentication answers the question “who one is”. OAuth is a specification for authorization and is not an authentication protocol but is used as a basis for authentication protocols like OpenID Connect.
Authorization
Authorization is the process of giving a subject permissions to access resources in a certain way.
OAuth 2.0 Roles
OAuth defines the following roles:
- Resource Owner
- Client Application
- Resource Server
- Authorization Server
Resource Owner
The resource owner is an entity which can grant a client application a scoped access to a resource. It can be a person (usually the end-user) but can also be a machine.
Client Application
A client application is an application which accesses protected resources on the behalf of the resource owner.
Resource Server
The resource server manages access to a protected resource, allowing access based on access tokens. It is also often called an API server. The protected resources can e.g. be the profile or personal information of a user.
Authorization Server
The authorization server issues access tokens to authenticated client applications when permissions for the access are granted by the resource owner. This is the “OAuth2” server.
OAuth 2.0 Role Interactions
Tokens
There are 3 types of tokens used when working with OAuth2 and OpenID Connect. Additionally, an authorization code is also defined.
- The ID token is defined in OpenID Connect on top of the tokens defined in OAuth2
- The access token is the main token defined in OAuth2
- The refresh token is used, well, to refresh a token
- The authorization code is not a token in itself but can be used to get an access token
ID Tokens
ID Tokens are JSON Web Tokent (JWT) introduced by OpenID Connect. These tokens identify a user and contain user’s authentication information. They contain three parts:
- a header
- a body
- a signature
In the token, each of these parts is encoded using Base64Url and concatenated using a “.” (dot) between them:
Base64Url(header) + “.” + Base64Url(body) + “.” + Base64Url(signature)
The body of the token contains a series of claims which provide data about the subject being identified by the token. The OpenID Connect specification doesn’t specify which claims have to be present in which context but does define “standard” claims (with registered claim names) and allows the use of custom claims. The registered claim names are:
- iss: it’s the issuer of the token.
- sub: this claim identifies the subject for which the token was issued. It is at least unique within the scope of an issuer.
- aud: this claim identifies the audience for this token. This is the intended recipient(s) for this token. If an entity is processing this token does not identify itself as part of this audience, the token will be rejected.
- exp: this is the expiration time of the token. An entity processing this token should reject it once the expiration time is reached.
- nbf: this is the “not before” claim i.e. it defines at which point in time this token will be valid from. An entity processing this token before this point in time should reject it.
- jti: this is an identifier for this particular token which should be unique per issuers and only have a low probably of not being unique globally.
Some claims are always added to the ID token by the authentication server and some claims depend on the scopes requested by the entity requesting the authentication.
Since the ID tokens contain privacy relevant data about subjects being identified, they should be kept confidential and access tokens should rather be used to access resources i.e. use the ID tokens to identify subjects and get data/metadata regarding the subject and access tokens to access resources / APIs.
This is how an ID token could look like:
{
"https://benohead.com/country": "Germany",
"https://benohead.com/timezone": "Europe/Berlin",
"email": "henri.benoit@gmail.com",
"email_verified": true,
"iss": "https://auth.benohead.com/",
"sub": "5b1789917b944931f4021e61",
"aud": "q580zCRvynSeIg3uXCChcSRlYtNKSUPe",
"iat": 1530800423,
"exp": 1530836423
}
Access Tokens
Access tokens allow a client application to access a protected resource and defines the scope of this allowed access. This token is provided by the client application to the resource server when accessing the resource e.g. as an HTTP header. Just like the ID token, the access token has a limited lifetime which is defined when the authorization server issues the token to the client application. Since it allows access to protected resources, it must be kept confidential as much as possible although this is not always possible especially when a web browser is involved.
This is how an access token could look like:
{
"iss": "https://auth.benohead.com/",
"sub": "5b3cd37722c8f80eecd338e5",
"aud": "https://benohead/api/dummy/",
"iat": 1530802106,
"exp": 1530888506,
"scope": "read update delete create"
}
Note that in case the authorization server is also the resource provider (so if the audience matches the authorization server), you might also get an opaque access token which is just a string without any further meaning and which cannot be decoded. The authorization server can then map this string to permission on its own protected resources.
Refresh Tokens
Refresh Token are tokens containing information required to obtain a new ID token or access token. It is usually used to get a new token after the previous one expires. It is usually more secure to have short lived access tokens combined with refresh tokens, since it allows the authorization server to refuse to issue a new access token based on the refresh token in case the token has been compromised but still allow renewing the token in case access to a resource is required for a longer time. Without refresh token (and with long lived access tokens) a resource provider would need to query the authorization server to see whether the long lived access token is still valid. You can skip it when using short lived access tokens.
As an alternative to using refresh tokens, you could get a new access token with credentials every time a short lived access token expires. But there has a drawback: If you get the access token with user credentials (additionally to client credentials), you will need these user credentials every time your token expires (while you wouldn’t need the user credentials, just the client credentials, when using a refresh token).
Authorization Codes
Authorization codes are codes returned to unsecure clients. These client can then exchange them with an access and/or ID token in a more secure way.
They are used in the Authorization Code Grant Flow which is a flow where the client is typically a browser which receives an authorization code from the authorization server and sends this to the web application which then interacts with the authorization server in the back-end to exchange the authorization code for an access token, a refresh token and/or ID token.
Scopes and Audiences
Scopes and audiences are used to handle multiple resource servers and multiple types of access permissions.
Audience
The JWT aud (Audience) Claim identifies the intended recipients of the token i.e. it allows an authorization server to issue tokens that are only valid for certain purposes.
An audience claim can either contain a list of strings (i.e. multiple audiences) or it can be a single string (i.e. there is only one intended audience). It does not matter if an audience value is a URL or some other application specific string.
Each recipient of such a token must validate that the audience specified in the token matches its own audience name. It must then reject any token that does not contain its own audience name in the intended audience. The authorization server which issues the token can only validate whether a token for this audience can be issued. It is the responsibility is the resource server to determine whether the token should be used or not. A resource server may choose to ignore the audience claim and accept any valid token.
So the audience claim is only useful if you want to issue tokens with different purposes (i.e. intended audiences) and if at least some of the APIs (resource servers) you are using are validating the audience claim.
Scopes
Scopes provide a way to limit the access to functionality provided by the resource servers. The client can request scopes to be provided in the issued access token. The authorization server can provide all requested scopes, some of them or even additional ones based on its internal rules/policies. The provided scopes are written in the scope claim in the access token.
So they basically act as permissions. Authorization servers also use them when getting the consent from the user (i.e. ask the user whether he really wants to provide the client application access to these specific scopes).
When the client then uses the issued access token to request access to a protected resource, the resource server will validate that the type of resource and the type of access match the scopes contained in the access token.
E.g. if your access token contains the scope claim “read:posts read:comments write:comments”, the resource server would allow an application presenting this token to have read access to the posts and read and write comments but not to create a new post.
OpenID Connect Scopes
The following scopes are defined in OpenID Connect:
- openid: this is the basic OpenID scope requesting to return the sub claim uniquely identifying the user and which can be used in combination with the scope values below.
- profile: requests the authorization server to provide access to the user’s profile claims: name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.
- email: requests the authorization server to provide access to the email and email_verified claims.
- address: requests the authorization server to provide access to the address claim.
- phone: requests the authorization server to provide access to the phone_number and phone_number_verified claims.
OpenID Connect Endpoints
OpenID Connect defines 2 endpoints which can be used to request one or more of the token types described above:
- The Authorization Endpoint is usually an endpoint accessible with the URL <authorizationserver>/login or <authorizationserver>/authorize
- The Token Endpoint is usually an endpoint accessible with the URL <authorizationserver>/token
In some of the flows described below (in the ones not requiring an authentication code), you might be connecting to either the authorization endpoint or the token endpoint (depending on you authorization server).
Additionally, it also defines the UserInfo endpoint which returns claims about the authenticated user.
The Authorization Endpoint
The authorization endpoint is an endpoint the user is sent to in order to:
- get authenticated by any supported method e.g. with a username and password, an existing session cookie or a federated identity provider such as a social login, SAML provider or ADFS integrated system
- issue a token to the client application (or deny it). Optionally, it will request a user consent (or issue a consent implicitly by using an internal policy)
The type of token being issued depends on the requested response type.
The Token Endpoint
The token endpoint can exchange a grant (e.g. an authorization codeprovided by the authorization endpoint) for a token. The grant supported as input are usually:
- An authorization code
- Client credentials i.e. a client ID and a client secret
- A username and password
- A refresh token
The UserInfo Endpoint
The UserInfo endpoint returns claims about the authenticated user. This is an OAuth 2.0 protected resource so you need to provide an access token to access it.
Here is a sample response from this endpoint:
{
"sub": "1761101158623",
"name": "Henri Benoit",
"given_name": "Henri",
"family_name": "Benoit",
"preferred_username": "benohead",
"email": "henri.benoit@gmail.com",
"picture": "https://secure.gravatar.com/avatar/bde998b4b8e4b4fb259d80b5ac05d63d"
}
OpenID Connect Response Types
OpenID Connect defines an additional URL parameter called response_type. With this parameter, you can request different types of tokens to be returned. You can combine them by separating them by a space e.g. response_type=code id_token token.
- id_token: if this response type is specified, the authorization server will return an ID token
- token: if this response type is specified, the authorization server will return an access token
- code: if this response type is specified, the authorization server will return an authorization code
Response type: code id_token token
When using this response type, the endpoints will issue the following tokens:
Endpoint | ID token | Access token | Authorization code |
---|---|---|---|
Authorization | Yes | Yes | Yes |
Token | Yes | Yes | No |
Note that the Token endpoint will never return an authorization code since it is an input for the token endpoint when the authorization code grant is used.
Response type: id_token token
When using this response type, the endpoints will issue the following tokens:
Endpoint | ID token | Access token | Authorization code |
---|---|---|---|
Authorization | Yes | Yes | No |
Token | Not used | Not used | Not used |
Note that the Token endpoint is not used in such cases.
Response type: code id_token
When using this response type, the endpoints will issue the following tokens:
Endpoint | ID token | Access token | Authorization code |
---|---|---|---|
Authorization | Yes | No | Yes |
Token | Yes | Yes | No |
Note that the Token endpoint will never return an authorization code since it is an input for the token endpoint when the authorization code grant is used.
Response type: code token
When using this response type, the endpoints will issue the following tokens:
Endpoint | ID token | Access token | Authorization code |
---|---|---|---|
Authorization | No | Yes | Yes |
Token | Yes/No | Yes | No |
The token endpoint will only return an ID token if scope openid is requested.
Note that the Token endpoint will never return an authorization code since it is an input for the token endpoint when the authorization code grant is used.
Response type: id_token
When using this response type, the endpoints will issue the following tokens:
Endpoint | ID token | Access token | Authorization code |
---|---|---|---|
Authorization | Yes | No | No |
Token | Not used | Not used | Not used |
Note that the Token endpoint is not used in such cases.
Response type: code
When using this response type, the endpoints will issue the following tokens:
Endpoint | ID token | Access token | Authorization code |
---|---|---|---|
Authorization | No | No | Yes |
Token | Yes/No | Yes | No |
The token endpoint will only return an ID token if scope openid is requested.
Note that the Token endpoint will never return an authorization code since it is an input for the token endpoint when the authorization code grant is used.
Response type: token
When using this response type, the endpoints will issue the following tokens:
Endpoint | ID token | Access token | Authorization code |
---|---|---|---|
Authorization | No | Yes | No |
Token | Not used | Not used | Not used |
Note that the Token endpoint is not used in such cases.
This scenario basically maps to the OAuth2 Implicit Grant Flow.
OAuth 2.0 Grant Flows
Grant Types
A grant type in OAuth 2.0 refers to the way an application gets an access token. OAuth 2.0 defines several grant types:
- Authorization Code
- Implicit
- Password
- Client Credentials
- Device Code
- Refresh Token
Extensions can also define new grant types. Each grant type maps to a different use case. E.g. using a native application, a web application, single page applications, machine-to-machine applications…
Which flow should I use ?
Depending on which type of application you are developping and your ability to open a browser window or store client secrets securely, you will choose one of the flows above.
Application Type | Grant Flow Type |
---|---|
Web Server Application | Authorization Code Grant |
Single Page Application | Implicit Grant Authorization Code Grant with public client |
Backend Server Application | Client Credentials Grant |
Native Application | Authorization Code Grant with PKCE Authorization Code Grant with public client |
Native Application with no Browser Window | Resource Owner Password Credential Grant |
Note that when you have a native application without the possibility to open a browser window your only solution is to use the password grant but this solution is still not ideal since this forces the user to enter his/her credentials in your application. So this requires some level of trust from the user.
Authorization Code Grant Flow
The Authorization Code grant type is used by web and mobile apps. It can only be used if the client application is able to open a web browser. It is considered more secure than the implicit grant flow because it doesn’t provide the access token directly in a callback URL parameter but provides a code which can then be exchanged with an access token from a web server or a native app.
You can see the authorization code flow in action on the OAuth playground. The corresponsing OpenID Connect flow (so involving an ID token) can also be checked on the OAuth playground.
This flow is mainly aimed at web application running on a server where the backend can act as a confidential client i.e. can keep both the client secret and the issues access token secure. So the client identity can be securely assessed and the access token is only shared between the autorization server, the backend of the web application and the resource server.
With single page application or other javascript heavy application or native application installed on a desktop computer or a mobile device, it is not possible to keep the client secret secure on the client side. And if such applications have no control on a server side component or this component is not appropriate for taking over the role of the client application in OAuth2 flows, they are considered public client.
When such a public client use the Authorization Code Grant, they cannot authenticate themselves as a client but can still authenticate the user. So the capabilities of the authorization server are somewhat limited since it cannot control which applications are allowed to get an access token in the name of the resource owner. Actually the authorization server cannot even make sure that the application exchanging the authorization code for an access token is actually the same application which got the authorization code. This is a much bigger issue since anyone getting hold of the authorization code could go to the authorization server and get an access token pretending to be the application which got the code.
To mitigate this risk, you can use a Proof Key for Code Exchange. This basically just means that when request the authentication code, you provide a secret called “code verifier” that you generate on the fly and use only in the scope of the authorization flow. The authorization server will associate the code verifier to the returned authorization code (it will not return it together with the code). When the application then wants to exchange the code for an access token, it also provides the code verifier which can be checked by the authorization server before returning an access token.
You can also see the authorization code flow with PKCE in action on the OAuth playground.
Implicit Grant Flow
The implicit grant flow is a flow where the authorization server directly returns an access token in a URL fragment. Unlike the Authorization Code Grant Flow it doesn’t the client application to exchange an authorization code for a token. Originally, it was the recommended “best practice” for all browser based apps. This flow is now mostly used in SPA (Single Page Applications – JavaScript application running in the browser). For other server based web application, you would rather use the Authorization Code Grant Flow.
You can see the implicit flow in action on the OAuth playground.
Password Credential Grant Flow
The password grant is probably the easiest (and least secure) grant type. In this flow, the application needs to know the user credentials and pass them to the authorization server.
It is recommended to avoid this flow as much as possible. The main issue bing that the client needs to know you credentials. Imagine you want to allow the great benohead.com app to get your name from Facebook but you have to give it your password and trust the app will not start friending random strangers or posting inappropriate messages or just shut you out by changing your password.
Very helpful article!
As always, you are just awesome Henri!
Very helpful article