Access Management 7.3.2

JWT profile for OAuth 2.0 authorization grant

The JWT profile for OAuth 2.0 authorization grant is designed for environments that want to use the REST-based services provided by AM’s OAuth 2.0 framework, but keep their existing authentication services. The trust relationship must be able to be expressed with a JWT bearer token.

Because the trust relationship is already established, this flow does not require the end user’s interaction.

RFC 7523 defines the use of JWT bearer tokens for requesting access tokens and for client authentication.

Read this section for information about requesting access tokens.

To use JWTs for client authentication, refer to JWT profile.

As the authorization server, AM must validate the bearer JWT to issue the access token to the client. To ensure that malicious clients cannot self-sign their own JWTs to acquire tokens, AM requires the token issuer to be pre-registered as a special type of agent.

JWT Bearer Profile for Authorization Grant
Figure 1. JWT Bearer Profile for Authorization Grant
  1. The client requests a JWT from the issuer. The client itself can be the issuer, in which case it will create a JWT for itself before starting the OAuth 2.0 flow.

    Regardless of who signs the JWT, the issuer must be pre-registered in AM as a trusted JWT issuer by completing the steps to configure a trusted JWT issuer. Alternatively, you can configure a scripted trusted JWT issuer that dynamically acquires the details of the issuer from an external system.

  2. The issuer returns a signed JWT to the client; JWTs with message authentication codes (MACs) applied to them are not supported.

    The JWT must contain, at least, the following claims in the payload:

    • aud. Specifies a string or an array of strings that is the intended audience of the JWT. Must be set to, or contain, the authorization server’s token endpoint.

    • exp. Specifies the expiration time of the JWT in Unix time.

      Providing a JWT with an expiry time greater than 30 minutes causes AM to return a JWT expiration time is unreasonable error message.

    • iss. Specifies the unique identifier of the JWT issuer. This could be the client or a third party.

      The identifier must match the issuer field configured in the trusted JWT issuer agent.

    • sub. Specifies the principal who is the subject of the JWT. It must be a string that identifies the resource owner.

      You can configure the trusted JWT issuer agent to check a different claim for the principal. For example, the preferred_username from an ID token.

      In this case, the JWT would contain both the sub and the preferred_username claims.

      For more information, refer to Configure a trusted JWT issuer agent.

      The following is an example of the payload of a basic JWT:

      {
        "aud": [
          "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token"
        ],
        "iss": "https://www.example.com/issuer",
        "exp": 1555530663,
        "sub": "demo"
      }

      For an example of a JWT containing different claims as supported by the trusted JWT issuer agent, refer to Configure a trusted JWT issuer agent.

      For more information about JWTs, refer to the RFC 7523 standard.

  3. The client includes the JWT and a client assertion type in the call to the OAuth 2.0 endpoint in the following parameters:

    • grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer

    • assertion=my-JWT

    For example:

    $ curl \
    --request POST \
    --data "client_id=myClient" \
    --data "client_secret=forgerock" \
    --data "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer" \
    --data "assertion=eyAiYWxnIjogIlJTMjU2IiB9.eyAic3ViIjogImp3…​"
    --data "redirect_uri=http://www.example.com" \
    --data "scope=write" \
    "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token"

    For information about the parameters supported by the /oauth2/access_token endpoint, refer to /oauth2/access_token.

    For information about client authentication methods, refer to OAuth 2.0 client authentication.

  4. AM validates the JWT following the guidance specified in section 3 of the RFC7523 and also performs the following additional checks:

    1. Decodes the payload and compares the value of the iss claim with the value of the JWT Issuer field in the list of trusted JWT issuer agents.

    2. Validates the JWT signature either with the keys exposed on the trusted issue agent’s JWK URI, or with the keys configured in the JWK Set field of the agent.

    If AM cannot validate the JWT it will return an error, such as JWT signature is invalid.

  5. The authorization server issues an access token to the client.

  6. The client requests access to the protected resources from the resource server.

  7. The resource server contacts the authorization server to validate the access token.

  8. The authorization server validates the token and responds to the resource server.

  9. If the token is valid, the resource server allows the client to access the protected resources.

Configure a trusted JWT issuer agent

