JWT policies overview

This topic provides general information about JWT and the Apigee JWT policies that may be of interest to Apigee proxy developers.

Introduction

JSON Web Tokens, or JWT, are commonly used to share claims or assertions between connected applications. The JWT policies enables Edge API proxies to:

  • Generate signed JWTs.
  • Verify digitally-signed JWTs and claims within those JWTs.
  • Decode signed JWTs without validating signatures on the token.

In the latter two cases, the policy also sets variables that allow additional policies, or the backend services themselves, to inspect the validated claims and to take decisions based on those claims.

When using the Verify JWT policy, an invalid JWT will be rejected and will result in an error condition. Similarly, when using the Decode JWT policy, a malformed JWT will result in an error condition.

Videos

Watch a short video for a quick introduction to JWT.

What a short video to learn more about the JWT structure.

Use cases

You can use the policy to:

  • Verify and extract claims from a JWT obtained from inbound client requests, from target service responses, from Service Callout policy responses, or from other sources. Edge will verify the signature on a JWT, whether the JWT was generated by a third-party, or by Edge itself, using either RSA or HMAC algorithms.
  • Generate a new JWT on either the proxy or target endpoint sides of an Edge proxy. For example, you might create a proxy request flow that generates a JWT and returns it to a client. Or, you might design a proxy so that it generates a JWT on the target request flow, and attaches it to the request sent to the target. Those claims would then be available to enable backend services to apply further security processing.
  • Decode a JWT. Decoding is most useful when used in concert with the Verify JWT Policy, when the value of a claim from within the JWT must be known before verifying the signature of the JWT.

About standard and additional claims

Edge supports a set of standard (registered) JWT claims including subject, issuer, audience, expiration time, and not-before time. You can read about these claims in the Registered Claim Names section of the JWT specification. In addition, Edge supports adding additional claims to a JWT. Each claim is a simple name/value pair, where the value can be of type number, boolean, string, map, or array of any of the primitives. In JWT, the set of claims, registered and additional, are represented as a simple JSON hash, then canonicalized and signed using a symmetric or asymmetric cryptographic signing algorithm.

About signature encryption algorithms

The JWT Verification and JWT Generation policies support RSA and HMAC algorithms, using SHA2 checksums of bit strength 256, 384, or 512. The JWT Decode policy works regardless of the algorithm that was used to sign the JWT.

HMAC algorithm

The HMAC algorithm relies on a shared secret, known as the secret key, for creating the signature (also known as signing the JWT) and for verifying the signature.

RSA algorithm

The RSA algorithm uses a public/private key pair for the cryptographic signature. With RSA signatures, the signing party uses a private key to sign the JWT, and the verifying party uses the matching public key to verify the signature on the JWT.

Parts of a JWT

A signed JWT encodes information in three parts: the header, the payload, and the signature.

  • The Generate JWT policy creates all of those parts.
  • The Verify JWT policy examines all three parts.
  • The Decode JWT policy examines the header and payload.

To learn more about the parts of a JWT and how they are encoded and signed, refer to IETF RFC7519.

Using a JSON Web Key Set (JWKS) to verify a JWT

When you verify a JWT signed with an RSA algorithm, you need to provide the public key that is associated with the private key used to sign the token. You have two options for providing the public key to the Verify JWT policy:

  • use the actual public key value (typically provided in a flow variable), or
  • use a public key wrapped in a JWKS.

About JWKS

A JWKS is a JSON structure that represents a set of JSON Web Keys (JWKs). A JWK is a JSON data structure that represents a cryptographic key. JWK and JWKS are described in RFC7517.

Required JWKS structure

Each key element in the JWKS must include these attributes:

  • kty - Must be set to RSA.
  • kid (the key id) - Can be any arbitrary value (no duplicates within a key set). If the inbound JWT bears a key ID which is present in the set of JWKS, then the policy will use the correct public key to verify the JWT signature.
  • n - The RSA key value "modulus".
  • e - The RSA key value "exponent".

Following are optional elements and their required values:

  • alg - If present, must be RS256.
  • use - If present, must be sig.

