OAuth 2.0 and OpenID Connect Explained

What are OAuth 2.0 and OpenID Connect?

OAuth 2.0 is an authorization framework developed by IETF defined in RFC 6749. OpenID Connect (AKA OIDC) is an identity layer built on top of OAuth 2.0. OpenID connect specification is built by OpenID Foundation.

The Problem Statement

If you wanted a service or an application to access and process some resources that you own on your behalf, you’ll need to find a way to provide that access to that application or that service. For example, if you want a third-party application to read your tweets, you’ll need to give that third-party application the rights to access the Twitter API. This problem is called the access delegation problem.

One way to provide that access is to share your Twitter login credentials – which is quite dangerous considering that not only the third-party application can read all tweets, it can also tweet on your behalf and also read your private messages.

The Solution

With OAuth 2.0 flow, the third-party application which is trying to access your tweets redirects the user to Twitter. Then the user authenticates to Twitter and provides consent that third-party is allowed to read your tweets. Twitter then provides a temporary token to the third-party application, by which the third-party application will get read access to the user’s tweets for a limited time. The subsequent requests from the third-party application send that token along with the request.

OAuth flow for a third-party application reading tweets.

Per OAuth 2.0 terminology, the following are the flow participants.

Resource OwnerThe Twitter use is called the resource owner. The owner should be the one deciding who has what kind of access to the resource.
Authorization ServerTwitter is the authorization server in our example.
Resource ServerThe Twitter API server in our example is the resource server.
ClientThe application which is tying to access our user’s tweets.
Access TokenThe token issued by Twitter – the authorization server.
ScopeAccess level of the access token is called scope. If the third-party application tries to tweet on behalf of the user, it will fail because that is out of scope.
Grant Flow and Grant TypeThe flow of events is called grant flow and is defined by the grant type – discussed below.
OAuth Participants

Grant Types

Depending on how a client application works, the client application selects a grant flow to get an access token from the authorization server. The OAuth 2.0 specification has five grant types. Each type specifies the steps for getting an access token.

Client CredentialsThis flow is suitable for authentication between two applications or services with no end-user.
Resource Owner PasswordThis flow is suitable in cases where the resource owner has a trust relationship with the client, such as the device OS.
Authorization CodeSuitable for applications with an end-user.
ImplicitNot suitable for any cases, dangerous! Discussed below.
Refresh TokenSuitable when renewing expired access tokens.
Grant Type and Use-Case

Note that OAuth 2.0 framework doesn’t limit you to the above five grant types, you can add grant types as needed. For example, SAML profile for SSO applications or JWT profile for SSO using OpenID Connect.

The following section describes the main five grant types.

Client Credentials Grant Type

In this grant type, only two participants are involved. In this type, the resource owner is not separate, the client application is itself is the resource owner.

Each client has its own credentials, a client ID, and a client secret. In our example, the application which is trying to read tweets from the Twitter API server is the client. The client is responsible to store and protect the client’s secret. The client application has to send its client ID and client secret to the authorization server to get an access token. The authorization server is responsible to validate the client ID and the secret combination.

Request

POST /oauth2/token HTTP/1.1
Host: 192.168.0.114:9443
Authorization: Basic cVFCcnFlNTVZb1dvMElrTkdQaUFMc29hS1BZYTpDMGxlQW9KTlRsRThhYjVjeV9vMHduYkZtTEVh
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 29
grant_type=client_credentials

Response

{
 "access_token":"da96597c-336d-4c54-ab26-ed29da4c0174",
 "token_type":"bearer",
 "expires_in":1530
}

The above flow is suitable for applications that do not have to worry about end-users. The client application accesses an API by itself, not on behalf of anyone else. That’s the reason why this grant type is used when one system or service is authenticating to another system or service.

Resource Owner Grant Type

Resource owner grant type builds on top of the client credential grant type and adds support for resource owner authentication with username and password.

In this flow, the client application prompts the end-user to provide a username and a password. The client application uses those credentials to request the authorization server for a token. Note the below request and response flow. The request by the client application to the authorization server.

Request

