Developer Resources

The ID Token

The ID token is a token used to identify an end-user to the client application and to provide data around the context of that authentication.

An ID token will be in the JSON Web Token (JWT) format. In most cases the ID token will be signed according to JSON Web Signing (JWS) specifications, however depending on the client profile used the verification of this signature may be optional.

When the id_token is received from the token endpoint via a secure transport channel (i.e. via the Authorization Code grant type) the verification of the digital signature is optional.

Decoding the ID Token

The id_token JWT consists of three components, a header, a payload and the digital signature. Following the JSON Web Token (JWT) standard, these three sections are Base64url encoded and separated by periods (.).

JWT and OpenID Connect assume base64url encoding/decoding. This is slightly different than regular base64 encoding. Refer to RFC4648 for specifics regarding Base64 vs Base64 URL safe encoding.

The following example describes how to manually parse a sample ID token provided below:

eyJhbGciOiJSUzI1NiIsImtpZCI6Imkwd25uIn0.eyJzdWIiOiJqb2UiLCJhdWQiOiJpbV9vaWNfY2xpZW50IiwianRpIjoidWY5MFNLNH
dzY0ZoY3RVVDZEdHZiMiIsImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTAzMSIsImlhdCI6MTM5NDA2MDg1MywiZXhwIjoxMzk0MDYx
MTUzLCJub25jZSI6ImU5NTdmZmJhLTlhNzgtNGVhOS04ZWNhLWFlOGM0ZWY5Yzg1NiIsImF0X2hhc2giOiJ3Zmd2bUU5VnhqQXVkc2w5bG
M2VHFBIn0.lr4L-oT7DJi7Re0eSZDstAdOKHwSvjZfR-OpdWSOmsrw0QVeI7oaIcehyKUFpPFDXDR0-RsEzqno0yek-_U-Ui5EM-yv0Pia
UOmJK1U-ws_C-fCplUFSE7SK-TrCwaOow4_7FN5L4i4NAa_WqgOjZPloT8o3kKyTkBL7GdITL8rEe4BDK8L6mLqHJrFX4SsEduPk0CyHJS
ykRqzYS2MEJlncocBBI4up5Y5g2BNEb0aV4VZwYjmrv9oOUC_yC1Fb4Js5Ry1t6P4Q8q_2ka5OcArlo188XH7lMgPA2GnwSFGHBhccjpxh
N7S46ubGPXRBNsnrPx6RuoR2cI46d9ARQ
It is strongly recommended to make use of common libraries for JWT and JWS processing to avoid introducing implementation specific bugs.

The above JWT token is first split by periods (.) into three components:

JWT Header

Contains the algorithm and a reference to the appropriate public key if applicable:

Component Value Value Decoded

JWT Header

eyJhbGciOiJSUzI1NiIsImtpZCI6Imkwd25uIn0

\{ "alg":"RS256", "kid":"i0wnn" }

JWT Payload

The second component contains the payload which contains claims relating to the authentication and identification of the user. The payload of the above example is decoded as follows:

Component Value Value Decoded

JWT Payload

eyJzdWIiOiJqb2UiLCJhdWQiOiJpbV9vaWN fY2xpZW50IiwianRpIjoidWY5MFNLNHdzY0 ZoY3RVVDZEdHZiMiIsImlzcyI6Imh0dHBzO lwvXC9sb2NhbGhvc3Q6OTAzMSIsImlhdCI6 MTM5NDA2MDg1MywiZXhwIjoxMzk0MDYxMTU zLCJub25jZSI6ImU5NTdmZmJhLTlhNzgtNG VhOS04ZWNhLWFlOGM0ZWY5Yzg1NiIsImF0X 2hhc2giOiJ3Zmd2bUU5VnhqQXVkc2w5bGM2 VHFBIn0

\{ "sub":"joe", "aud":"im_oic_client", "jti":"uf90SK4wscFhctUT6Dtvb2", "iss":"https:\/\/localhost:9031", "iat":1394060853, "exp":1394061153, "nonce":"e957ffba-9a78-4ea9-8eca-ae8c4ef9c856", "at_hash":"wfgvmE9VxjAudsl9lc6TqA" }

The following claims you can expect in an id_token and can use to determine if the authentication by the user was sufficient to grant them access to the application. (Refer to the OpenID Connect specifications to additional details on these attributes):

