Validating an ID Token

Validating an ID Token

develop

How to decode and validate an ID Token

Overview

This article describes how to validate an OpenID Connect ID Token. What each part of the token means and when to use them.

The ID Token is a signed JSON Web Token (JWT). The JWT format is specified in RFC7519.

The ID Token is a JWT with specified contents, defined by the OpenID Foundation in the OpenID Connect Core Specification specification.


The ID Token JWT

An ID Token is always a JWT token. It cannot be issued as an opaque reference token.

The ID Token is a an encoded and signed JSON Web Token (JWT) containing claims issued by the OpenID Connect Provider (Curity).

The ID token consists of three main parts:

  • header - Metadata about the token and its cryptographic algorithm
  • payload - Claims about the issuer, the user and user authorization
  • signature - For verification of the integrity of the token

The parts are separated with a . period in the encoded token:

Example:

eyJraWQiOiItMzgwNzQ4MTIiLCJ4NXQiOiJNUi1wR1RhODY2UmRaTGpONlZ3cmZheTkwN2ciLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiQ2djcDVvaWtzdDM4M2NBYl9KNU1WUSIsImRlbGVnYXRpb25faWQiOiI4MDkxZDEzMi02OGRkLTQ2YzMtYWRiZC0xYzgzNjQzYzRhNTciLCJhY3IiOiJ1cm46c2U6Y3VyaXR5OmF1dGhlbnRpY2F0aW9uOmh0bWwtZm9ybTpodG1sU3FsIiwic19oYXNoIjoiYXFpRFYxQ3k2U0NzZmEyQXlOcjVfUSIsImF6cCI6ImNsaWVudC1vbmUiLCJhdXRoX3RpbWUiOjE1NTA4MzMxNDgsIm5vbmNlIjoiMTU1MDgzMzExODA5Ni1IVm0iLCJleHAiOjE1NTA4MzY4MDcsIm5iZiI6MTU1MDgzMzIwNywianRpIjoiZTc1ZmQ0ZTctNjVjNC00ZmVlLWJjYjYtZGUxZjI5OTYwMTViIiwiaXNzIjoiaHR0cHM6Ly9leGFtcGxlLmN1cml0eS5pbyIsImF1ZCI6ImNsaWVudC1vbmUiLCJzdWIiOiJqb2huZG9lIiwiaWF0IjoxNTUwODMzMjA3LCJwdXJwb3NlIjoiaWQifQ.kKq2h2x20K-eXtoAeTFsjOIFzmfTjR8zP5F48qTQAkAK9qLKJCQkJ9UkAmHUw9BVgfb2zxLiRG6e5ACTpmxR_Mqv4mFeGyBR1faLJTXwdmiuJqqBcCKWS48KmhE-7XkxoF9nuiU_aaQArVn-Cx3qI2pQ945qUcXa47J2vn0xxGpm82VLQNfCsttT4M7HU-SjvTHOqS439bKl4gIcg-MIxmog08J5GdQpmyt7p2-cGvwd4iQVcd5t6sL0hcw5Q36sLSRnms4e8F9xWLZS2cD6ppdv4h8nr9whQB5NtIbw10f8rgy6o6vODu91JoxrAodTyu6QlVtN6uugicAR4OWs5w

OAuth.Tools

You can use Curity's OAuth.Tools to decode and analyze JWT:s to try them out.

Decoding

To decode the token, you need to do a base64url decode of each part. The decoded parts will be represented as JSON content data. Split the token on the . period.

The header contains metadata, such as and the hash algorithm used for the token contents, and (somewhat redundantly for our purposes) the type of token.

Example:

{
  "kid": "-38074812",
  "x5t": "MR-pGTa866RdZLjN6Vwrfay907g",
  "alg": "RS256"
}

The header should be used to figure out what key to use when validating the token. There are a few options but the most common ones are:

kid : The key id. The key is found on the Json Web Key Set (JWKS) endpoint of the issuer.

x5t or x5t#256 : The fingerprint of the certificate to use hashed with SHA1 or SHA256.

alg : The algorithm used when signing the token. This should be a strong enough algorithm. Do not rely on this solely. Libraries should block algorithms such as none to prevent attacks.

x5t is commonly used when the certificate is pre-distributed to the clients, and kid when JWKS is used.

For more information, see the Registered Header Parameter Names section of RFC7515 - JSON Web Signature.

Payload

The Payload contains the claims for use by the client. When decoding the first thing to look for is the iss issuer field. It shows who issued the token and must be an HTTPS url. This is important for the following reasons:

  • Only trust tokens from known issuers
  • Use the issuer to find the JWKS endpoint via the OpenID Connect Metadata

Example:

{
  "at_hash": "Cgcp5oikst383cAb_J5MVQ",
  "delegation_id": "8091d132-68dd-46c3-adbd-1c83643c4a57",
  "acr": "urn:se:curity:authentication:html-form:htmlSql",
  "s_hash": "aqiDV1Cy6SCsfa2AyNr5_Q",
  "azp": "client-one",
  "auth_time": 1550833148,
  "nonce": "1550833118096-HVm",
  "exp": 1550836807,
  "nbf": 1550833207,
  "jti": "e75fd4e7-65c4-4fee-bcb6-de1f2996015b",
  "iss": "https://example.curity.io",
  "aud": "client-one",
  "sub": "johndoe",
  "iat": 1550833207,
  "purpose": "id"
}

Signature

The last part of the ID Token is the signature, which you use to to verify that the token was issued by the trusted issuer and not tampered with in route.

kKq2h2x20K-eXtoAeTFsjOIFzmfTjR8zP5F48qTQAkAK9qLKJCQkJ9UkAmHUw9BVgfb2zxLiRG6e5ACTpmxR_Mqv4mFeGyBR1faLJTXwdmiuJqqBcCKWS48KmhE-7XkxoF9nuiU_aaQArVn-Cx3qI2pQ945qUcXa47J2vn0xxGpm82VLQNfCsttT4M7HU-SjvTHOqS439bKl4gIcg-MIxmog08J5GdQpmyt7p2-cGvwd4iQVcd5t6sL0hcw5Q36sLSRnms4e8F9xWLZS2cD6ppdv4h8nr9whQB5NtIbw10f8rgy6o6vODu91JoxrAodTyu6QlVtN6uugicAR4OWs5w

Validating the token

After initial decoding the token can be validated.

Signature

The signature is generated using the hashing algorithm from the token header.

To verify the signature, you must:

  • Check the signing algorithm
  • Verify signature with a public key

Check Algorithm

The alg value for the signature should be of the expected type. (Whitelist the types your organization allows)

Verify signature

To verify the signature you should:

  • Retrieve the public key by using the x5t or kid parameter.
  • Break off the signature from the message leaving the header.payload encoded
  • Convert the header+payload segment to an ASCII array
  • Base64Url decode the signature
  • Use the decoded signature to validate the header+payload ASCII byte array

Verifying Standard Claims

Once the token has been received and decoded, you can validate the claims it contains, to make sure that they match the expected values:

ClaimDescriptionComment
acrAuthentication method usedShould match the acr_values request parameter (if it was sent in the request).
issIssuerIdentifier for the creator of the token. Must be a plain https:// URL.
subSubjectIdentifier for the authenticated user.
audAudienceExpected recipient(s). Must contain some identifier for client, for example the Client ID or domain.
iatIssued AtWhen the ID Token was issued.
auth_timeAuthentication TimeWhen the user last was logged in without SSO.
jtiToken identifierA unique identifier for this token. Can be used to prevent replays
nonceNonceIf the client passed in a nonce when requesting the token it must be present here

Was this page helpful?