The following JWKS includes the required elements and values and would be valid on Edge (from https://www.googleapis.com/oauth2/v3/certs):

{  
   "keys":[  
      {  
         "kty":"RSA",
         "alg":"RS256",
         "use":"sig",
         "kid":"ca04df587b5a7cead80abee9ea8dcf7586a78e01",
         "n":"iXn-WmrwLLBa-QDiToBozpu4Y4ThKdwORWFXQa9I75pKOvPUjUjE2Bk05TUSt7-V7KDjCq0_Nkd-X9rMRV5LKgCa0_F8YgI30QS3bUm9orFryrdOc65PUIVFVxIwMZuGDY1hj6HEJVWIr0CZdcgNIll06BasclckkUK4O-Eh7MaQrqb646ghFlG3zlgk9b2duHbDOq3s39ICPinRQWC6NqTYfqg7E8GN_NLY9srUCc_MswuUfMJ2cKT6edrhLuIwIj_74YGkpOwilr2VswKsvJ7dcoiJxheKYvKDKtZFkbKrWETTJSGX2Xeh0DFB0lqbKLVvqkM2lFU2Qx1OgtTnrw",
         "e":"AQAB"
      },
      {  
         "kty":"RSA",
         "alg":"RS256",
         "use":"sig",
         "kid":"91412840c1019dedff1854b89938ad0a9078b871",
         "n":"veQQDTKL8UKIRIuWxHLeRH0umTHtnm2LXej56MungXuFZwmk_xccvsMpCtXmqhvEmHyAmKF06YRRWAomHIwC7IBz9BsIjPYtOkvVkff_SCFoyuyDFkNsDsbTydIDUFAV5YlhZOvgq1PJCzu8AfU9HoRf0WIEnexpTDyWzlqNbg0b94Tlmw01C5kDRGDD33v8i-RM4xXXyovBA_8R8D9t0aIzC9iVL418E0FOXAQ5xvrbvTXAkr17U4yIINUZ03q7i5tOxSA1z0s_-CK6NNoLZcDQXddBFCCYXNmfhaiu-NeSVePZMmDZGIFZIhIvagPcs3pZfSC5ysyo3tu3r16tdw",
         "e":"AQAB"
      },
      {  
         "kty":"RSA",
         "alg":"RS256",
         "use":"sig",
         "kid":"f5010d8a5353b010c81fa45f1e43d2789b18bc9c",
         "n":"2_jAH07j0ufDhv3sE2ky4m7w7xWbOZfMa1EI3MAOMdAcskKGDlv67sN8OKTo3B8uDLIx8F8lfGToG4hUr8N08YYXFzqRpXk3b8_kzVDrF1Z7dSi_OhQXkXsnEdUPAn3CQu6a_ObQ7XAyrr2eF0L_unptoQhanYte78LwOrPGKZTEKN6MEwHxz1yTR37yl88VNoV3WsLoeuDxhomgtSD5liFGBFuJt5-f5x-ZwXdDKgNgdIA7k8YYw246o47OKrb9xGR62qi0Mahxot_523BLyY18CUgbpb-VBPpVyseNOrpUQarEFcAi3Pab4vwAzZoA8NVyvl7aF2QRdIMIXoDmPw",
         "e":"AQAB"
      }
   ]
}

Designing your proxy to use JWKS

When a JWT is obtained from an issuer, often the issuer inserts a Key ID (or kid) into the JWT header. The key tells the recipient of the JWT how to find the public or secret key necessary to verify the signature on the signed JWT.

As an example, suppose an issuer signs a JWT with a private key. The "Key ID" identifies the matching public key to use to verify the JWT. The list of public keys is typically available at some well-known endpoint, for example: https://www.googleapis.com/oauth2/v3/certs.

This is the basic sequence that Edge (or any platform that works with JWKS) needs to perform to work with a JWT that has a JWKS:

  1. Examine the JWT header to find the Key ID (kid).
  2. Retrieve the list of keys and IDs from the well-known endpoint for a given issuer.
  3. Extract the public key from the list of keys with the key ID noted in the JWT header.
  4. Use that public key to verify the signature on the JWT.

As an Edge API proxy developer you need to do the following to perform JWT verification:

  1. Retrieve the list of keys and IDs from the well-known endpoint for a given issuer. You can use a Service Callout policy for this step. Note: It's a good practice to cache the JWKS to avoid a Service Callout on every request.
  2. In the Verify JWT policy, specify the location of the JWT (in the Source element) and the JWKS payload (in the PublicKey/JWKS element). For example:
    <VerifyJWT name="JWT-Verify-RS256">
        <Algorithm>RS256</Algorithm>
        <Source>json.jwt</Source>
        <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
        <PublicKey>
            <JWKS ref="public.jwks"/>
        </PublicKey>
        <Subject>apigee-seattle-hatrack-montage</Subject>
        <Issuer>urn://apigee-edge-JWT-policy-test</Issuer>
        <Audience>urn://c60511c0-12a2-473c-80fd-42528eb65a6a</Audience>
        <AdditionalClaims>
            <Claim name="show">And now for something completely different.</Claim>    
        </AdditionalClaims>
    </VerifyJWT>
    

The Verify JWT policy does everything else:

  • If a key with a Key ID that matches the Key ID (kid) asserted in the JWT is not found in the JWKS, then the Verify JWT policy throws an error and does not validate the JWT.
  • If the inbound JWT does not bear a key ID (kid) in the header, then this mapping of keyid-to-verification-key is not possible.

As the proxy designer, you are responsible for determining the key to use; in some cases this may be a fixed, hard-coded key.

See also