API Authentication
Jul 11, 2025

JSON Web Token profile for OAuth 2.0 access tokens (RFC 9068)

Hrishikesh Premkumar
Founding Architect

OAuth 2.0 has become the standard authorization framework for securing APIs. At the heart of OAuth 2.0 are access tokens, credentials that allow an application to access protected resources on behalf of a user or itself.

Typically, access tokens indicate that the client app has been granted permissions to access specific resources, such as user data.

OAuth 2.0 itself doesn't specify a format for access tokens, leading many implementations to adopt proprietary token formats. RFC 9068 addresses this by defining a standardized JSON Web Token (JWT) profile for OAuth 2.0 access tokens, enabling better interoperability and security.

Why JWT access tokens?

Originally, OAuth 2.0 tokens were opaque strings requiring additional calls to authorization servers for validation (token introspection). JWT access tokens eliminate this overhead by embedding structured token information directly into a cryptographically signed JWT, allowing resource servers to independently validate tokens.

For developers, RFC 9068 simplifies token validation by eliminating additional round trips to authorization servers for token introspection.

Practical example: E-commerce API

Consider an e-commerce mobile app that allows customers to view their profile information, manage orders, and check their purchase history. To access these protected resources, the app needs an access token issued by an authorization server after successful user authentication. This access token authorizes the app to interact securely with the e-commerce resource server at `https://api.ecommerce.com`.

Structure of a JWT access token

JWT access tokens contain standardized claims within their payload. RFC 9068 specifies the following required claims:

iss (Issuer): URL identifying the authorization server.
sub (Subject): Identifier for the customer or client application.
aud (Audience): Identifies the intended resource server.
exp (Expiration): Indicates when the token becomes invalid.
iat (Issued At): Timestamp of token issuance.
jti (JWT ID): Unique identifier to prevent replay attacks.
client_id: Identifier of the OAuth client application.

Here's a sample JWT access token for our e-commerce app:

{ "iss": "https://auth.ecommerce.com/", "sub": "customer-789", "aud": "https://api.ecommerce.com/", "exp": 1735682400, "iat": 1704146400, "jti": "unique-token-id-12345", "client_id": "ecommerce-mobile-app", "scope": "profile orders" }

Requesting a JWT access token

The mobile app explicitly requests JWT access tokens by including a resource parameter and scopes within the authorization request. Here’s an example request:

GET /authorize?response_type=code &client_id=ecommerce-mobile-app &scope=profile%20orders &redirect_uri=ecommerceapp://callback &resource=https://api.ecommerce.com/ HTTP/1.1 Host: auth.ecommerce.com

The resulting JWT token issued upon successful authorization looks like this:

Header:

{ "typ": "at+jwt", "alg": "RS256", "kid": "ecom-key-001" }
  • kid (Key ID): Identifies the specific key used by the authorization server to sign the token.
{ "iss": "https://auth.ecommerce.com/", "sub": "customer-789", "aud": "https://api.ecommerce.com/", "exp": 1735682400, "iat": 1704146400, "jti": "unique-token-id-12345", "client_id": "ecommerce-mobile-app", "scope": "profile orders" }

Validating JWT access tokens

The e-commerce resource server validates JWT access tokens independently by:

  1. Verifying the token's signature using public keys advertised by the authorization server.
  2. Ensuring token claims (iss, aud, exp) match expected values.
  3. Checking that the token type (typ) header explicitly states at+jwt.

Here's a simplified Node.js validation logic example for our e-commerce API:

