IV.a. Getting started with Oauth2

elevysi · 04 July 2019 |

If you are like me and browse the web tirelessly, you must have encountered Oauth2 at some point during one of the trips within the web. Mine came in 2013 as I was investigating how I could securely connect two apis. Oauth2 at that moment seemed like the best choice and I went with it. However, in the hurry of the implementation, I never got to deeply explore the protocol and understand the concepts behind it. One of the biggest misconceptions that I have carried all along is the confusion between authentication and authorization since I have always referred to Oauth2 as an authentication protocol. Well, it is damn time I set the record straight and dissipate the confusion. The book Oauth2 in Action defines it as follows: 

The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf. 

Let’s explore Oauth2 protocol through a basic case; let’s assume the case of a person viewing a post on this blogging website. The post displays at the end of the article details about the author, namely a short biography; interestingly, the blogging website lies in a microservices landscape and different services are responsible for delivering different sections on the blogging website:

  • The post content itself comes from the article service, one through which a user can create, edit and visualize articles
  • The author biography comes from the social service, one that lets users describe themselves in order to network with other system users

When users are viewing the articles from the articles service, the latter is responsible for fetching details about the author of the article through an api (application programming interface). 

 

Let’s assume that you can only see a biography in case the current user is connected to the author of the article through the social service. In this case, the current user has access to the protected resource (the author’s biography) and needs to delegate the article service the right to access the bio and show it on its user interface. Such an act of delegation is what Oauth2 is about; a delegation process through which a user authorize an api to access a protected resource (on a different application). This will usually go through the authorization server, one that will act as a middle man between the two parties (namely the client and the protected resource) and let the user accept or reject the delegation.
Simply put, the Oauth2 protocol lets users allow a client to access a protected resource on their behalves. Let's reduce the confusion on the terminology by having a closer look at the different actors involved:

  • Resource owner : it usually refers to an end-user that has some sort of access on a protected resource; the end-user will be requesting access to a protected resource in the course of the protocol via a client;
  • Protected resource : it refers to the element the resource owner has an access right on; the system responsible for delivering these resources is refered to as a resource server; the term Resource Server  is used when refering to the entity or application hosting the protected resource;
  • Client : it refers to the requesting api through which the resource owner is asking the access to the protected resource;
  • Authorization server : it refers to the application through which the resource owner accepts, rejects or selects the set of protected resources to which the client should have access on their behaves. 

The classic Oauth2 process undergoes the steps described below:

  • The resource owner tells the client that the latter should act on behalf of the former in order to access the protected resource
  • The client hands the process to the authorization server so that the resource owner can authorize the delegation process
  • The resource owner accepts the delegation to the client through the authorization server
  • The authorization server issues an access token to the client
  • The client uses the acquired access token and presents it to the protected resource
  • If valid, the protected resource accepts the access token and delivers the content to the client

Access Tokens, JWT and JOSE

If successful, the delegation process yields an access token, produced by the authorization server and delivered to the client. The access token is a chain of alphanumeric characters that once acquired, can be used by the client to request access to protected resources for as long as it remains valid without going through a delegation acceptance again from the resource owner; please note that an access token has an expiry date amongst other attributes such as a scope (i.e. scope is the set of protected resources the resource owner has agreed authorization upon on).
The protected resource is responsible for validating the access token presented by the client by assessing the scope related to the access token and its time validity. This can be executed either through online introspection (i.e. asking through an endpoint from the authorization server if a given token is valid or not) or through the implementation of a custom mechanism ( such as a read access to a shared storage of tokens between the authorization server and the resource server).
Due to the limitation of Oauth2 regarding the structure of issued tokens, traditional access tokens are short on self validation through a simple assessment of the content for instance. The JWT (JSON Web Token) standard was introduced to overcome this aspect. It is a token format that allows information to be shared with the resource server via the delivered token; in practice, similarly to the access token, a JWT is a chain of alphanumeric characters. However, the JWT carries decodable information and is formed with up to 3 sections of characters divided by dots (.); each of these sections is a base64 serialization of a JSON object. An example of a valid JWT object is shown below (the left hand side indicates the raw JWT Token while the right hand side is the information it holds decoded):




The three potential parts of a JWT format are:

  • The Header which when base64 decoded is an object of type below:
    {
      "alg": "HS256",
      "typ": "JWT"
    }
    The typ key holds the value for the type of content that is addressed in the token, i.e. JWT in our case while the alg key represents the algorithm used to encrypt or sign the token. The value none says that the token is unsigned.
  • The Payload is a serialization of a JSON object holding the information we need to pass through the token. E.g:
    {
      "sub": "basicService",
      "aud": [
        "authorizationResourceApi"
      ],
      "scope": [
        "read"
      ],
      "iss": "http://localhost:2000",
      "exp": 1563370967,
      "jti": "cd62482b-23d2-49a7-b433-983ffbe64f69",
      "client_id": "basicService"
    }
  • The Signature portion of the token 


