So far, we have talked about the technical properties of JWT tokens. However, using JWTs in practice requires careful consideration of the security properties of a JWT.
In most cases, JWTs are used in the form of bearer tokens. A bearer token is a token that can be used by anyone who possesses it. Consequently, obtaining a JWT suffices for an attacker to start abusing the privileges associated with that token. In this final section, we will briefly highlight a few use cases.
JWTs in OpenID Connect
We have mentioned the use of JWT in OpenID Connect before. The provider issues an identity token to the client. That identity token contains information about the user's authentication with the provider. The identity token is a JWT token, signed with the provider's private key.
OpenID Connect went through great lengths to improve the security properties of the identity token. For example, the protocol mandates the use of the "exp," "iss" and "aud" claims. Additionally, the token includes a nonce to prevent replay attacks. Because of these requirements, abusing a stolen identity token becomes hard or even impossible.
JWTs as OAuth 2.0 access tokens
An OAuth 2.0 access token is another good use case of a JWT. Such an access token gives a client application access to a protected resource, such as an API. OAuth 2.0 access tokens come in two flavors: reference tokens and self-contained tokens.
A reference token points to server-side metadata, kept by the authorization server. A reference token functions as an identifier, much like a traditional session identifier.
A self-contained token comes in the form of a JWT. It contains all the metadata as the payload. To protect the data, the issuer signs the token using a private key.
Traditional OAuth 2.0 tokens are bearer tokens. If one becomes compromised, it can be used without restrictions by whoever possesses it. A compromised reference token can be revoked by the authorization server. For self-contained tokens, revocation is a lot trickier.
Therefore, it is strongly recommended to keep the lifetime of access tokens as short as possible. Token lifetimes of minutes or hours are quite common. Lifetimes of days or months are not recommended. If possible, short-lived access tokens should be combined with refresh tokens to improve security.
Additionally, modern additions to the specification address the bearer token properties by introducing proof-of-possession mechanisms.
JWTs as session objects
Protocols such as OpenID Connect and OAuth 2.0 actively try to address the weaknesses of JWTs. Unfortunately, we also observe many applications that incorporate JWTs into their architecture, without taking these precautions into account.
A concrete example is an application using JWTs to store authorization state on the client. This enables the use of a stateless backend, which makes deployment significantly easier.
However, such a client-side token is a bearer token. Not having a short lifetime or revocation mechanism in place makes such a scenario extremely vulnerable.
Diving into all these details would take us too far for this article. If you want to know more about these issues, we recommend reading “Stop using JWT for sessions” and “Reference Tokens and Introspection.”