---
title: IdTokenValidationFilter
description: Validates an ID token by checking the standard claims, aud, exp, and iat. If specified in the configuration, this filter also checks the ID token issuer and signature.
component: pinggateway
version: 2023.11
page_id: pinggateway:reference:IdTokenValidationFilter
canonical_url: https://docs.pingidentity.com/pinggateway/2026/reference/IdTokenValidationFilter.html
llms_txt: https://docs.pingidentity.com/pinggateway/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
revdate: 2025-06-03T11:21:31Z
superseded_by: https://docs.pingidentity.com/pinggateway/2026/reference/IdTokenValidationFilter.html
section_ids:
  IdTokenValidationFilter-usage: Usage
  IdTokenValidationFilter-properties: Properties
  IdTokenValidationFilter-example: Example
  IdTokenValidationFilter-moreinfo: More information
---

# IdTokenValidationFilter

Validates an ID token by checking the standard claims, `aud`, `exp`, and `iat`. If specified in the configuration, this filter also checks the ID token issuer and signature.

This filter passes data into the context as follows:

* If the JWT is validated, the request continues down the chain. The data is provided in the [JwtValidationContext](Contexts.html#JwtValidationContext).

* If the JWT is not validated, data is provided in the [JwtValidationErrorContext](Contexts.html#JwtValidationErrorContext).

  If a failure handler is configured, the request passes to the failure handler. Otherwise, an HTTP 403 Forbidden is returned.

The `iat` claim is required, and the `iat` minus the `skewAllowance` must be before the current time on the IG clock. For information, see [OpenID Connect Core 1.0 incorporating errata set 1](https://openid.net/specs/openid-connect-core-1_0.html).

## Usage

```json
{
   "name": string,
   "type": "IdTokenValidationFilter",
   "config": {
     "idToken": runtime expression<string>,
     "audience": configuration expression<string>,
     "issuer": configuration expression<string>,
     "skewAllowance": configuration expression<duration>,
     "verificationSecretId": configuration expression<secret-id>,
     "secretsProvider": SecretsProvider reference,
     "customizer": JwtValidatorCustomizer reference,
     "failureHandler": Handler reference
   }
}
```

## Properties

* `"idToken"`: *runtime expression<[string](preface.html#definition-string)>, required*

  The ID token as an expression representing the JWT or signed JWT in the request. Cannot be null.

* `"audience"`: *configuration expression<[string](preface.html#definition-string)>, required*

  One `aud` claim to check on the JWT. Cannot be null.

* `"issuer"`: *configuration expression<[string](preface.html#definition-string)>, optional*

  One `iss` claim to check on the JWT. Can be null.

- `"skewAllowance"`: *configuration expression<[duration](preface.html#definition-duration)>, optional*

  The duration to add to the validity period of a JWT to allow for clock skew between different servers. To support a zero-trust policy, the skew allowance is by default zero.

  A `skewAllowance` of 2 minutes affects the validity period as follows:

  * A JWT with an `iat` of 12:00 is valid from 11:58 on the IG clock.

  * A JWT with an `exp` 13:00 is expired after 13:02 on the IG clock.

  Default: `zero`

* `"verificationSecretId"`: *configuration expression<[secret-id](preface.html#definition-secretid)>, required to verify the signature of signed tokens*

  The secret ID for the secret to verify the signature of signed tokens.

  This secret ID must point to a [CryptoKey](../security-guide/keys.html#secret-types).

  If configured, the token must be signed. If not configured, IG does not verify the signature.

  For information about how signatures are validated, refer to [Validate the signature of signed tokens](../security-guide/keys.html#secret-valid-signature). For information about how each type of secret store resolves named secrets, refer to [Secrets](secrets.html).

* `"secretsProvider"`: *SecretsProvider [reference](preface.html#definition-reference), required*

  The [SecretsProvider](secrets.html#SecretsProvider) to use to resolve queried secrets, such as passwords and cryptographic keys. Provide either the name of a SecretsProvider object defined in the heap or specify a SecretsProvider object inline.

- `"customizer"`: *JwtValidatorCustomizer [reference](preface.html#definition-reference), optional*

  A set of validation constraints for JWT claims and sub-claims. If a claim is not validated against the constraint, the JWT is not validated.

  The customizer does not override existing constraints, such as `aud`, `iss`, `exp`, and `iat`, which are predefined in the IdTokenValidationFilter, Defining a new constraint on an already constrained claim has an impact only if the new constraint is more restrictive.

  JwtValidatorCustomizer provides a ScriptableJwtValidatorCustomizer to enrich a `builder` object by using its methods. Get more information about the following items:

  * The `builder` object, at [Available Objects](Scripts.html#Scripts-availableob).

  * Transformer methods, to enrich the builder object, at [org.forgerock.openig.util.JsonValues](../_attachments/apidocs/org/forgerock/openig/util/JsonValues.html).

  * Constraints, at [org.forgerock.openig.tools.jwt.validation.Constraints](../_attachments/apidocs/org/forgerock/openig/tools/jwt/validation/Constraints.html).

  * Other properties for ScriptableJwtValidatorCustomizer, at [Scripts](Scripts.html).

  The following examples provide checks:

  * Check that the value of the claim `greaterThan5` is greater than 5

    ```json
    "customizer": {
      "type": "ScriptableJwtValidatorCustomizer",
      "config": {
        "type": "application/x-groovy",
        "source": [
          "builder.claim('/greaterThan5', JsonValue::asInteger, isGreaterThan(5))"
        ]
      }
    }
    ```

  * Check that the value of the claim `sub` is `george`

    ```json
    "customizer": {
      "type": "ScriptableJwtValidatorCustomizer",
      "config": {
        "type": "application/x-groovy",
        "source": [
          "builder.claim('subname', JsonValue::asString, isEqualTo('george'))"
        ]
      }
    }
    ```

  * Check that the value of the custom sub-claim is `ForgeRock`

    ```json
    "customizer": {
      "type": "ScriptableJwtValidatorCustomizer",
      "config": {
        "type": "application/x-groovy",
        "source": [
          "builder.claim('customclaim/subclaim', JsonValue::asString, isEqualTo('ForgeRock'));"
        ]
      }
    }
    ```

  * Check the value of multiple claims

    ```json
    "customizer": {
      "type": "ScriptableJwtValidatorCustomizer",
      "config": {
        "type": "application/x-groovy",
        "source": [
          "builder.claim('aud', listOf(JsonValue::asString), contains('My App'))",
          "       .claim('iat', instant(), isInThePast())",
          "       .claim('exp', instant(), isInTheFuture());",
          "builder.claim('iss', JsonValue::asString, isEqualTo('ForgeRock AM'));"
        ]
      }
    }
    ```

  * Check that the value of `val1` is greater than `val2`

    ```json
    "customizer": {
      "type": "ScriptableJwtValidatorCustomizer",
      "config": {
        "type": "application/x-groovy",
        "source": [ "builder.claim('/val1', JsonValue::asInteger, isGreaterThan(claim('/val2').asInteger()))" ]
      }
    }
    ```

  * Check that the value of `val1` is greater than `val2`, when both are YYYY-MM-DD dates

    ```json
    "customizer": {
      "type": "ScriptableJwtValidatorCustomizer",
      "config": {
        "type": "application/x-groovy",
        "source": [
          "Function<JsonValue, java.time.LocalDate, Exception> asDate() {",
          "  return (jsonValue) -> java.time.LocalDate.parse(jsonValue.asString());",
          "}",
          "builder.claim('claim1', asDate(), isGreaterThan(claim('claim2').as(asDate())));"
        ]
      }
    }
    ```

  * Check that the claim issuer matches the regex pattern

    ```json
    "customizer": {
      "type": "ScriptableJwtValidatorCustomizer",
      "config": {
        "type": "application/x-groovy",
        "source": [ "builder.claim('iss', JsonValue::asString, find(~/.*am\.example\.(com|org)/))" ]
      }
    }
    ```

Default: Claims are not validated

* `"failureHandler"`: *Handler [reference](preface.html#definition-reference), optional*

  Handler to treat the request on failure.

  Provide an inline handler configuration object or the name of a handler object declared in the heap. See also [Handlers](Handlers.html).

  Default: HTTP 403 Forbidden, the request stops being executed.

## Example

Validate an id_token

1. Set up AM:

   1. Set up AM as described in [Validate access tokens through the introspection endpoint](../gateway-guide/oauth2.html#oauth2-rs-introspect).

   2. Select Applications > OAuth 2.0 > Clients, and add the additional scope `openid` to `client-application`.

2. Set up IG:

   1. Add the following route to IG:

      * Linux

      * Windows

      ```
      $HOME/.openig/config/routes/idtokenvalidation.json
      ```

      ```
      %appdata%\OpenIG\config\routes\idtokenvalidation.json
      ```

      ```json
      {
        "name": "idtokenvalidation",
        "condition": "${find(request.uri.path, '^/idtokenvalidation')}",
        "capture": "all",
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [{
              "type": "IdTokenValidationFilter",
              "config": {
                "idToken": "<id_token_value>",
                "audience": "client-application",
                "issuer": "http://am.example.com:8088/openam/oauth2",
                "failureHandler": {
                  "type": "ScriptableHandler",
                  "config": {
                    "type": "application/x-groovy",
                    "source": [
                      "def response = new Response(Status.FORBIDDEN)",
                      "response.headers['Content-Type'] = 'text/html; charset=utf-8'",
                      "def errors = contexts.jwtValidationError.violations.collect{it.description}",
                      "def display = \"<html>Can't validate id_token:<br> ${contexts.jwtValidationError.jwt} \"",
                      "display <<=\"<br><br>For the following errors:<br> ${errors.join(\"<br>\")}</html>\"",
                      "response.entity=display as String",
                      "return response"
                    ]
                  }
                },
                "verificationSecretId": "verify",
                "secretsProvider": {
                  "type": "JwkSetSecretStore",
                  "config": {
                    "jwkUrl": "http://am.example.com:8088/openam/oauth2/connect/jwk_uri"
                  }
                }
              }
            }],
            "handler": {
              "type": "StaticResponseHandler",
              "config": {
                "status": 200,
                "headers": {
                  "Content-Type": [ "text/html; charset=UTF-8" ]
                },
                "entity": "<html><body>Validated id_token:<br> ${contexts.jwtValidation.value}</body></html>"
              }
            }
          }
        }
      }
      ```

      Notice the following features of the route:

      * The route matches requests to `/idtokenvalidation`.

      * A SecretsProvider declares a JwkSetSecretStore to validate secrets for signed JWTs, which specifies the URL to a JWK set on AM that contains the signing keys.

      * The property `verificationSecretId` is configured with a value. If this property is not configured, the filter does not verify the signature of tokens.

      * The JwkSetSecretStore specifies the URL to a JWK set on AM, that contains verification keys identified by a `kid`. The signature of the token is verified as follows:

        * If the value of a `kid` in the JWK set matches a `kid` in the the signed JWT, the JwkSetSecretStore verifies the signature.

        * If the JWT doesn't have a `kid`, or if the JWK set doesn't contain a key with the same value, the JwkSetSecretStore looks for valid secrets with the same purpose as the value of `verificationSecretId`.

      * If the filter validates the token, the StaticResponseHandler displays the token value from the context `${contexts.jwtValidation.value}`. Otherwise, the ScriptableHandler displays the token value and a list of violations from the context `${contexts.jwtValidationError.violations}`

3. Test the setup:

   1. In a terminal window, use a `curl` command similar to the following to retrieve an id_token:

      ```bash
      $ curl -s \
      --user "client-application:password" \
      --data "grant_type=password&username=demo&password=Ch4ng31t&scope=openid" \
      http://am.example.com:8088/openam/oauth2/access_token

      {
       "access_token":"...",
       "scope":"openid",
       "id_token":"...",
       "token_type":"Bearer",
       "expires_in":3599
      }
      ```

      * In the route, replace `<id_token_value>` with the value of the `id_token` returned in the previous step.

   2. Access the route on <http://ig.example.com:8080/idtokenvalidation>.

      The validated token is displayed.

      * In the route, invalidate the token by changing the value of the audience or issuer, and then access the route again.

        The value of the token, and the reasons that the token is invalid, are displayed.

## More information

[org.forgerock.openig.filter.oauth2.client.IdTokenValidationFilterHeaplet](../_attachments/apidocs/org/forgerock/openig/filter/oauth2/client/IdTokenValidationFilterHeaplet.html)

[org.forgerock.openig.filter.jwt.JwtValidationContext](../_attachments/apidocs/org/forgerock/openig/filter/jwt/JwtValidationContext.html)

[org.forgerock.openig.filter.jwt.JwtValidationErrorContext](../_attachments/apidocs/org/forgerock/openig/filter/jwt/JwtValidationErrorContext.html)

[OpenID Connect Core 1.0 incorporating errata set 1](https://openid.net/specs/openid-connect-core-1_0.html)
