Understanding authorisation & authentication with OAuth2 and OpenID Connect

OAuth2 and OpenID are both important protocols for enabling secure and seamless authentication and authorization in modern web applications. 

In this article we will explain the difference between them, and what they are used for.

Real-World Use Cases of OAuth2 and OpenID 

OAuth2 for delegated authorisation

Let’s start off with an example: we are building a job board web app. To gain traction, we want to integrate the app with LinkedIn, and be able to post and apply to a LinkedIn job on behalf of our users, who happen to have a LinkedIn account.
In a nutshell, we need to ask LinkedIn to give us the authorisation to use their APIs on behalf of a LinkedIn user.
That’s where OAuth2 comes into play. OAuth2 enables application users to grant third-party applications access to their resources, without the need to share sensitive credentials. You have probably used it many times already, when allowing an application to access some data and perform some actions on your behalf.

Note: OAuth2 is different from Auth0. OAuth2 is a protocol for delegated authorization. Auth0, is a service that provides authentication and authorization capabilities, like for example amazon cognito,. Auth0 is used to make it easier to integrate with third party applications, using (mostly) OAuth2 and OIDC under the hood.

OpenID Connect for authentication

Continuing with our job board example, we realize that forcing the users of our app to sign up before they can use it is cumbersome. They have to enter their name, email, etc. and remember yet another password.

To make things smoother, we want to leverage common identity providers such as Facebook, Google, LinkedIn, etc. This would remove the sign-up process friction. That’s where OpenId Connect (OIDC) comes in useful.
Using the OpenId authentication protocol, we have a standardized protocol to authenticate a user that’s already registered with a trusted identity provider. Once again, without the user needing to share any credentials.

Under the Hood: How OAuth2 and OpenID Work

OAuth2

OAuth2 is a protocol to allow for delegated authorization. It involves different parties:

  • a client: the application requesting the delegated authorisation
  • a resource owner: the user whose data the client is trying to request / act on.
  • a resource server: the protected API interfacing the user data that the client app wants to access.
  • an authorisation server: responsible for handling the authorisation and authentication before a client can access the resource server protected APIs. It essentially exposes an authorisation endpoint, and a token endpoint.

Communication Flow

Broadly speaking, the process works like so: 

  1. The user connects to the client application. The client needs to gain access to a resource owned by the user on another application, for example LinkedIn.
  2. The client redirects the user (via an http redirect), to the authorisation endpoint of the authorisation server (authorisation server of LinkedIn in our example)
  3. The user signs-in, and confirms that they grant certain permissions, expressed as scopes, to the client app
  4. The authorisation server redirects the user to the client on the callback url, along with an authorisation code to be exchanged by the client for an access token.
  5. The client calls the authorisation server on the token endpoint, and exchanges the authorisation code for the access token.
  6. Now the client can call the resource owner APIs and perform operations on behalf of the user, usually by embedding the access token inside an http authorisation header.

It is important to note that the privileges that the client app gains with the access token cannot exceed those of the user for which it was granted. Meaning that, for example, it is impossible for the client to read data of other users since the user that granted permissions did not possess those privileges in the first place.

sequenceDiagram
    actor User
    participant Client
    participant AuthorizationServer as  Authorization Server (authorisation endpoint)
   participant AuthorizationServerToken as  Authorization Server (token endpoint)

    User->>+Client: Requests feature requiring delegated authorisation
    Client->>-User: Redirects client to authorisation server

    User->>+AuthorizationServer: Forwards authorisation request 
    AuthorizationServer->>-User: Presents authorisation screen
   User->>+AuthorizationServer: Approves authorisation request
   AuthorizationServer->>-User: Redirects to client with authorisation code

   User->>+Client: Forwards authorisation code

    Client->>+AuthorizationServerToken: Sends Authorization Code
    AuthorizationServerToken->>-Client: Exchanges code for access token

Here’s an example of http request sent from the user to the authorisation server:

GET https://auth.example.com/authorize?
response_type=code&
client_id=client123&
redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback&
scope=email.read email.send

and a request example of a client exchanging an authorisation code for an access token:

POST /oauth/token HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
client_id=your_client_id&
client_secret=your_client_secret&
grant_type=authorization_code&
code=your_authorization_code&
redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback

Understanding Key Concepts

What are scopes ?

To specify the level of access the client needs on the user’s resources, we use scopes. Practically speaking, they are merely a space separated list of permissions that the client requests. 

Those permissions are specific to each resource server. For example, here’s the list of scopes for the gmail api.
We can see that to read and send mails, the scope parameter would be

scope=https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.send

Note that they don’t express only permissions. By adding the openid scope for example, we can request an id token along with an access token.

What is an access token ?

An access token is an opaque string of characters issued by the resource owner. The token enables access to the user’s resources, for a limited amount of time.
By opaque, we mean that the client application should not (and cannot if the token is encrypted) rely on its content. It is not intended to be read by the client, and its implementation can change at any time without notice. The implementation remains a private matter between the resource server and authorisation server.

What are grants

The flow we described above is the authorisation code grant. In this flow, the authorisation server returns an access code (which is just a string), to the client, through an http redirect on the user browser. This code is short-lived and can only be used once, by the application matching the one who made the request in the first place.
The client then reaches the authorisation server and exchanges that code for an access token. Usually, the client would need to be registered on the authorisation server, and thus provide a client secret when exchanging the code for a token, to prove its identity. This adds a layer of security.

Another common flow is the implicit grant. In this one, instead of returning a code, the authorisation server returns the access token immediately inside the redirect http request. Which means that the access token is returned to the client application through the user browser. Since the access token is exposed on the user side, this flow is considered less secure. It’s usually used when a client secret cannot be registered in advance, or safely stored, for example in the case of a mobile app.

2 other flows exists:

  • Resource Owner Password Credentials Grant: used when the user has a trusted relationship with the client application and can provide their username and password directly to the client application.
  • Client Credentials Grant: Used when a client application needs to access protected resources on behalf of itself, without the involvement of a user. 

OpenId

OpenId works almost the same way as OAuth2. The main difference being that it issues an id token.
In order to obtain an ID token, it is necessary to include openid in the requested scopes. Typically, email and profile are scopes that are also specified along with openid. This is meant to request additional information about the user.
Sometimes, the same endpoint is used for both openID and OAuth2, like for example on google. Meaning that by adding scopes related to OAuth2 permissions + the openid scope, we would get both an id token and access token at the same time.

Understanding Key Concepts

What is an id token ?

An ID token is a JSON Web Token (JWT) that contains information about an authenticated user. The information usually includes an id, a name, email but it’s not limited to that.

Unlike the access token, the id token is meant to be consumed by the client. Hence, its format is standardized. 

Example of id token

Here’s a possible example of Base64 id token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkhvbWVyIFNpbXBzb24iLCJpc3MiOiJMaW5rZWRJbi5jb20iLCJhdWQiOiJteUpvYkJvYXJkQXBwLmNvbSIsInNjb3BlcyI6ImNsaWVudC5yZWFkIiwibm9uY2UiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjIsImVtYWlsIjoiSG9tZXIuU2ltcHNvbi5ob21lckBnbWFpbC5jb20ifQ.PxUJnWsb6vt3qS90S1g-Sy2qzwzMHq3kqYZJ9aAkk0Q

If we decode it on the jwt.io debugger, we get:

a header

{
  "alg": "HS256",
  "typ": "JWT"
}

where alg is the signature algorithm used

a payload

{
  "sub": "1234567890",
  "name": "Homer Simpson",
  "iss": "LinkedIn.com",
  "aud": "myJobBoardApp.com",
  "scopes": "client.read",
  "nonce": "1234567890",
  "iat": 1516239022,
  "exp": 1516242622,
  "email": "Homer.Simpson.homer@gmail.com"
}

Those attributes are commonly called claims:

  • sub: the unique id identifying the user
  • iss: the issuer of the token
  • aud: the audience of the token, ie., who the token was generated for
  • scopes: the set of permissions requested
  • nonce: a random value generated of the client app for security purposes (prevents replay attacks)
  • iat: issued at
  • exp: expires at

The list of claims that can be requested, along with the other information of importance are listed in a discovery endpoint. Here’s the discovery endpoint of google openID.

Conclusion

Overall, OAuth2 and OpenID Connect are essential tools for modern web development. To continue on this topic, I invite you to get the Auth0 free ebook that dives much deeper into the topic.