POST /oauth2/token HTTP/1.1
Host: 192.168.0.114:9443
Authorization: Basic cVFCcnFlNTVZb1dvMElrTkdQaUFMc29hS1BZYTpDMGxlQW9KTlRsRThhYjVjeV9vMHduYkZtTEVh
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 62
grant_type=password&username=admin&password=admin&scope=openid

Response

{
"access_token":"841e262c-9bcc-35eb-97b2-f3c1f8192b42",
"refresh_token":"e28c8b38-e5fa-34e7-8861-b6080c545347",
"scope":"openid",
"id_token":"eyJ4NXQiOiJNell4TW1Ga09HWXdNV0kwWldObU5EY3hOR1l3WW1NNFpUQTNNV0kyTkRBelpHUXpOR00wWkdSbE5qSmtPREZrWkRSaU9URmtNV0ZoTXpVMlpHVmxOZyIsImtpZCI6Ik16WXhNbUZrT0dZd01XSTBaV05tTkRjeE5HWXdZbU00WlRBM01XSTJOREF6WkdRek5HTTBaR1JsTmpKa09ERmtaRFJpT1RGa01XRmhNelUyWkdWbE5nX1JTMjU2IiwiYWxnIjoiUlMyNTYifQ.eyJhdF9oYXNoIjoiYVJrekVVenB5T2tBZVBnXzhWUndYUSIsImF1ZCI6InFRQnJxZTU1WW9XbzBJa05HUGlBTHNvYUtQWWEiLCJzdWIiOiJhZG1pbiIsIm5iZiI6MTYxOTQzNTkzNCwiYXpwIjoicVFCcnFlNTVZb1dvMElrTkdQaUFMc29hS1BZYSIsImFtciI6WyJwYXNzd29yZCJdLCJpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0NDNcL29hdXRoMlwvdG9rZW4iLCJleHAiOjE2MTk0Mzk1MzQsImlhdCI6MTYxOTQzNTkzNH0.YxXUWApeWlc0wmUMiSb0R4_crAcAZ6Plw8LGw9s4KDdg3RtjY0chgSfYvw4nearK2hvZjkOlfxQ-DzkHQoD0e2jFiXJsoM4lJTwXYl1PEIiZ5DP6IUvl3k5SaRxMYyLuy1NmzfG_CGX58MvndxnD7aU0nIZwTzuhsVWjm7V9s7H-UJH463TsGfHV4A12wqLI3es-TV-8i4x3DMgEFu-14ycFlBGlSuPrKh_jVq98Xrlt3gXC6Mifm_MD0exzESr3kHX45Yc4whZjHZFww_4nzUoPvRX-38MQefPVH3gPicnwT73hjYMkaYeFECxn9UebIP2UhohyWGnPveumJMe9Tg",
"token_type":"Bearer",
"expires_in":1542
}

In this grant type flow, it is important to note that the authorization server will validate both the client ID and the client secret and user’s credentials. The authorization server will issue the token only if all four fields are valid. In this flow, the authorization server also provides a refresh token which can be used to renew the access token before it expires. (Note: We didn’t get a refresh token in the client credentials grant type). This mode of delegating access is called access delegation with credential sharing. Note that this type of grant flow is considered legacy.

Also, the client application is responsible to process the store credentials from the users correctly – ideally using it only to get the access token and then discard the credentials.

Refresh Token Grant Type

The refresh token grant type is used to renew an existing token before the access token is expired – so that the user of the application doesn’t have to enter credentials again. With this grant type, the application should receive a new access token and a new refresh token.

Request

POST /oauth2/token HTTP/1.1
Host: 192.168.0.114:9443
Authorization: Basic eDJmSWR1Y2FuRVF0RjU5Njg3VnhMU0dqQlJrYTpjREpkcHZ6NDhtbEZFVnFJTlRUV2M2UjViMmdh
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 75
grant_type=refresh_token&refresh_token=e99f94aa-ba5b-3a42-9281-6eea5db98ced

Response