As a standard,
JWT holds within its payload claims that are meant to be standard across the range of applications. Some of these are but not limited to: 

  • sub: meant to be a unique identifier of the resource owner;
  • iss: meant to be the issuer of the token; usually a URL of the authorization server that issued the token;
  • exp: meant to be the expiration timestamp of the token 

From the JWT standard, we provide the resource server a way of validating the access token directly without asking the authorization server or requiring complex shared mechanisms to access tokens. It simply assesses the information contained into the access token and decides whether or not it can proceed providing the client a protected resource.

One concern of these
JWTs can be their lack of security in case they are unsigned. Their aim being to allow the protected resource  and the authorization server to be able to validate the access tokens, they carry information that should be readable to both parties. The issued tokens, however, need to stay opaque to the client, in order to avoid the latter forging its own tokens for access to the protected resource without the resource owner consent for instance. This shortcoming is addressed by the JSON Object Signing and Encryption ( JOSE) specifications. It is a new standard that allows the signing of an access token through symmetric (shared secret and HS256 for instance) or asymmetric (PKI - Public Key Infrastructure and RS256 for instance) algorithms such that the information can be validated from both the authorization server and the protected resource while restricitng a potential forge from a third party or the client. 

How does Oauth2 happen in practice?

We have seen so far the theoretical protocol but how would Oauth2 happen in real life? It does not just happen on the fly; beforehand, the parties involved need to be accustomed to each other before the whole process can be established; some of the preleminary set-up involves the following steps:

  • The authorization server provides unique identifiers to its set of clients, namely the client_id, along with a secret, the client_secret; any authorization requests issued to the authorization request need to carry the two pieces of information
  • The client on the other side needs to be aware of 2 endpoints exposed by the authorization server:
  • The client can also be configured to communicate more information to the authorization server such as a redirection URI, namely a redirect_uri, or the scope of information to which it needs access to. Obviously the client initiates the process and is hence aware of the resource server; the latter will be expecting a valid access token in order to deliver the protected resource.

Acquiring Access Tokens Through Oauth2 Flows

There are several schemes through which a client can acquire an access token; the Oauth2 specs define 4 different flows through which the authorization can be executed: 

  • Authorization Code: this is the most complete flow upon which all the others are built on. It is executed as follows:
    • The client redirects the resource owner to the authorization server authorization endpoint; the authorization endpoint URL is called along with some parameters so as to indicate what the client expects and what should be executed next, once the authorization server is ready to issue a response; these URL parameters are: 
      • response_type : shall be set to code for this oauth2 grant type
      • client_id: the client_id of the client involved in the interaction
      • redirect_uri: the base client uri to which the authorization server will redirect to after a successful authorization
      • state (optional) : a parameter used by the client such that the authorization server includes the same value as a redirection parameter in order to prevent cross-site attacks
        An example redirection URL would be in the following form: http://authorizationserver/authorize?response_type=code&client_id=id&redirect_uri=http//client/callBack&state=randomString
    • The resource owner is prompted to accept the authorization delegation from the authorization endpoint

    • The authorization server provides a code that is transmitted to the client’s indicated redirect uri value as a query parameter, along with the value of the state parameter that was passed to it from the client, i.e. http://client/callBack?code=theCode&state=randomString
      The first set of interactions uses a front channel; i.e. all the steps described above are executed via the browser
    • After the redirection back to the client, it needs to execute a back channel HTTP POST request to the authorization server token endpoint with the acquired code along with a grant type and a redirect_uri as the form request body; i.e.
      data = {
      	grant_type: 'authorization_code',
      	code: theCode,
      	redirect_uri: http://client/callBack
      };

      Please note that the redirect_uri needs to match the value provided in the first interaction with the authorization server. This POST request needs to include within its authorization header an HTTP Basic authentication which will be a base64 concatenation of the client_id and client_secret; i.e

      headers = {
      	'Content-Type': 'application/x-www-form-urlencoded',
      	'Authorization': 'Basic ' + base64(client_id.client_secret)
      };

      Why not exhange an access token instead of an authorization code in the first place? Why go through the struggle of an extra interaction to exchange first an authorization code first and an access token after, you'd ask me! The answer lies in the notions refered to as front and back channels; the latter is accepted as more secure than the former. The authorization code exchange uses a front channel, i.e. the browser, while the access token uses a back channel. The extra step involved in swapping an authorization code is implemented to improve on the security.

    • As a response to the above request, the authorization server replies with a JSON object containing both the access token and the token type; i.e.
      {
        "access_token": "123AccessToken456",
        "token_type": "Bearer"
      }
    • Oauth2 in Action describes the remainder of the interaction as follows:
      The kind of OAuth access token that we have is known as a bearer token, which means that whoever holds the token can present it to the protected resource. The OAuth Bearer Token Usage specification actually gives three ways to send the token value:
      • As an HTTP Authorization header
      • As a form-encoded request body parameter 
      • As a URL-encoded query parameter 
    • Hence, the client makes the request to the protected resource by including the access token preferably as an HTTP Authorization header; i.e.
      headers = {
            'Authorization': 'Bearer 123AccessToken456’
      };

 

  • Client Credentials: This flow is intended to be used when there are no end-user interactions involved; i.e. when the client is the resource owner (ideal for api to api interactions). For this case, the client makes an HTTP POST to the authorization server token endpoint, specifying within the form request body the grant type as being client_credentials and providing an HTTP Basic Authentication.
    The rest of the interaction is similar to the authorization code grant flow; below is a mock execution from Postman.


  • Implicit Flow: This flow is intended to be used by browsers and is less secure than the previous two since it exposes the client_id to the browser and does not need a client_secret; it is ideal for Javascript applications such as a SPA (Single Page Application). This time, the client redirects the resource owner to the authorization server authorization endpoint, as it was the case for the authorization code, except that the response_type value will be set to the access token instead of code; i.e.
    http://authorizationserver/authorize?response_type=token&client_id=id&redirect_uri=http//client/callBack
    Instead of replying with a code, the authorization server redirects to the client’s callback with the access_token as URL parameter; i.e.
    http://client/callBack?access_token=theAccessToken&type=Bearer
    It is then up to the client to use the subsequent requests to the protected resource with the parsed access token
  • Password Flow (also known as the Resource Owner Credentials grant type): This grant flow is the less recommended and the less secured authorization specified in the Oauth2 protocol. In this case, the client asks the resource owner credentials and posts them to the authorization server in exchange of an access token.  

