Using JWT OAuth tokens

You're viewing Apigee X documentation.
View Apigee Edge documentation.

This topic explains how to generate, verify, and refresh JWT access tokens using the OAuthV2 policy.

Introduction

The JWT operations allow the OAuthV2 policy to generate, verify, and refresh access tokens that conform to IETF RFC 9068, a standard that describes how to issue access tokens in JWT format. JWTs are commonly used to share claims or assertions between connected applications. Issuing OAuthV2 access tokens in JWT format is an alternative to issuing opaque access tokens.

When configured for JWT, the OAuthV2 policy generates and returns a Base64-encoded JWT that consists of a header, a payload, and a signature separated by dots. For example:

Figure 1: JWT serialized format consisting of header, payload, and signature separated by dots.

The encoded contents of these elements depend on how you configure the OAuthV2 policy. In the policy, you specify such parameters as the signing algorithm and payload elements like subject and name. For example, the header might decode as {"alg":"HS256","typ":"at+JWT"}, and the payload might decode as: {"sub":"ABC1234567","iat":1516239022}.

The header specifies a typ claim (always "at+JWT) and the alg claim, indicating the algorithm used to sign the JWT. Apigee supports RSA and HMAC algorithms: RS(256,384,512) and HS(256,384,512).

Payload

The payload consists of claims about the entity. Some claims must be provided in the policy configuration, while others are automatically generated by the Apigee runtime. The policy supports the following claims:

Claim Description Provided by
iss The token issuer. This value is set as follows: (http|https)://{domain-name-for-proxy}/{proxy-basePath}. For example: https://api.mycompany.com/auth/v2. Apigee
sub Either the Client ID or the ID of the resource owner (in the case of password or authorization grant types). If the appEndUserId parameter is provided in the request, that value is used as the resource owner ID. You can control where this value is set using the <AppEndUser> element of the OAuthV2 policy. API developer
jti A unique ID, represented as a UUID-backed random string to uniquely identify the token. Apigee
exp The expiry time, in other words the time after which the token must be considered invalid. The value is expressed in epoch time (in seconds). Apigee
iat The issued-at time, the time the token was created. The value is expressed in epoch time (in seconds). Apigee
client_id The unique identifier of the client application. API developer
scope The OAuth scope assigned to the token. See also Working with OAuth scopes. API developer

Signature

The signature is generated using the encoded header, payload, secret/private key, and the algorithm. The signature is used to ensure that the contents of the token have not been tampered with.

For more information about JWT-format OAuth 2.0 access tokens, see the IETF RFC 9068: JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens.

Prerequisites

This document assumes that you understand how to generate and verify OAuthV2 access tokens using the OAuthV2 policy. Whether you use the JWT operations or the traditional operations that create opaque string tokens, the basic use of the OAuthV2 policy is the same. You can use JWT access tokens with all of the supported OAuthV2 grant types. See also Introduction to OAuth 2.0.

Generating

Use the GenerateJWTAccessToken and GenerateJWTAccessTokenImplicitGrant operations to generate a JWT access token with the OAuthV2 policy. These operations are similar to the policy's traditional GenerateAccessToken and GenerateAccessTokenImplicitGrant operations. The main difference is that the JWT operation returns a JWT-formatted access token instead of an opaque string token. See also Requesting access tokens and authorization codes.

The following examples show how to use these operations in the OAuthV2 policy. The examples use the client_credentials grant type; however, you can use any of the supported grant types with these operations.

Generating a JWT-format token signed with an HMAC algorithm

Specify the <Algorithm> element with one of the HMAC (HS256/HS384/HS512) algorithms. Also provide the <SecretKey>. The following example shows a policy configured to generate a JWT signed with the HS512 algorithm, using the specified secret key.

<OAuthV2 name="generate-policy">
  <Operation>GenerateJWTAccessToken</Operation>
  <SupportedGrantTypes>
    <GrantType>client_credentials</GrantType>
  </SupportedGrantTypes>
  <GenerateResponse enabled="true"/>
  <Algorithm>HS512</Algorithm>
  <SecretKey>
    <Value ref="private.mysecretkey"/>
  </SecretKey>
  <ExpiresIn ref="kvm.oauth.expires_in">3600000</ExpiresIn>
</OAuthV2>

Generating a JWT-format token signed with an RSA algorithm

Specify one of the RSA algorithms (one of RS256/RS384/RS512) in the <Algorithm> element, and provide the private key in the <PrivateKey> element. The following example shows a policy configured to generate a JWT signed with an RSA private key using the RS256 algorithm.

<OAuthV2 name="generate-policy">
  <Operation>GenerateJWTAccessToken</Operation>
  <SupportedGrantTypes>
    <GrantType>client_credentials</GrantType>
  </SupportedGrantTypes>
  <GenerateResponse enabled="true"/>
  <Algorithm>RS256</Algorithm>
  <PrivateKey>
    <Value ref="private.rsa-privatekey-1"/>
  </PrivateKey>
  <ExpiresIn ref="kvm.oauth.expires_in">3600000</ExpiresIn>
</OAuthV2>

Generating a JWT-format token with the implicit grant type

The GenerateJWTAccessTokenImplicitGrant operation generates a JWT access token using the implicit grant type. It automatically gives the token the implicit grant type; therefore, the <SupportedGrantTypes> element is not required. Because it is a JWT, the <Algorithm> element is required. The following example shows the use of the RS256 algorithm. Because of that, the <PrivateKey> element is required. See also Requesting the implicit grant type.

<OAuthV2 name="generate-policy">
  <Operation>GenerateJWTAccessTokenImplicitGrant</Operation>
  <GenerateResponse enabled="true"/>
  <Algorithm>RS256</Algorithm>
  <PrivateKey>
    <Value ref="private.rsa-privatekey-1"/>
  </PrivateKey>
  <ExpiresIn ref="kvm.oauth.expires_in">3600000</ExpiresIn>
</OAuthV2>

Verifying

Use the VerifyJWTAccessToken operation to verify a JWT access token with the OAuthV2 policy. This operation is similar to the VerifyAccessToken operation; the difference is that VerifyJWTAccessToken applies to tokens in JWT format, while VerifyAccessToken applies to opaque tokens.

Verifying a JWT access token signed with an HMAC algorithm

The following example shows how to configure the OAuthV2 policy to verify a JWT token that was signed with the HS512 algorithm. When using the VerifyJWTAccessToken operation with an HMAC algorithm, the policy configuration must use the <SecretKey> element to specify the secret key that was used to sign the JWT.

<OAuthV2 name="OAuthV2-verify-jwt">
  <Operation>VerifyJWTAccessToken</Operation>
  <Algorithm>HS512</Algorithm>
  <SecretKey>
    <Value ref="private.mysecretkey"/>
  </SecretKey>
</OAuthV2>

Verifying a JWT access token signed with an RSA algorithm

The following example shows how to configure the OAuthV2 policy to verify a JWT token that was signed with the RS512 algorithm. When using the VerifyJWTAccessToken operation with an RSA algorithm, the policy configuration must use the <PublicKey> element to specify the public key that corresponds to the private key that was used to sign the JWT.

<OAuthV2 name="OAuthV2-verify-jwt">
  <Operation>VerifyJWTAccessToken</Operation>
  <Algorithm>RS512</Algorithm>
  <PublicKey>
    <Value ref="propertyset.non-secrets.rsa-publickey-1"/>
  </PublicKey>
</OAuthV2>

Refreshing

Use the RefreshJWTAccessToken operation to refresh a JWT access token. This operation is similar to the policy's traditional RefreshAccessToken operation. See also Refreshing an access token.

Refreshing an HMAC-signed access token

The following policy sample illustrates how to configure the OAuthV2 policy to refresh a JWT token that was signed with an HMAC algorithm. The <SecretKey> and <Algorithm> elements are required in this case.

The response of the refresh operation is similar to the response of a newly generated token. The refresh operation generates a new JWT token with an updated expiry time, keeping other claims the same.

<OAuthV2 name="RefreshAccessToken">
    <Operation>RefreshJWTAccessToken</Operation>
    <GenerateResponse enabled="true"/>
    <Algorithm>HS512</Algorithm>
    <SecretKey>
      <Value ref="private.mysecretkey"/>
    </SecretKey>
    <RefreshTokenExpiresIn ref="kvm.oauth.expires_in">3600000</RefreshTokenExpiresIn>
</OAuthV2>

Refreshing an RSA-signed JWT access token

The following policy sample illustrates how to configure the OAuthV2 policy to refresh a JWT token that was signed with an RSA algorithm. See also Refreshing an access token.

<OAuthV2 name="RefreshAccessToken">
    <Operation>RefreshJWTAccessToken</Operation>
    <GenerateResponse enabled="true"/>
    <Algorithm>RS256</Algorithm>
    <PrivateKey>
      <Value ref="private.rsa-privatekey-1"/>
    </PrivateKey>
    <RefreshTokenExpiresIn ref="kvm.oauth.expires_in">3600000</RefreshTokenExpiresIn>
</OAuthV2>

Sample token response

For both generate and refresh operations, the response body looks similar to the following example. Note that the access_token is represented as a serialized JWT, and the refresh token is a traditional opaque token.

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImF0K0pXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
  "token_type": "Bearer",
  "developer.email": "developer@example.org",
  "token_type": "Bearer",
  "issued_at": "1658352381404",
  "expires_in": 1799,
  "refresh_token": "rVSmm3QaNa0xBVFbUISz1NZI15akvgLJ",
  "refresh_token_issued_at": "1658352381404",
  "refresh_token_expires_in": 86399,
  "refresh_token_status": "Approved",
  "refresh_count": "0",
  "organization_name": "cerruti",
  "api_product_list_json": [ "TestingProduct" ]
}