Claim Description

iss

Issuer of the id_token

sub

Subject of the id_token (ie the end-user’s username)

aud

Audience for the id_token (must match the client_id of the application)

exp

Time the id_token is set to expire (UTC, Unix Epoch time)

iat

Timestamp when the id_token was issued (UTC, Unix Epoch time)

auth_time

Time the end-user authenticated (UTC, Unix Epoch time)

nonce

Nonce value supplied during the authentication request (REQUIRED for implicit flow)

acr

Authentication context reference used to authenticate the user

acr

Authentication context reference used to authenticate the user

at_hash

Hash of the OAuth2 access token when used with Implicit profile

c_hash

Hash of the OAuth2 authorization code when used with the hybrid profile

Digital Signature

Base64 URL encoded signature of section 1 and 2 (period concatenated). The algorithm and key reference used to create and verify the signature is defined in the JWT Header.

Component Value Value Decoded

JWT Signature

lr4L-oT7DJi7Re0eSZDstAdOKHwSvjZfR-OpdWSOmsrw0QVeI7oaIce hyKUFpPFDXDR0-RsEzqno0yek-_U-Ui5EM-yv0PiaUOmJK1U-ws_C-f CplUFSE7SK-TrCwaOow4_7FN5L4i-4NAa_WqgOjZPloT8o3kKyTkBL7 GdITL8rEe4BDK8L6mLqHJrFX4SsEduPk0CyHJSykRqzYS2MEJlncocB BI4up5Y5g2BNEb0aV4VZwYjmrv9oOUC_yC1Fb4Js5Ry1t6P4Q8q_2ka 5OcArlo188XH7lMgPA2GnwSFGHBhccjpxhN7S46ubGPXRBNsnrPx6Ru oR2cI46d9ARQ

N/A

Validating the ID Token

The validation of the ID token includes evaluating both the payload and the digital signature.

Payload Validation

The ID token represents an authenticated user’s session. As such the token must be validate before an application can trust the contents of the ID token. For example, if a malicious attacker replayed a user’s id_token that they had captured earlier the application should detect that the token has been replayed or was used after it had expired and deny the authentication.

Refer to the OpenID Connect specifications for more information on security concerns. The specifications also include guidelines for validating an ID token (Core specification section 3.1.3.7). The general process would be as follows:

Step # Test Summary

1

Decrypt the token (if encrypted)

2

Verify the issuer claim (iss) matches the OP issuer value

3

Verify the audience claim (aud) contains the OAuth2 client_id

4

If the token contain multiple audiences, then verify that an Authorized Party claim (azp) is present

5

If the azp claim is present, verify it matches the OAuth2 client_id

6, 7 & 8

Optionally verify the digital signature (required for implicit client profile) (see section 4.4)

9

Verify the current time is prior to the expiry claim (exp) time value

10

Client specific: Verify the token was issued within an acceptable timeframe (iat)

11

If the nonce claim (nonce) is present, verify that it matches the nonce passed in the authentication request

12

Client specific: Verify the Authn Context Reference claim (acr) value is appropriate

13

Client specific: If the authentication time claim (auth_time) present, verify it is within an acceptable range

14

If the implicit client profile is used, verify that the access token hash claim (at_hash) matches the hash of the associated access_token

Signature Validation

Signature validation is only required for tokens not received directly from the token endpoint (i.e. for the Implicit Client Profile). In other cases where the id_token is received directly by the client from the token endpoint over HTTPS, transport layer security should be sufficient to vouch for the integrity of the token.

The ID token is signed according to the JSON Web Signature (JWS) specification; algorithms used for signing are defined in the JSON Web Algorithm (JWA) specification. PingFederate 7.1 can support the following signing algorithms:

"alg" Value Signature Method Signing Key

NONE

No Digital Signature

N/A

HS256

HMAC w/ SHA-256 hash

Uses the client secret of the OAuth2 client

HS384

HMAC w/ SHA-384 hash

Uses the client secret of the OAuth2 client

HS512

HMAC w/ SHA-512 hash

Uses the client secret of the OAuth2 client

RS256

RSA PKCS v1.5 w/ SHA-256 hash

Public key available from the JWKS (see below)

RS384

RSA PKCS v1.5 w/ SHA-384 hash

