OAuth 2.0 is an authorization framework having different possible flows (authorization code, implicit, resource owner password credentials and client credentials).
Learning the difference between each flow can take some time, but it is not important for understanding this post.
For the moment you only need to know the following facts:
- the authorization server receives some credentials and produces an authorization token.
- the resource server validates this token and provides access to protected resources.
The following diagram shows the simplest OAuth 2.0 flow: the client credentials.
In this case there is no user involved and the client application provides the credentials directly to the authorization server. When the user is involved the credentials insertion is more complex and the client usually doesn’t receive directly the credentials, however the next calls remain the same (the client receives the token from the authorization server and sends it to the resource server), so we can use this flow for simplifying the examples.
So, how the resource server performs the token validation? It depends on the token format and usage and there is usually an agreement between the authorization server and the resource server.
The OAuth 2.0 specification doesn’t set any constraint on the token content. Access token attributes and the methods used to access protected resources are defined by companion specifications.
Tokens can be classified in 2 categories:
- reference tokens: tokens which are just a handle, a random string, and it is necessary to do an additional call to the authorization server in order to validate them and retrieve their content (the authorization server provides a “token inspection” endpoint);
- self-contained tokens: tokens which contains all the information inside and are signed or encrypted, so they can be validated locally by the resource server. Self-contained tokens provide a better scalability but it is usually not possible to revoke them.
The most known self-contained tokens are JWT (JSON Web Tokens). One of the best resources on the Internet for playing with JWT is jwt.io.
JWT are signed or encrypted with some kind of keys. This means that those keys need to be shared between the authorization server and the resource server, so the latter will be able to validate the token generated by the first. Keys are usually distributed between the servers using dedicated JWKS (JSON Web Key Set) endpoints. They simply list the available keys using a specific format. If asymmetric encryption is used the JWKS endpoint can be public, because it displays only public keys.
An example of a JWKS content:
{
"keys":[
{
"kty": "RSA",
"kid": "<key_unique_id>",
"use": "sig",
"n": "<base64_encoded_public_key>",
"e": "AQAB"
}
]
}
The most commonly used tokens are bearer tokens, described in RFC 6750. They can be both reference tokens or self-contained tokens.
The specification says:
Any party in possession of a bearer token can use it to get access to the associated resources (without demonstrating possession of a cryptographic key).
This can be confusing, especially thinking about the keys inside JWKS. To understand the meaning of this, we need to talk a bit more about the client behavior and also introduce another kind of tokens: MAC tokens.
When a client uses a bearer token it simply adds it to the HTTP header:
Authorization : Bearer <token>
If a different application is able to steal this token it can use it and the resource server will not be able to notice any different. The resource server only knows that the token is generated by a well known authorization server.
MAC tokens are specifically designed to avoid this. MAC tokens are not widely used and the specification describing them is still a draft.
MAC stands for Message Authentication Code and indicates hash functions that take as input both a message and a secret key. In case of OAuth 2.0 MAC tokens, the secret key is a temporary key (called also session key) provided by the authorization server to the client together with the access token. The client adds the computed hash to the requests it makes to the resource server, in order to demonstrate the possession of the session key.
The MAC access token is a JWT encrypted with a key shared between the resource server and the authorization server. The JWT contains also the session key. The resource server is able to decrypt the token and can use the session key inside it to verify the hash computed by the client.
The protocol involves also the usage of timestamps or sequence numbers to avoid that the hash can be reused.