OAuth2 / OpenID Connect overview

Introduction

The purpose this text is not to write yet another general OAuth 2.0 overview. There are plenty already. Instead this text is intended as a “how to get started with OAuth2 and OpenID in Equinor” guide.

Just using Google as a learning tool does not work very well, for many reasons:

  • The terminology can be confusing until properly defined (and it is not always consistent)
  • There are multiple different use cases supported by the OAuth 2.0 protocol and you need to know which is relevant for you, so you know what to search for
  • There are a lot of libraries out there, and it’s not always easy to determine what are the limitations of the library versus the underlaying OAuth 2.0 standard
  • There are several OAuth 2.0 providers and it’s not always easy to determine what is a custom add-on, and what is part of the OAuth 2.0 standard, and how well they conform to the standard
  • There are several standards in addition to OAuth 2.0 which may or may not be relevant depending on your use case (e.g. web client vs web application vs web api etc)

OAuth 2.0 concepts

The purpose of the OAuth 2.0 protocol

From the specification:

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.

It is worth unpacking this paragraph a bit. But first, we see which roles are defined in OAuth 2.0 specification

Resource owner

An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user.

Resource server

The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens.

Client

An application making protected resource requests on behalf of the resource owner and with its authorization. The term “client” does not imply any particular implementation characteristics (e.g., whether the application executes on a server, a desktop, or other devices).

Authorization server

The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.

OAuth 2.0 Roles

With these 4 roles defined, let’s look at the purpose of OAuth 2.0 again.

“OAuth 2.0 is authorization framework.”

That means it is written control access to protected resources exposed via REST API’s. Although a resource owner need to identify himself/herself/itself to an Authorization server in order to delegate access to a Client, how this is actually done is not part of the OAuth 2.0 specification.

Again, OAuth 2.0 specifies the protocol for how to delegate access from a Resource Owner to a Client. How the Resource Owner identifies himself/herself to the Authorization Server is not part of the spec, and this is actually a big advantage. Any mechanism, such as Multi Factor Authentication or face recognition may therefore be used to identify the Resource Owner. It is also worth noting that OAuth 2.0 is not a specification intended to verify the identity of and end-user towards a web server. (That’s what OpenID Connect is for, which we will get back to.).

“OAuth 2.0 […] enables a third-party application to obtain limited access to an HTTP service”.

Now that we have the roles defined we should read that sentence as “OAuth 2.0 […] enables a Client to obtain limited access to a Resource Server”. “Limited access” is a reference to Scopes, which allows a Resource Server to define a more granular access control. For example a scope could be “Read”, while another was “Read Write”. In addition, a scope could determine access to various parts of the Resource Servers API, such as “Read User Info” or “Read Calendar”

“limited access […] on behalf of a resource owner, or by allowing the third-party application (i.e. the Client) to obtain access on its own behalf.”

OAuth 2.0 is referred to as a “delegation framework” which might sound a bit academic at first. If we look at what OAuth 2.0 originally set out to solve this might be clearer: In most cases a Client is either a Javascript Single Page Application running in the Browser, or a traditional server side web application written in any language. Before OAuth, if an end user needed the Client to call another service (Resource Server), the end user would often share his/her password with the Client. In OAuth 2.0, the end user will normally[^1] never share his/her password with a Client, but instead share a temporary access token, which from a security perspective is a whole lot better than sharing a password. By sharing the access token, the end user effectively delegates access to the Resource Server to whoever/whatever possesses the access token.

[^1] The exception is the Resource Owner Password Credentials grant type. Even though less secure than the other flows, it is better than distributing a password further to the resource server. In this flow, the username/password is exchanged for an access token by the Client, which in turn is used towards the Resource Server.

Tokens

From the spec:

Access tokens are credentials used to access protected resources. An access token is a string representing an authorization issued to the client. The string is usually opaque to the client. Tokens represent specific scopes and durations of access, granted by the resource owner, and enforced by the resource server and authorization server.

Refresh tokens are credentials used to obtain access tokens. Refresh tokens are issued to the client by the authorization server and are used to obtain a new access token when the current access token becomes invalid or expires, or to obtain additional access tokens with identical or narrower scope

A good analogy for an access token are old fashioned bus tickets. They typically had the following characteristics:

  • expiration time (tokens also has an expiration time)
  • valid for a specific bus line / zone (tokens also has a defined validity, e.g. App Client ID in the “aud” claim, and scopes in the “scp” claim)
  • the bus driver did not care who presented a valid ticket (resource servers also do not care who uses a token)

Note that Refresh tokens will only be issued to trusted/confidential clients, such as a server side web application that identifies itself to the authorization server. A client running in the browser (implicit flows) should not be considered confidential and should not receive refresh tokens.

The format of the Tokens issued by Azure Active Directory is defined in the JWT (JSON Web Token) specification. JWT is just one of the ways a token may be formatted, the OAuth 2.0 spec does not dictate a format. (However, OpenID Connect builds on JWT)

Further reading:

The OAuth 2.0 specification

The JWT specification

Microsoft Identity Platform

We’ve lightly touched on the basics of OAuth 2.0 in this section. There’s another scenario which is very common, and that is for a Client e.g. a web application to need to verify the identity of an End User towards a web application. This is what the OpenID Connect specification is for. Just as for OAuth 2.0, the web application (Client) does not need to know or validate the end user password.

OpenID Connect concepts

OpenID Connect is a standard that build onto the OAuth 2.0 specification. The roles are the same, except they have different names. Client is often referred to a the Relaying Party and the Authorization Server could be referred to as the OpenID Provider or sometimes Identity Provider (IdP).