Public key available from the JWKS (see below)

RS512

RSA PKCS v1.5 w/ SHA-512 hash

Public key available from the JWKS (see below)

ES256

ECDSA w/ P-256 curve and SHA-256 hash

Public key available from the JWKS (see below)

ES384

ECDSA w/ P-384 curve and SHA-384 hash

Public key available from the JWKS (see below)

ES512

ECDSA w/ P-521 curve and SHA-512 hash

Public key available from the JWKS (see below)

RS256 is the default signature algorithm.

The basic steps to verify a digital signature involve retrieving the appropriate key to use for the signature verification and then performing the cryptographic action to verify the signature.

To validate the signature, take the JWT header and the JWT payload and join with a period. Validate that value against the third component of the JWT using the algorithm defined in the JWT header. Using the above ID token as an example:

Signed data (JWT Header + "." + JWT Payload):

eyJhbGciOiJSUzI1NiIsImtpZCI6Imkwd25uIn0.eyJzdWIiOiJqb2UiLCJhdWQiOiJpbV9vaWNfY2xpZW50IiwianRpIjoidWY5MFNLNHdz
Y0ZoY3RVVDZEdHZiMiIsImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTAzMSIsImlhdCI6MTM5NDA2MDg1MywiZXhwIjoxMzk0MDYxMTUz
LCJub25jZSI6ImU5NTdmZmJhLTlhNzgtNGVhOS04ZWNhLWFlOGM0ZWY5Yzg1NiIsImF0X2hhc2giOiJ3Zmd2bUU5VnhqQXVkc2w5bGM2VHFB
In0

Signature value to verify:

lr4L-oT7DJi7Re0eSZDstAdOKHwSvjZfR-OpdWSOmsrw0QVeI7oaIcehyKUFpPFDXDR0-RsEzqno0yek-_U-Ui5EM-yv0PiaUOmJK1U-ws_C
-fCplUFSE7SK-TrCwaOow4_7FN5L4i-4NAa_WqgOjZPloT8o3kKyTkBL7GdITL8rEe4BDK8L6mLqHJrFX4SsEduPk0CyHJSykRqzYS2MEJln
cocBBI4up5Y5g2BNEb0aV4VZwYjmrv9oOUC_yC1Fb4Js5Ry1t6P4Q8q_2ka5OcArlo188XH7lMgPA2GnwSFGHBhccjpxhN7S46ubGPXRBNsn
rPx6RuoR2cI46d9ARQ
The actual implementation of the signing algorithm used to validate the signature will be implementation specific. It is recommended to use a published library to perform the signature verification.

For symmetric key signature methods, the client secret value for the OAuth2 client is used as the shared symmetric key. For this reason the client secret defined for the OAuth2 client must be of a large enough length to accommodate the appropriate algorithm (i.e. for a SHA256 hash, the secret must be at least 256 bits "“ 32 ASCII characters).

Asymmetric signature methods require the application to know the corresponding public key. The public key can be distributed out-of-band or can be retrieved dynamically via the JSON Web Key Set (JWKS) endpoint as explained below:

Step 1. Determine the signing algorithm (alg) and the key identifier (kid) from the JWT header. Using the sample JWT token above as an example, the following values are known:

OpenID Connect issuer

https://localhost:9031

Signing algorithm (alg)

RS256

Key reference identifier (kid)

i0wnn

Step 2. Query the OpenID configuration URL for the location of the JWKS:

GET https://localhost:9031/.well-known/openid-configuration HTTP/1.1

This will result in a HTTP response containing the OpenID Connect configuration for the OpenID Connect Provider (OP) :

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
  "version":"3.0",
  "issuer":"https:\/\/localhost:9031",
  "authorization_endpoint":"https:\/\/localhost:9031\/as\/authorization.oauth2",
  "token_endpoint":"https:\/\/localhost:9031\/as\/token.oauth2",
  "userinfo_endpoint":"https:\/\/localhost:9031\/idp\/userinfo.openid",
  "jwks_uri":"https:\/\/localhost:9031\/pf\/JWKS",
  "scopes_supported":["phone","address","email","admin","edit","openid","profile"],
  "response_types_supported":["code","token","id_token","code token",
    "code id_token","token id_token","code token id_token"],
  "subject_types_supported":["public"],
  "id_token_signing_alg_values_supported":["none","HS256","HS384","HS512","RS256",
    "RS384","RS512","ES256","ES384","ES512"],
  "token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],
  "claim_types_supported":["normal"],
  "claims_parameter_supported":false,
  "request_parameter_supported":false,
  "request_uri_parameter_supported":false
}

