JSON Web Tokens (JWT) as OAuth 2.0 Bearer Access Tokens

Decentralized and Stateless API Security Architecture

Johann Dilantha Nallathamby
Identity Beyond Borders
6 min readMay 8, 2020

--

Introduction

JSON Web Tokens (JWTs) [1] are rapidly becoming a popular choice for security tokens due to the following properties:

  1. Their representation is compact, and they are small in size, which makes the transmission of a JWT efficient using the HTTP protocol.
  2. They provide a structured way to declare users and entitlements.
  3. They provide extensibility to the structure so that you can modify it to suit your application needs.
  4. They can be signed to prevent tampering on the client side.
  5. They can be encrypted to prevent sensitive information leakage on the client side.

Given these interesting and useful properties, it is not surprising that JWT bearer access tokens are rapidly catching up with the popularity of opaque bearer access tokens [2]. This is made possible due to the provision of extensible token types in OAuth 2.0.

Benefits of JWT Bearer Access Tokens

One of the major benefits of JWTs are that they are very portable. Modern applications tend to have many faces: the logged in view, the logged out view, or the restricted view. It’s all about fine-grained entitlements and access control. All the users get to use the same application, but they may have different levels of access based on fine-grained entitlements. You’ll find yourself building authorization logic for your frontend and your backend. JWTs instantly become a very suitable candidate for this purpose. They can be used by your frontend and your backend for authorization. Using the same token improves performance and developer experience. Typically, in addition to the JWT metadata, the JWT bearer access tokens contain OAuth 2.0 metadata such as granted scopes and expiry time. It could also contain user entitlements represented as roles and permissions depending on the specific implementations. This kind of information allows the application to do things like authorization and access token renewal interval calculation, entirely on the client-side.

Another major benefit of using JWTs as bearer access tokens, is that it makes the API security layer decentralized and stateless because all the information that your API security layer needs is in the JWT. What does this mean? With standard opaque bearer access tokens, the API Server needs to introspect the access tokens with the OAuth 2.0 authorization server by invoking its introspection endpoint. This means that the OAuth 2.0 authorization server holds information related to the validity of the access token which makes the API security layer centralized and stateful. In contrast, with JWT bearer access tokens, which is a kind of self-contained and self-verifiable access token, the API Server can introspect the access token by itself. In order to introspect the JWT bearer access token, the API server could use the X509 public certificate to validate its signature, and establish the authenticity and integrity of the token. It could also use the exp attribute to make sure that the access token is still active. Likewise there could be more standard and non-standard information in the JWT that can be used to validate and authorize the OAuth 2.0 client’s access to APIs. This greatly improves access patterns against the authorization server and reduces the load on it, and improves speed of token introspection during resource access. This decentralized and stateless solution has better scalability than a centralized and stateful system using opaque access tokens.

Caveats and Solutions

The decentralized and stateless architecture of JWT bearer access tokens however, does not come without drawbacks. One of the main drawbacks is the difficulty in communicating changes to the JWT’s access rights, due to changes in access rights of the user or client, or revocation of the token, across to the various components like the resource servers, unlike in the centralized and stateful architecture.

  1. In a fully distributed and stateless implementation, the OAuth 2.0 authorization server will not store any metadata related to the issued JWT bearer access tokens. Hence there is no way of revoking an issued token at the OAuth 2.0 authorization server.
  2. Even in some stateful implementations, only certain metadata information about the access token may be stored for audit purposes. The JWT’s access rights may be updated in this entry in the OAuth 2.0 authorization server. But still, in the decentralized architecture, since the API Server will not be directly interacting with OAuth 2.0 authorization server, the OAuth 2.0 authorization server cannot intercept the access token introspection process.

So as an acceptable optimization, most vendors recommend having a low expiry time for access tokens and a long expiry time for refresh tokens. Though not perfect, some organizations with non-critical use cases and relatively relaxed security policies, would be mostly fine with this optimization. This split way of handling tokens has other benefits too:

  1. The shorter lifetime for leaked access tokens reduces the chance of a leaked token allowing access to a protected resource.
  2. Since refresh tokens will continue to be opaque tokens and not self-contained tokens, they can continue to be used as an identifier to revoke the authorization grant.
  3. Opaque refresh tokens don’t impact performance as much as opaque access tokens, since they are used more sparsely.
  4. Even if refresh tokens are compromised, they cannot be used to renew the access token without the client authentication in the case of confidential clients.
  5. Sliding-sessions (sessions that expire after a period of inactivity, generally considered a security best practice) can be easily implemented using shorter lifetime access tokens and longer lifetime refresh tokens.

Still for some organizations and business use cases this compromise may not be acceptable. One of the commonly considered solutions for this problem is changing the JWT signing key. By changing the JWT signing key, all the JWTs issued against the old key will immediately get revoked, as they cannot be verified using the new key. However, is a situation where the OAuth 2.0 authorization and resource servers are decoupled and distributed, implementing key rotation becomes much harder to the point that it is not worth the candle.

If a perfect solution needs to be sought for this problem, we need to maintain partial token state including the ‘jti’ and ‘exp’ attributes of the JWT bearer access token in the OAuth 2.0 authorization server. When a JWT bearer access token is revoked in the OAuth 2.0 authorization server, the API servers have to be notified with the ‘jti’ that has been revoked and its ‘exp’ value. The API servers can maintain a revocation list of ‘jti’s, until their expiry time is reached. With this we will lose the holy grail of stateless authentication check — but sometimes security requires tradeoffs.

Another place where many developers go wrong is, to use the JWT access token payload to transfer logged-in user’s information other than entitlements. It is highly recommended NOT to do this, since this information could contain sensitive information about the user, and you may inadvertently expose them to attackers. Also you’re probably going to want to cache it in the client-side for future reloads of the application, which would increase the attack surface even further. If the logged-in user’s information is needed for the application, it is recommended to use APIs over HTTPS, such as the OIDC Userinfo API or any other custom Rest API, which are also secured using OAuth 2.0. Another option is to use the OAuth 2.0 token response’s JSON payload to send this additional information to the application. You may use the JWT access token to transfer logged-in user information to the application, ONLY IF the information is deemed non-sensitive.

One more caution when using JWTs is, they might become a disadvantage if you store a lot of data inside it. Since it is sent back and forth with every request, it can slow down the communication, whereas with opaque tokens you are just sending a reference and looking up the data on the server side, which makes the transfer over the network faster.

References

[1] https://tools.ietf.org/html/rfc7519

[2] https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-06

--

--