Perform the steps in this procedure to configure a trusted JWT issuer agent:

  1. In the AM admin UI, go to Realms > Realm Name > Applications > OAuth 2.0 > Trusted JWT Issuer.

  2. Add a new trusted JWT issuer agent.

  3. Complete the following fields to create the agent:

    • In the Agent ID field, give the trusted JWT issuer agent a name.

      For example, myJWTAgent.

    • In the JWT Issuer field, provide the URI of the JWT issuer.

      This URI must match the value of the issuer (the iss claim) in the JWTs.

    • Select Create.

      You are presented with a screen with additional information regarding the agent.

  4. Review the trusted JWT issuer agent information.

    You must, configure either the JWKs URI or the JWK Set fields, as follows:

    • JWKs URI: specifies a URI in the JWT issuer that exposes the verification keys that AM will use to validate the JWT signature. For example, http://www.example.com/issuer/jwk_uri.

      If you configure this field, ensure the following properties are configured with sensible values for your environment:

      • JWKs URI content cache timeout in ms

      • JWKs URI content cache miss cache time

    • JWK set: Specifies a JWK set containing the verification keys to validate the JWT signature.

      The following is an example of an elliptic curve JWK set:

      {
        "keys": [{
           "kty": "EC",
           "crv": "P-256",
           "x": "i-rdOmi5lC3pn3y5sTgYiLLFVFY7XxDLinWneHEaAXA",
           "y": "mxmqqauiq44INgyyPP2vATt3IkDL_6W5CAcfAMSZl8k",
           "kid": "signing_key",
           "x5c": [
               "MIIBSjCB76ADAgEC.....955PByPrflZkQOC/g==" ]
        }]
      }

      For more information about the contents of the JWK set, refer to the JSON Web Key (JWK) specification.

    You can store more than one key in the JWK set. However, it is easier to implement key rotation exposing the validation keys on the URI instead.

  5. Configure the following values to suit your environment:

    • Consented Scopes Claim. The name of a JWT claim that indicates which scopes the resource owner consented to. The claim in the JWT can contain either a JSON array or a space-separated allowlist of scopes that the resource owner has consented to.

      For example, if you configure the scp claim name in this field and the JWT contains the claim "scp":"read", but you request both the read and write scopes, AM will only grant the read scope.

      Leave this field blank to allow any scope.

      The following are example JWTs containing a claim that specifies scopes:

      {
         "aud": [
             "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token"
         ],
         "iss": "https://www.example.com/issuer",
         "exp": 1555530663,
         "sub": "demo",
         "scope": ["read", "write"]
      }

      In this case, the scope claim is a JSON array of scopes.

      {
         "aud": [
             "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token"
         ],
         "iss": "https://www.example.com/issuer",
         "exp": 1555530663,
         "sub": "demo",
         "scp": "read write"
      }

      In this case, the scp claim is a space-separated list of scopes.

    • Resource Owner Identity Claim. Claim in the JWT that identifies the resource owner in AM. By default, the sub claim.

      Note that even if you configure the trusted JWT issuer agent to verify a different claim, such as the preferred_username claim, the sub claim must still exist in the JWT.

    • Allowed Subjects. List of subjects this JWT issuer is allowed to provide consent for.

      For example, if you configure the demo user in this field but the JWT subject value is demo2, AM will not grant the access token.

      Leave it blank to provide consent to any user.

  6. Save your changes.

    The trusted JWT issuer agent is ready for use.

Configure a scripted JWT issuer

Use a script to configure a trusted JWT issuer so that AM can dynamically retrieve the details of an issuer during the JWT profile for authorization grant.

This lets a scripted JWT issuer represent an existing entity in an external system.

For example, if an administrative user creates a service account in IDM, a reference to that service account is provided in the JWT so that the identity can be retrieved and verified by the scripted JWT issuer. The issuer then sends the signed JWT to AM to obtain an access token using the JWT profile for authorization grant.

To configure a scripted JWT issuer, follow these steps:

The examples used in these steps assume an administrator has already created a managed object representing a service account in IDM.

  1. Create a trusted JWT issuer script.

    1. In the AM admin UI, go to Realms > Realm Name > Scripts and click New Script.

    2. Enter a unique name for your script and select OAuth2 Trusted JWT Issuer from the Script Type drop-down list. Click Create.

    3. Write a script that returns an org.forgerock.oauth2.core.TrustedJwtIssuerConfig object.

      The TrustedJwtIssuerConfig object should contain the details of the issuer, the JWK set, and where to locate the subject and scope in the original JWT.

      Note that scripted JWT issuer scripts can currently only access secrets with IDs that have a prefix of scripted.jwtissuer.

      Example script
      ( function() {
          var fr = JavaImporter(
              org.forgerock.oauth2.core.TrustedJwtIssuerConfig,
              java.util.HashSet
          );
          var iss = idRepository.getIdentity(issuer);
          if (iss == null) {
              logger.message('No issuer found for: ' + issuer);
              return null;
          }
          logger.message('Found for: ' + iss);
          var jwksAttrs = iss.getAttributeValues('fr-attr-jwks');
          if (!jwksAttrs || jwksAttrs.length === 0) {
              logger.message('No jwk attributes in issuer');
              return null;
          }
          var jwkSet = jwksAttrs[0];
          if (!jwkSet) {
              logger.message('No jwk set in issuer');
              return null;
          }
          var config = new fr.TrustedJwtIssuerConfig(
              issuer,
              'sub',
              'scope',
              new fr.HashSet([issuer]),
              jwkSet,
              null, null, null
          );
          return config;
      }());
    4. Click Save Changes.

  2. Configure AM to use the script.

    1. In the AM admin UI, go to Realms > Realm Name > Services > OAuth2 Provider > Secondary Configurations to configure the OAuth 2.0 provider for the realm.

    2. Click Add a Secondary Configuration.

    3. Provide a name, select the name of the script you created from the drop-down list, and click Create.

The scripted JWT issuer is ready for use.

To reference the external entity to be retrieved by the scripted JWT issuer, the provided JWT must contain iss and sub claims set to the entity identifier.

For example, this JWT references a service account with the identifier e0c47854-b0cf-4512-9aec-1ae6ada6d521:

{
    "iss": "e0c47854-b0cf-4512-9aec-1ae6ada6d521",
    "sub": "e0c47854-b0cf-4512-9aec-1ae6ada6d521",
    "aud": "https://:443/am/oauth2/access_token",
    "exp": 1555530663,
    "jti": "pwgb48EzRrnN4VaI55H+0g=="
}