Please note that within an Oauth2 authorization code flow, the authorization server includes  a refresh token along the access token as a response to the client. The aim of the refresh token is to allow the client to request a new access token once the first one has expired without implying a resource owner interaction. 

Authorization vs Authentication

So far we have realized that the client acquires an access token at the end of the Oauth2 protocol interaction; the Oauth2 specifications do not include the user information as data contained in the access token (in case it is a chain of characters holding data in reversible form for instance, i.e. JWT).
Consequently, in a basic Oauth2 protocol implementation, the client does not have information about the current user; it can query the protected resource api by using the access token but the latter does not tell the client about the current resource owner or end-user.
The book Oauth2 In Action highlights the fact that Oauth2 is not an authentication protocol through the following excerpts:

Authentication, in this context, is what tells an application who the current user is and whether they’re currently using your application. It’s the piece of the security architecture that tells you the user is who they claim to be, usually by providing a set of credentials (such as a username and password) to the application that prove this fact.

Instead, the OAuth 2.0 access token allowed the client to act on the user’s behalf. This is a powerful paradigm for delegated client authorization, but it’s rather antithetical to authentication, in which the whole point is figuring out whether the user is there and who they are.

Oauth2 beyond an authorization protocol and the use of OpenID Connect (OIDC)

Oauth2 comes with its set of limitations, especially due to the lack within the protocol specs of proving an identity to the parties; an eavesdropping process could maliciously use an unsigned access token to access the protected resource for instance. Moreover, the Oauth2 specifications allow the use of so-called refresh tokens, tokens which are meant to be used by the client to acquire valid access tokens in case the previously acquired access token has expired for instance; the problem is that nothing guarantees the authorization server that the resource owner is indeed still present when this refresh process is executed. Let's mention that the resource owners need to authenticate themselves at the authorization server so as to provide authorization. The Oauth2 protocol is an authorization protocol that relies on an authentication protocol and upon which an authentication process can be built on top of (let's just say that Oauth2 must have inspired the movie Inception).

Since we know that we rely on an authentication process, let’s see how we can exploit this to prove the identity of the current user to the parties involved.
When it comes to authentication, the actors differ from those described in the Oauth2 protocol specs; the authentication actors are:

  • The end-user : the party that is interacting with the system
  • The RP (Relying Party) : the party that needs authentication from the user
  • The IdP (Identity Provider) : the party that authenticates the user

Adding authentication on top of the Oauth2 authorization shall go through a mapping of the above actors to those involved in an Oauth2 authorization process. Let’s map them as follows:

  • The end-user as the Oauth2 resource owner
  • The relying party as the Oauth2 client
  • The identity provider as the combination from Oauth2’s authorization server and protected resource