{
"access_token":"18b9048d-2a9d-352a-82c2-f43abbb96d57",
"refresh_token":"4b6cd6e0-ebd0-3675-8e57-dcdd42cd6a9a",
"scope":"openid",
"id_token":"eyJ4NXQiOiJNell4TW1Ga09HWXdNV0kwWldObU5EY3hOR1l3WW1NNFpUQTNNV0kyTkRBelpHUXpOR00wWkdSbE5qSmtPREZrWkRSaU9URmtNV0ZoTXpVMlpHVmxOZyIsImtpZCI6Ik16WXhNbUZrT0dZd01XSTBaV05tTkRjeE5HWXdZbU00WlRBM01XSTJOREF6WkdRek5HTTBaR1JsTmpKa09ERmtaRFJpT1RGa01XRmhNelUyWkdWbE5nX1JTMjU2IiwiYWxnIjoiUlMyNTYifQ.eyJhdF9oYXNoIjoienJodHlYdlpfcTlaXzlINE1ZbUpLZyIsImF1ZCI6IngyZklkdWNhbkVRdEY1OTY4N1Z4TFNHakJSa2EiLCJzdWIiOiJhZG1pbiIsIm5iZiI6MTYxOTk2NTI4MSwiYXpwIjoieDJmSWR1Y2FuRVF0RjU5Njg3VnhMU0dqQlJrYSIsImFtciI6WyJyZWZyZXNoX3Rva2VuIl0sImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTQ0M1wvb2F1dGgyXC90b2tlbiIsImV4cCI6MTYxOTk2ODg4MSwiaWF0IjoxNjE5OTY1MjgxfQ.ZwjZ0z_RZkYox8hgFtYaGelPBpHiaz3QCIaIDxCVsfGZviphc9hDoNRYdBQl8pGbW9XVy2Rp-fDByOpZ681hYeAT1DrhLncBd6nzuBAsfLzj3rQQbUxPtx17cw4D8x_-oVOg3m7Vnhqxg4y65iR33pO5elNuqS3JLpM9-zW6rBp4QjX7hPLKycJ60wU4Qj8PctDGurXkepDaN-my4CQfIv16a-mhqwcnRRYI_bSTRSQtedB5TmMxShNnT-avLAbXZElRG8wtIVXfsxlMqDjbJ-3n97-qhnF7iEMjlAY_H_XLfnRqrAJWsiw8kEMoA-sgU8YUZQjpNJlEmzzWGnlVww",
"token_type":"Bearer",
"expires_in":3600
}

Note that in the request, we’re sending the current refresh token to the authorization server and in the response, the authorization server will send a new access token with a new refresh token. The expiry time of the refresh token is generally longer than the access token, this is intentional to make sure that the application can renew the access token after some time of inactivity.

Authorization Code Grant Type

Authorization code grant type is used when the client application is a web-browser based application or an application capable of processing HTTP redirects. The client application initiates the request with an authorization code request to the authorization server. The following are the steps associated with the authorization code grant type.

  1. The client application initiates the flow by redirecting the user to the authorization server.
  2. The authorization server will prompt the user to enter credentials in a browser prompt.
  3. The user submits credentials for authentication and the browser posts the credentials to the authorization server.
  4. The authorization server redirects the user back to the client application with an authorization code.
  5. The client application will use the authorization code to request the authorization server to get an access token.
  6. The authorization server sends an access token to the client application.

Request

GET /oauth2/authorize?client_id=x2fIducanEQtF59687VxLSGjBRka&scope=openid&redirect_uri=localhost:8080/callback&response_type=code HTTP/1.1
Host: 192.168.0.114:9443
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: JSESSIONID=D51E5AEF8F516B9674526F8CDC4E92EB; requestedURI=../../carbon/admin/index.jsp
Upgrade-Insecure-Requests: 1

Response

HTTP/1.1 302 
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Set-Cookie: JSESSIONID=730A05AA6C9EA098636761DC456BD13E38CC7B182602C6717D40473782945BA2CC617BE45F57661D494C7123D4F8EEB86ED6C89ED09D049BF0A2185D0C2C59329C92983CDF818407E1516492DE1A007F8F4848199AC438BE92B03C323F0E7186B946A77040DF7BCEE54FBE06333A155D661FE021897069E4E969062BED7D1919; Path=/oauth2; Secure; HttpOnly
Location: https://192.168.0.114:9443/authenticationendpoint/login.do?client_id=x2fIducanEQtF59687VxLSGjBRka&commonAuthCallerPath=%2Foauth2%2Fauthorize&forceAuth=false&passiveAuth=false&redirect_uri=localhost%3A8080%2Fcallback&response_type=code&scope=openid&tenantDomain=carbon.super&sessionDataKey=de622420-0f8a-44dd-901d-1764ad6307d9&relyingParty=x2fIducanEQtF59687VxLSGjBRka&type=oidc&sp=oauth2&isSaaSApp=false&authenticators=BasicAuthenticator%3ALOCAL
Content-Length: 0
Date: Sun, 02 May 2021 14:54:51 GMT
Connection: close
Server: WSO2 Carbon Server