Step 3. Parse the JSON to retrieve the jwks_uri value (bolded above) and make a request to that endpoint, JSON Web Keystore (JWKS), to retrieve the public key for key identifier "i0wnn" and key type (kty) of RSA as the algorithm is RS256 that was used to sign the JWT:

GET https://localhost:9031/pf/JWKS HTTP/1.1

Which will return the JWKS for the issuer:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
  "keys":[
    {
      "kty":"EC",
      "kid":"i0wng",
      "use":"sig",
      "x":"AXYMGFO6K_R2E3RH42_5YTeGYgYTagLM-v3iaiNlPKFFvTh17CKQL_OKH5pEkj5U8mbel-0R1YrNuraRXtBztcVO",
      "y":"AaYuq27czYSrbFQUMo3jVK2hrW8KZ75KyE8dyYS-HOB9vUC4nMvoPGbu2hE_yBTLZLpuUvTOSSv150FLaBPhPLA2",
      "crv":"P-521"
    },
    ...
    {
      "kty":"RSA",
      "kid":"i0wnn",
      "use":"sig",
"n":"mdrLAp5GR8o5d5qbwWTYqNGuSXHTIE6w9HxV445oMACOWRuwlOGVZeKJQXHM9cs5Dm7iUfNVk4pJBttUxzcnhVCRf
      9tr20LJB7xAAqnFtzD7jBHARWbgJYR0p0JYVOA5jVzT9Sc-j4Gs5m8b-am2hKF93kA4fM8oeg18V_xeZf11WWcxnW5YZwX
      9kjGBwbK-1tkapIar8K1WrsAsDDZLS_y7Qp0S83fAPgubFGYdST71s-B4bvsjCgl30a2W-je9J6jg2bYxZeJf982dzHFqV
      QF7KdF4n5UGFAvNMRZ3xVoV4JzHDg4xe_KJE-gOn-_wlao6R8xWcedZjTmDhqqvUw",
      "e":"AQAB"
    },
    ...
  ]
}

We now have the modulus (n) and the exponent (e) of the public key. This can be used to create the public key and validate the signature.

The public key can be stored in secure storage (i.e. in the keychain) to be used for verification of the id_token when a user is offline.

Validating the token hashes (at_hash, c_hash)

We now have the modulus (n) and the exponent (e) of the public key. This can be used to create the public key and validate the signature.

In specific client profiles, a specific hash is included in the id_token to use to verify that the associated token was issued along with the id_token. For example, when using the implicit client profile, an at_hash value is included in the id_token that provides a means to verify that the access_token was issued along with the id_token.

The following example uses the id_token above and associated access_token to verify the at_hash id_token claim:

Signing algorithm

RS256

at_hash value

wfgvmE9VxjAudsl9lc6TqA

OAuth 2.0 access_token

dNZX1hEZ9wBCzNL40Upu646bdzQA

  1. Hash the octets of the ASCII representation of the access token (using the hash algorithm specified in the JWT header (i.e. for this example, RS256 uses a SHA-256 hash)): SHA256HASH("dNZX1hEZ9wBCzNL40Upu646bdzQA") = c1f82f98 4f55c630 2e76c97d 95ce93a8 9a5d61f7 dc99b9ad 37dc12b3 7231ff9d

  2. Take the left-most half of the hashed access token and Base64url encode the value. Left-most half: c1f82f98 4f55c630 2e76c97d 95ce93a8 Base64urlencode([0xC1, 0xF8, 0x2F, 0x98, 0x4F, 0x55, 0xC6, 0x30, 0x2E, 0x76, 0xC9, 0x7D, 0x95, 0xCE, 0x93, 0xA8]) = "wfgvmE9VxjAudsl9lc6TqA"

  3. Compare the at_hash value to the base64 URL encoded left-most half of the access token hash bytes.

at_hash value

wfgvmE9VxjAudsl9lc6TqA

left-most half value

wfgvmE9VxjAudsl9lc6TqA

Validation result

VALID