Within the authentication process, the end-user will be interacting, as usual, with the relying party ( or the Oauth2 client). The latter hands the process to the identity provider which can handle authentication for the current user and provide the result to the other parties through secure and relying party (or Oauth2 client) parsable tokens, namely JWTs. This way, both the client and the protected resource know about the current user and could be further provided means for enforcing identity through a call to an IdP's exposed endpoint, namely the UserInfo endpoint as specified for OIDC.
The need to prove the identity on top of the
Oauth2 process as well as the practise of standardization has led to OpenID Connect or OIDC. An excerpt from Oauth2 in Action describes it as follows:

OpenID Connect3 is an open standard published by the OpenID Foundation in February 2014 that defines an interoperable way to use OAuth 2.0 to perform user authentication. Since the protocol is designed to be interoperable, an OpenID client application can speak one protocol to many identity providers instead of implementing a slightly different protocol to each identity provider.

The OIDC specs define a standard for providing identity on top of Oauth2 authorization. Moreover, the specifications address the format of the JWT object such that different implementations from different providers respect a common standard.
The provided JWT object shall be an object carrying both the access token for authorization and the id_token for the authentication along with a token_type. The standard defines three keys within the JWT object namely access_token, id_token and token_type.

The interesting and new concept here is the id_token, the key within the JWT token that carries authentication data. It is itself a base64 serialized token that carries within its payload information, carefully formatted in a standard format with keys such as  iss, sub, exp, … whose meaning matches the explanation provided earlier in this post.
Moreover, the OIDC protocol defines a standard for accessing the user profile through an endpoint called the UserInfo endpoint, the user profile being a set of attributes related to the current user. The Userinfo endpoint is a URL rather than a parameterizable URI that is issued to the identity provider so that information about the current user is retrieved hence usable by both the client and the protected resource.
Keep in mind that the IdP has been mapped as corresponding to a combination of the authorization server and the protected resource from the Oauth2 protocol. As such, the query to the UserInfo endpoint follows an Oauth2 authorization request through which the client will be asking for protected resources (the user profile) by using an access token.

One more interesting aspect about OIDC is the standardization of authorization scopes; the scope will define which data or subset of user profile information is to be returned from the UserInfo endpoint. The most common scopes are:

  • openid : scope for the sub
  • profile: scope for attributes name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, updated_at
  • email: scope for attributes email, email_verified
  • address: scope for attributes address, a JSON object which itself contains formatted, street_address, locality, region, postal_code, country
  • phone : scope for attributes phone_number, phone_number_verified

The result of the delivered JWT object through an OIDC protocol holds within its payload the following keys:

{
	"access_token": "987tghjkiu6trfghjuytrghj",
	"token_type": "Bearer",
	"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjkwMDEvIiwic3ViIjoiOVhFMy1KSTM0LTAwMTMyQSIsImF1ZCI6Im9hdXRoLWNsaWVudC0xIiwiZXhwIjoxNDQwOTg3NTYxLCJpYXQiOjE0NDA5ODY1NjF9.LC5XJDhxhA5BLcT3VdhyxmMf6EmlFM_TpgL4qycbHy7JYsO6j1pGUBmAiXTO4whK1qlUdjR5kUm ICcYa5foJUfdT9xFGDtQhRcG3-dOg2oxhX2r7nhCjzUnOIebr5POySGQ8ljT0cLm45edv_rO5fSVPdwYGSa7QGdhB0bJ8KJ__RsyKB707n09y1d92ALwAfaQVoyCjYB0uiZM9Jb8yHsvyMEudvSD5urRuHnGny8YlGDIofP6SXh5-
	1TlR7ST7R7h9f4Pa0lD9SXEzGUG816HjIFOcD4aAJXxn_QMlRGSfL8NlIz29PrZ2xqg8w2w84hBQcgchAmj1TvaT8ogg6w"
}

Throughout this post we have seen the key concepts to implementing authorization and authentication. Let's map our two services to the concepts that we have seen so far:

  • The basic service will be
    • In the context of Oauth2 authorization protocol:
      • a resource server holding protected resources, namely posts
      • a client to the authentication service users (e.g. retrieving posts authors)
      • a resource-owner when no end-user is involved in the interactions
    • In the context of authentication:
      • a relying party
  • The authentication service will be:
    • In the context of Oauth2 authorization protocol:
      • an authorization server
      • a resource server holding protected resources about the system users
      • a client to the basic service posts (e.g. retrieving posts for a given user)
      • a resource-owner when no end-user is involved in the interactions
    • in the context of authentication:
      • an identity provider and, as such, will act as a single sign on server (sso) for the services contained into our cloud based landscape

The resource owner will in most of the cases be the end-user interacting with the system or an Oauth2 client in some cases.
We can now go ahead and implement Oauth2 into the authentication service.