Rate this page

JWT

OAuth 2 access tokens and OpenID Connect ID tokens issued by the Ping Identity Data Governance Broker use the JSON Web Token (JWT) format. JWT, pronounced like the English word jot, is a standard format defined by RFC 7519, with related behaviors and extensions defined by the JavaScript Object Signing and Encryption (JOSE) standards. At a basic level, a JWT is really just a hash of data encoded into a compact format with a header and an optional signature, but JWTs have several features that make them well-suited for data exchange:

  • JWTs are easy to parse and contain structured data in the widely supported JSON format.
  • JWTs are compact and therefore may be used in URLs and HTTP headers.
  • JWTs may be digitally signed and encrypted, making them appropriate for trusted exchange of potentially sensitive data.

In this article, we’ll go over the basic structure of a JWT and provide an example of how to verify the integrity of a JWT by checking its signature.

JWT structure

JWTs use a simple dot-delimited three-part structure. To parse a JWT, an application splits it along the dots and decodes each piece using the Base64url scheme.

<Base64url-encoded header>.<Base64url-encoded payload>.<Base64url-encoded signature>

Here’s an example ID token. Let’s look at each piece in turn.

eyJhbGciOiJSUzI1NiJ9.ewogICJhdXRoX3RpbWUiOiAxNDY0NjE2OTIyLAogICJleHAiOiAxNDY0NjE3ODI2LAogICJzdWIiOiAiVXNlcnMvMzBiMTVjMTYtMjYzOC00NDEzLWIxZDItZWI2MDkxMGM0YmFkIiwKICAiYXRfaGFzaCI6ICI4X3RpanhzcFJTQkxlWVdUaW4yVUdRIiwKICAibm9uY2UiOiAiNzcyNTM4NzU5ZWUyNDdmOWE5NWUwODJkYThlNGU2ZDkiLAogICJhdWQiOiAidGVzdDEiLAogICJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbSIsCiAgImlhdCI6IDE0NjQ2MTY5MjYsCiAgImFjciI6ICJEZWZhdWx0IiwKICAiYW1yIjogWwogICAgInBhc3N3b3JkIgogIF0KfQ.GPNL1kKBvK98be9rgNb6xaOkprIZRgvpexcqwTGjVkhq51YsndIt_J2hs9HgRgO2NGpEx0HCmbBEdnhpiMWbyoxsP4QdEZ08HXZ1dWAaXnEyS-4gne_J_ZEALssUJrEOWoRsfV9ZTLBTt511c1FOKrybGyNHuf6VQZCPESnygTc6m1MQSWfapJDJOE_VkCMJZ2CvWEYrxFk-FuUi57VNlsvqIUzXPe7gjDpdPxScx0QhbaX4vfMGswOYfSoXli3tqNIlUwMKFCc7n5ixY4UhGQu2Dr9eFFGIDSx0Lf1bve_uCIOJtA42SaurOODe2PTdrg0fcceulGst_NxnNzUtvg

Decoding the first part gives us the header, a JSON object with two fields describing the JWT’s signing or encryption metadata. JWT fields are usually called claims.

{
  "alg": "RS256",
  "kid": "rsa-idtoken"
}

The alg (algorithm) claim is a standard claim that denotes the algorithm used to sign the JWT. The kid (key ID) claim is a standard claim that identifies the key that was used to sign the JWT.

Decoding the second part gives a set of claim called the payload.

{
  "auth_time": 1464616922,
  "exp": 1464617826,
  "sub": "Users/30b15c16-2638-4413-b1d2-eb60910c4bad",
  "at_hash": "8_tijxspRSBLeYWTin2UGQ",
  "nonce": "772538759ee247f9a95e082da8e4e6d9",
  "aud": "test1",
  "iss": "https://example.com",
  "iat": 1464616926,
  "acr": "Default",
  "amr": [
    "password"
  ]
}

