JWK-Based Proof-of-Possession

To implement JWK-based proof-of-possession, the client includes a JWK when making a request to the authorization server for an access token as per Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs) spec. The JWK consists of the public key of a key pair generated by the client.

When the client presents the access token to a resource server, the resource server can cryptographically confirm proof-of-possession of the token by using the associated JWK to form a challenge-response interaction with the client.

OAuth 2.0 JWK-Based Proof-of-Possession Flow
OAuth 2.0 JWK-Based Proof-of-Possession Flow

The steps in the diagram are described below:

  1. The client requests an access token using any of the OAuth 2.0 grant flows, and includes a JWK in the request.

    This JWK consists of the public key of a key pair generated by the client.

  2. The authorization server returns the access token to the client:

    • If the authorization server is configured for CTS-based OAuth 2.0, it stores the JWK with the access token in the CTS token store and provides the client with the access token ID.

    • If the authorization server is configured for client-based OAuth 2.0, the access token is a JWT that contains the JWK embedded in it.

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

  4. The resource server recovers the JWK associated with the access token:

    • If the resource server receives an access token ID (CTS-based OAuth 2.0), it introspects the access token ID to recover the JWK from the authorization's server CTS token store.

    • If the resource server receives an access token JWT (client-based OAuth 2.0), it already has access to the JWK, which is embedded.

  5. The resource server creates a challenge using the JWK. Usually, these challenges are messages or nonces that have been encrypted with the JWK.

  6. The resource server sends the challenge to the client.

  7. The client solves the challenge using the private key of its key pair.

  8. The client sends the response to the challenge to the resource server.

  9. The resource server validates the response and allows access to the resource.

To use JWK-based proof-of-possession by associating a JWK with an OAuth 2.0 access token, perform the following steps:

To Obtain an Access Token Using JWK-Based Proof-of-Possession
  1. Generate a JSON web key pair for the OAuth 2.0 client.

    AM supports both RSA and elliptic curve (EC) key types. For testing purposes, you can use an online JSON web key generator, such as https://mkjwk.org/, to generate a key pair in JWK format. Be sure to store the full key pair, including the private key, in a secure location that is accessible by your OAuth 2.0 client.

    Your OAuth 2.0 client should never reveal the private key.

  2. Represent the public key of the key pair in JWK format. For example:

    {
      "jwk":{
        "alg":"RS256",
        "e":"AQAB",
        "n":"xea7Tb7rbQ4ZrHNKrg...QFXtJ-didSTtXWCWU1Qrcj0hnDjvkuUFWoSQ_7Q",
        "kty":"RSA",
        "use":"enc",
        "kid":"myPublicJSONWebKey"
      }
    }

    Note

    The jwe and jku formats are not supported, the public key must be represented in jwk format.

  3. Base64-encode the JWK. For example:

    ew0KICAgICJKV0siOiB7DQogICAgICAgICJhbGciOiAiUlMyNTYiLA0KICAgICAgICAiZSI6IC
    JBUUFCIiwNDQogICAgICAgICJraWQiOiAibXlQdWJsaWNKU09OV2ViS2V5Ig0KICAgIH0NCn0=
  4. The client includes the base64-encoded JWK as the value of the cnf_key parameter in the request to the authorization server for an access token.

    For example, in the Client Credentials grant, the client makes a POST call to the authorization server's token endpoint specifying, at least, the following parameters:

    • grant_type=client_credentials

    • cnf_key=your_base64-encoded-JWK

    Confidential clients can authenticate to the OAuth 2.0 endpoints in several ways. This example uses the following form parameters:

    • client_id=your_client_id

    • client_secret=your_client_secret

    For more information, see OAuth 2.0 Client Authentication.

    For example:

    $ curl \
    --request POST \
    --data "grant_type=client_credentials"\
    --data "client_id=myClient" \
    --data "client_secret=forgerock" \
    --data "cnf_key=ew0KICAgICJKV0siOiB7DQogICAgICAgICJhb
                 GciOiAiUlMyNTYiLA0KICAgICAgICAiZSI6IC
                 JBUUFCIiwNDQogICAgICAgICJraWQiOiAibXl
                 QdWJsaWNKU09OV2ViS2V5Ig0KICAgIH0NCn0=" \
    "https://openam.example.com:8443/openam/oauth2/realms/root/access_token"

    For more information about how to use the different OAuth 2.0 grant flows, see OAuth 2.0 Grant Flows.

    The authorization server returns the access token:

    • If the authorization server is configured to use CTS-based OAuth 2.0 tokens, the response will include an access token ID in the access_token property, which identifies the access token data stored on the server. For example:

      {
          "access_token":"f08f1fcf-3ecb-4120-820d-fb71e3f51c04",
          "scope":"profile",
          "token_type":"Bearer",
          "expires_in":3599
      }
    • If the authorization server is configured to use client-based OAuth 2.0 tokens, the response will be a JSON web token in the access_token, which has the JWK embedded within. The following example has shortened the access token for display purposes:

      {
          "access_token": "eyJ0eXAiOiJKV1QiLCHi51zbE3t...zc2NjI3NDgsInNjb3zUOCVKCX0Se0",
          "scope": "profile",
          "token_type": "Bearer",
          "expires_in": 3599
      }
  5. The client now requests access to the protected resources from the resource server.

    If CTS-based OAuth 2.0 tokens are enabled, the resource server can make a POST request to the /oauth2/introspect endpoint to acquire the public key. The public key from the original JWK is returned in the cnf element:

    $ curl \
    --request POST \
    --header "Authorization: Basic bXlDbGllbnQ6Zm9yZ2Vyb2Nr" \
    --data "token=f08f1fcf-3ecb-4120-820d-fb71e3f51c04" \
    "https://openam.example.com:8443/openam/oauth2/realms/root/introspect"
    {
        "active": true,
        "scope": "profile",
        "client_id": "myClient",
        "user_id": "myClient",
        "token_type": "access_token",
        "exp": 1477666348,
        "sub": "(age!myClient)",
        "subname": "myClient",
        "iss": "https://openam.example.com:8443/openam/oauth2/realms/root",
        "cnf": {
            "jwk": {
                "alg": "RS256",
                "e": "AQAB",
                "n": "xea7Tb7rbQ4ZrHNKrg...QFXtJ-didSTtXWCWU1Qrcj0hnDjvkuUFWoSQ_7Q",
                "kty": "RSA",
                "use": "enc",
                "kid": "myPublicJSONWebKey"
            },
        "auth_level": 0
        }
    }
  6. The resource server should now use the public key to cryptographically confirm proof-of-possession of the token by the presenter; for example, with a challenge-response interaction.

    Successful completion of the challenge-response means that the client must possess the private key that matches the public key presented in the original request, and access to resources can be granted.

Read a different version of :