Unlike for OAuth 2.0, the Resource Server plays a lesser role here. Typically OpenID Connect is used for verifying the identity of the end user between the user agent and a server side web application or a SPA (Relaying Party). If the Relaying Party needed to invoke a web API on behalf the end user, it would need an access token to do so.

Typically ID tokens are for web applications where the identity of the user matters, while regular access tokens are used when accessing API’s where the API does not care who the consumer is (besides that the consumer has access). One example:

A bus driver does not care who the passenger is, so an access token is sufficient.

At the check-in counter to a flight, the identity of the passenger does matter, so here we’d use an ID token.

Postman example

We have a server side web application that maintains a user session via an OpenID ID Token and in addition needs to call one or more web api’s (Resource Server) on behalf of the end user. Reponse type ID Token + Token

Summarized

In Azure AD v2, OpenID can be looked at just like another scope, however ID token is returned separately from any Access tokens or Refresh token. Again, Refresh tokens will not be available for public clients.

Azure AD & OAuth2 flows / grant types

The primary use cases for OAuth / OpenID are the following:

  • Authenticate a user (OpenID Connect)
  • Role Based Access control for a user (OpenID Connect)
  • Use an API written by someone else (OAuth 2.0)
  • Expose an API written by you to someone else (OAuth 2.0)

To support these use cases, there are a number of flows (variations of the protocol) that should be used. For example, is the Client a Single Page Application or a web server, is the User Agent/Client running on a device with a GUI, etc?

Before we look at the flows, it’s important to be aware that Azure AD has two versions of the OAuth 2.0 protocol. v1 and v2. For example, when googling, it’s very easy to land in the v1 section of the documentation, when you intended to use v2.

The v1 and v2 endpoints are available at different URL’s:

v1:

https://login.microsoftonline.com/{tenant}/oauth2/authorize
https://login.microsoftonline.com/{tenant}/oauth2/token

Single Page Application must use ADAL or an equivalent library.

v2:

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token

Single Page Applications must use MSAL or an equivalent library.

For applications written in Equinor, {tenant} should be replaced by either 3aa4a235-b6e2-48d5-9195-7fcf05b459b0 or equinor.com. It very important to make a conscious choice over which version of the API to use. The API’s themselves and functionality is different between the two. v2 supports scopes, which allows more granular access control to parts of an API. (Example: the Microsoft Graph API makes heavy use of scopes to provide granular access to its functionality.) In addition, v2 supports more grant types / flows, which means it can be used in a wider range of scenarios.

It’s important to know that /authorize is intendent for User Agent / Browser communication. This is where the login process happens. For implicit flows this is also where you get the access and/or ID token (but never refresh tokens). User Agent <-> /authorize communication is referred to as front-channel communication.

The /token endpoint is for communication with the Client. This is where a client retrieves tokens. Client <-> /token communication is referred to as back-channel communication. A Browser should never access this endpoint.

Azure AD supports the following flows (the links are to the V2 docs):

OpenID Connect

Used when the identity of the end user matter. Supported in V1 and V2, but with differences.

OAuth 2.0 Implicit Grant Flow

Useful for Single Page Applications directly accessing back-end API’s Supported in V1 and V2

OAuth 2.0 auth code grant

Useful for Web Application where the server maintains client session control. Can be combined with refresh tokens. Considered more secure since the web client (User Agent) never sees the access token. Supported in V1 and V2

OAuth 2.0 on behalf of flow

Useful in a scenario an API A needs to call API B on behalf of the User, i.e. a request is chained through multiple API’s. Supported in V1 and V2

OAuth 2.0 client credentials flow

Used when the Client accesses a Resource Server on it’s own behalf. Admin consent needed. Supported in V1 and V2

OAuth 2.0 device code flow

Example: Azure Command Line Interface. Login on a separate device. Useful for obtaining an access token where no graphical browser is available. (Unix shells, TV’s, etc) This is an extension to the original OAuth 2.0 specification (https://tools.ietf.org/html/draft-ietf-oauth-device-flow-15) Supported in V2 only

OAuth 2.0 Resource Owner Password Credential flow

The User sends the username and password to the Client, which trades the credentials in for an access token. Supported in V2 only

OAuth 2.0 SAML bearer assertion flow

The Client exchange a SAML assertion for an access token. Supported in V2 only

JWT

One way of structuring access tokens is defined in the JSon Web Token standard. OAuth 2.0 does not say how a token looks like. It doesn’t even need to contain information, but since it is very useful to transfer information in the token, Azure AD uses JWT.

Example JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1 (...)

This is not encrypted (however it is signed). This text is just a Base64 encoded JSon text. That is also why the Azure AD access and ID tokens always start with “ey” which is how {“ (the start of the JSon block) looks Base64 encoded.

Decoded it looks like this:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "ie_qWCXhXxt1zIEsu4c7acQVGn4",
  "kid": "ie_qWCXhXxt1zIEsu4c7acQVGn4"
}.
{
  "aud": "xxxxx",
  "iss": "https://sts.windows.net/3aa4a235-b6e2-48d5-9195-7fcf05b459b0/",
  "iat": 1566391329,
  "nbf": 1566391329,
  "exp": 1566395229,
  (...)
  "family_name": "Andersen",
  "given_name": "Nils Hofseth",
  "ipaddr": "143.97.2.42",
  "name": "Nils Hofseth Andersen",
  (...)  
  "roles": [
    "ROLE_A",
    "ROLE_B"
  ],
  (...)
  "ver": "1.0"
}.
(RSASHA256 signature)

How to verify the signature is a topic for another article.

Azure AD App Registrations

Shown in the Azure portal

Clear consent

Logout

Azure Security Engineer