The claims in the payload will vary depending on the kind of JWT. This JWT happens to be an ID token, and the claims are defined by the OpenID Connect standard. For example, the sub claim provides a unique ID for the authenticated end user, while the iss claim identifies the provider that issued the ID token.

The third part is the JWT signature, which is computed using the header and the payload, and decodes to a binary value. See RFC 7515 for more information about this.

Verifying JWT signatures

JWTs are signed using various schemes called JSON Web Algorithms (JWAs). The JWA used to sign a JWT determines how a client may verify the signature.

  • HMAC-based (HS256, HS384, HS512): Signed and verified using a shared secret. Because both parties must know the shared secret, this is a symmetric cryptographic relationship.
  • RSA-based (RS256, RS384, RS512): Signed using a public/private key pair and verified using the public key. Because only one party possesses the private key, this is an asymmetric cryptographic relationship.

A large number of third-party libraries are available for performing JWT signature verification, and we strongly recommend that you use a well-vetted library rather than providing your own implementation. The site JWT.IO catalogs JWT-related libraries for a variety of languages.

JWT.IO and the jsrsasign site also provide useful exploratory tools for decoding and verifying JWTs from a web browser.

When to verify JWT signatures

Do you always need to verify signed JWTs? Not necessarily. But as a rule of thumb, if you did not receive a JWT directly from the entity that issued the JWT over a trusted HTTPS connection, then you should validate the JWT’s integrity by verifying its signature.

For example, if you are writing a resource server that accepts access tokens issued by the Data Governance Broker, then you must either verify the JWT’s signature or submit the token to the Data Governance Broker’s token validation endpoint for verification. This is because a resource server always receives bearer tokens from a potentially untrusted third party, the client.

On the other hand, if you are writing an OAuth 2 or OpenID Connect client, then your client will receive access tokens or ID tokens directly from the Data Governance Broker, and the trust provided by the HTTPS connection between your client and the server may be sufficient to accept the authenticity of the token. Be aware, though, that the OpenID Connect specification defines additional validation steps for clients to perform on ID tokens. We strongly recommend that your application perform this additional validation.

Verifying JWTs signed by the Ping Identity Data Governance Broker

Either family of JWAs may be used by the Data Governance Broker to sign ID tokens, while only RSA-based JWAs are used to sign access tokens. ID tokens signed using HMAC-based JWAs use the client secret of the client making the OpenID Connect request. ID tokens signed using RSA-based JWAs use a key pair determined by the configuration of the client, while all access tokens are signed using an RSA key pair defined for the server globally. This is summarized by the table below.

Type of JWT Servers signs with Client verifies with Appropriate client role
Access token RSA-based JWA RSA public key Resource server
ID token HMAC-based JWA Client secret Server-side web application
ID token RSA-based JWA RSA public key Client-side or server-side web application

In all cases, the necessary secret or public keys may be provided out of band by the server administrator. RSA public keys are also exposed in JSON Web Key (JWK) form through the Data Governance Broker’s JWKS endpoint; a client can obtain this key set by making an HTTP GET request to /jwks. The response is described in detail by the OAuth 2 and OpenID Connect Endpoint Reference. The client can use the kid field of the JWT header to determine which JWK to choose, if multiple JWKs are present.

Here’s a Java example that uses the Nimbus JOSE+JWT library to retrieve a JWK public key from a Data Governance Broker’s JWKS endpoint, then uses the JWK to verify an ID token’s signature:

SignedJWT idToken = SignedJWT.parse("eyJhbGciOiJSUzI1NiJ9.eyJhdXRo...");
String kid = idToken.getHeader().getKeyID();

JWKSet jwks = JWKSet.load(new URL("https://example.com/jwks"));
RSAKey jwk = (RSAKey) jwks.getKeyByKeyId(kid);

JWSVerifier verifier = new RSASSAVerifier(jwk);
if (idToken.verify(verifier)) {
  System.out.println("valid signature");
} else {
  System.out.println("invalid signature");
}