const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); const client = jwksClient({ jwksUri: 'https://auth.ecommerce.com/.well-known/jwks.json' }); function getKey(header, callback){ client.getSigningKey(header.kid, (err, key) => { callback(null, key.publicKey || key.rsaPublicKey); }); } jwt.verify(token, getKey, { audience: 'https://api.ecommerce.com/', issuer: 'https://auth.ecommerce.com/', algorithms: ['RS256'] }, (err, decoded) => { if (err) { // Handle invalid token console.error("Token validation failed", err); } else { // Token is valid console.log("Token valid:", decoded); } });

💡 Pro tip: Validate the aud claim properly to avoid cross-resource confusion or unauthorized access.

Security considerations

RFC 9068 mandates specific practices to ensure security:

  • JWT access tokens MUST NOT use the none algorithm.
  • Tokens should clearly distinguish themselves from OpenID Connect ID tokens (typ header set to at+jwt).
  • Authorization servers should use distinct identifiers (aud) for different resources to avoid cross-JWT confusion.

Privacy considerations

As JWT tokens carry user information, the e-commerce authorization server must consider privacy implications:

  • Avoid including unnecessary sensitive information.
  • Encrypt tokens or sensitive claims when necessary.
  • Use unique subject (sub) identifiers per resource to prevent tracking across resource servers.

Developer best practices

  • Regularly rotate signing keys and publish them via JWKS endpoint.
  • Ensure clock synchronization (for claim validation like exp and iat).
  • Clearly document the claims used within your JWT tokens for consumer APIs.

Edge cases to watch out for

  • Token reuse across multiple resources causing JWT confusion.
  • Handling token expiration gracefully on resource servers.
  • Ensuring public key availability for JWT signature validation.

When NOT to use this RFC

This RFC can be avoided in highly sensitive environments requiring opaque tokens and frequent token introspection. It’s also best avoided if there is limited support for cryptographic operations.

Conclusion

RFC 9068 brings standardization to JWT access tokens, facilitating secure, efficient, and interoperable OAuth 2.0 implementations. This standardized profile reduces complexity and enhances security by clearly defining token structure, issuance, and validation processes.

By adopting RFC 9068, developers and organizations can ensure their OAuth 2.0 implementations are more secure.

No items found.
On this page
Share this article
Secure your APIs with OAuth

Acquire enterprise customers with zero upfront cost

Every feature unlocked. No hidden fees.
Start Free
$0
/ month
1 FREE SSO/SCIM connection each
1000 Monthly active users
25 Monthly active organizations
Passwordless auth
API auth: 1000 M2M tokens
MCP auth: 1000 M2M tokens
API Authentication

JSON Web Token profile for OAuth 2.0 access tokens (RFC 9068)

Hrishikesh Premkumar

OAuth 2.0 has become the standard authorization framework for securing APIs. At the heart of OAuth 2.0 are access tokens, credentials that allow an application to access protected resources on behalf of a user or itself.

Typically, access tokens indicate that the client app has been granted permissions to access specific resources, such as user data.

OAuth 2.0 itself doesn't specify a format for access tokens, leading many implementations to adopt proprietary token formats. RFC 9068 addresses this by defining a standardized JSON Web Token (JWT) profile for OAuth 2.0 access tokens, enabling better interoperability and security.

Why JWT access tokens?

Originally, OAuth 2.0 tokens were opaque strings requiring additional calls to authorization servers for validation (token introspection). JWT access tokens eliminate this overhead by embedding structured token information directly into a cryptographically signed JWT, allowing resource servers to independently validate tokens.

For developers, RFC 9068 simplifies token validation by eliminating additional round trips to authorization servers for token introspection.

Practical example: E-commerce API

Consider an e-commerce mobile app that allows customers to view their profile information, manage orders, and check their purchase history. To access these protected resources, the app needs an access token issued by an authorization server after successful user authentication. This access token authorizes the app to interact securely with the e-commerce resource server at `https://api.ecommerce.com`.

Structure of a JWT access token

JWT access tokens contain standardized claims within their payload. RFC 9068 specifies the following required claims:

iss (Issuer): URL identifying the authorization server.
sub (Subject): Identifier for the customer or client application.
aud (Audience): Identifies the intended resource server.
exp (Expiration): Indicates when the token becomes invalid.
iat (Issued At): Timestamp of token issuance.
jti (JWT ID): Unique identifier to prevent replay attacks.
client_id: Identifier of the OAuth client application.

Here's a sample JWT access token for our e-commerce app:

{ "iss": "https://auth.ecommerce.com/", "sub": "customer-789", "aud": "https://api.ecommerce.com/", "exp": 1735682400, "iat": 1704146400, "jti": "unique-token-id-12345", "client_id": "ecommerce-mobile-app", "scope": "profile orders" }

Requesting a JWT access token

The mobile app explicitly requests JWT access tokens by including a resource parameter and scopes within the authorization request. Here’s an example request:

GET /authorize?response_type=code &client_id=ecommerce-mobile-app &scope=profile%20orders &redirect_uri=ecommerceapp://callback &resource=https://api.ecommerce.com/ HTTP/1.1 Host: auth.ecommerce.com

The resulting JWT token issued upon successful authorization looks like this:

Header:

{ "typ": "at+jwt", "alg": "RS256", "kid": "ecom-key-001" }
  • kid (Key ID): Identifies the specific key used by the authorization server to sign the token.
{ "iss": "https://auth.ecommerce.com/", "sub": "customer-789", "aud": "https://api.ecommerce.com/", "exp": 1735682400, "iat": 1704146400, "jti": "unique-token-id-12345", "client_id": "ecommerce-mobile-app", "scope": "profile orders" }

Validating JWT access tokens

The e-commerce resource server validates JWT access tokens independently by:

  1. Verifying the token's signature using public keys advertised by the authorization server.
  2. Ensuring token claims (iss, aud, exp) match expected values.
  3. Checking that the token type (typ) header explicitly states at+jwt.

Here's a simplified Node.js validation logic example for our e-commerce API:

const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); const client = jwksClient({ jwksUri: 'https://auth.ecommerce.com/.well-known/jwks.json' }); function getKey(header, callback){ client.getSigningKey(header.kid, (err, key) => { callback(null, key.publicKey || key.rsaPublicKey); }); } jwt.verify(token, getKey, { audience: 'https://api.ecommerce.com/', issuer: 'https://auth.ecommerce.com/', algorithms: ['RS256'] }, (err, decoded) => { if (err) { // Handle invalid token console.error("Token validation failed", err); } else { // Token is valid console.log("Token valid:", decoded); } });

💡 Pro tip: Validate the aud claim properly to avoid cross-resource confusion or unauthorized access.

Security considerations

RFC 9068 mandates specific practices to ensure security:

  • JWT access tokens MUST NOT use the none algorithm.
  • Tokens should clearly distinguish themselves from OpenID Connect ID tokens (typ header set to at+jwt).
  • Authorization servers should use distinct identifiers (aud) for different resources to avoid cross-JWT confusion.

Privacy considerations

As JWT tokens carry user information, the e-commerce authorization server must consider privacy implications:

  • Avoid including unnecessary sensitive information.
  • Encrypt tokens or sensitive claims when necessary.
  • Use unique subject (sub) identifiers per resource to prevent tracking across resource servers.

Developer best practices

  • Regularly rotate signing keys and publish them via JWKS endpoint.
  • Ensure clock synchronization (for claim validation like exp and iat).
  • Clearly document the claims used within your JWT tokens for consumer APIs.

Edge cases to watch out for

  • Token reuse across multiple resources causing JWT confusion.
  • Handling token expiration gracefully on resource servers.
  • Ensuring public key availability for JWT signature validation.

When NOT to use this RFC

This RFC can be avoided in highly sensitive environments requiring opaque tokens and frequent token introspection. It’s also best avoided if there is limited support for cryptographic operations.

Conclusion

RFC 9068 brings standardization to JWT access tokens, facilitating secure, efficient, and interoperable OAuth 2.0 implementations. This standardized profile reduces complexity and enhances security by clearly defining token structure, issuance, and validation processes.

By adopting RFC 9068, developers and organizations can ensure their OAuth 2.0 implementations are more secure.

No items found.
Ship Enterprise Auth in days