Summary of required policy elements

The following table describes JWT-specific elements used in the preceding examples:

Element Type Notes
Algorithm Static value Specifies the algorithm used to sign the token.
SecretKey Referenced value Provides the secret key used to verify or sign tokens with an HMAC algorithm: HS (256/384/512).
PrivateKey Referenced value Provides the private key used to generate the token. Use only when the algorithm is an RSA algorithm: RS (256/384/512).
PublicKey Referenced value Provides the public key used to verify the token. Use only when the algorithm is an RSA algorithm: RS (256/384/512).

Unsupported policy elements

The following OAuthV2 policy elements are not supported with JWT token configurations:

Element Notes
ExternalAuthorization When generating a JWT access token, the OAuthV2 policy will validate the client ID and Secret.
ExternalAccessToken When generating a JWT access token, the token will be signed by Apigee and the claims will be provided by Apigee either by default or through policy configuration. The policy does support ExternalRefreshToken, which can aid in migration use cases.
RFCCompliantRequestResponse The generation and refresh of JWT access tokens is RFC compliant by default.

Usage notes

  • Encrypted JWTs are not supported.
  • In addition to a JWT access token, the policy response also includes an opaque refresh token for grant types where refresh tokens are supported. Only the password and auth code grant types support refresh tokens.
  • Include the Authorization header in requests sent to the proxy containing the OAuthV2 policy.
  • You cannot revoke a JWT access token. The generated JWT token will remain valid until it expires. You can revoke a refresh token that is associated to a JWT access token.
  • Generation, verification, and refresh of access tokens must be handled by Apigee. Although an external application or gateway that has access to the public or secret key can decode the contents of the JWT, the external application does not have information about the API products that are identified by the client_id claim, and therefore cannot play a role in API proxy authorization.