Note the Location header, the user is redirected to the authorization server which will give the user a prompt to login or to approve similar to the below screenshot.

Once the user approves the prompt or enters the credentials, the authorization server will validate the credentials and if successful, the authorization server will redirect the user back to the client with an authorization code. With a redirect like this:

https://localhost:8080/login?code=e840ff23-41b9-4ebb-add1-a2340f051bd7

The application then uses the above code to the authorization server for swapping it for an access token.

A word of caution: This redirect from the authorization server to the client application happens via a URI which means that it will be logged in browser history and server logs. In a man in the middle scenario, an attacker can also use the same code to get an access token. That’s the reason why it is important to make sure that the flow occurs over an encrypted channel (HTTPS) and the authorization server invalidates the code as soon as it has been used to retrieve an access token. If the code is used more than once, the authorization server should revoke all tokens previously issued with the same code.

Implicit Grant Type

The implicit grant type is similar to the authorization gran type except for the intermediate step of getting an authorization code before getting an access token. Instead of redirecting the user to the client application with an authorization code, the authorization server redirects the user to the client application with an access token.

Instead of redirecting the user with a code, the redirect in this grant type will work something like this:

https://localhost:8080/login?access_token=e840ff23-41b9-4ebb-add1-a2340f051bd7&expires_in=3599

Again, because the access token is passed in the URL, the access token will be saved in browser history and server logs. This grant type is not recommended.

More on Tokens & Scopes

Scope for a token is the purpose of the token. A token can be associated with one or more scopes (more than one purpose). When a client application requests a token to the authorization server, the request contains the list of scopes. Although this doesn’t mean that the authorization server is required to grant a token for all requested scopes, the authorization server will grant a token for the scopes for which the user has given permissions (when prompted).

There are two types of tokens.

  1. Reference tokens
  2. Self-contained tokens

A reference token is a token that can only be understood and validated by the issuer of that token – the authorization server. This means that the resource server will have to talk to the authorization server continuously to validate the token.

A self-contained token, however, can be validated without talking to the authorization server. This token is generally in the form of a signed JWT (RFC 7523). The resource server will need to pre-establish trust to verify the signatures of the JWT based self-contained tokens.

OpenID Connect

OpenID Connect (AKA OIDC) is an identity layer built on top of OAuth 2.0. OpenID Connect is based on a token called ID token. An ID token is also a JWT token (Similar to a JWT profile access token) that contains additional information about the user such as the email address or the username of the user.

Whenever the authorization server sends an access token to a client application, the authorization server also sends an ID token with the access token in OpenID connect.

The following is an example payload of an ID token from OpenID Connect Specification.

{
  "iss":"http://demo.demo.com",
  "sub":"example@example.com",
  "aud":"s6BhdRkqt3",
  "nonce":"n-0S6_WzA2Mj",
  "exp":1311281970,
  "iat":1311280970,
  "auth_time":1311280969,
  "acr":"urn:mace:incommon:iap:silver",
}

Attribute details of the example payload:

issThe identifier of the authorization server.
subThe user information for which this ID token was generated.
audInformation about the audience of the token who are supposed to use the ID token. This must contain the client ID of the client application.
iatThe time at which the ID token was issued
expThe time at which the ID token will expire.
Information about attributes of an ID token payload.

The ID token is sent by an authorization server whenever OpenID is as the scope of the access token request from a client to the authorization server. If you check the example request and response in the above sections for the resource owner or the refresh token grant type, you’ll see that the responses contain id_token along with access_token (see the below screenshot).

References

Demo server used in the request responses: Identity Server – On-Premise and in the Cloud (wso2.com)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s