---
title: ID token validation with PingAM
description: This page uses an IdTokenValidationFilter to validate an ID token.
component: pinggateway
version: 2026
page_id: pinggateway:gateway-guide:validate-idtoken
canonical_url: https://docs.pingidentity.com/pinggateway/2026/gateway-guide/validate-idtoken.html
revdate: 2025-07-07T16:48:22Z
---

# ID token validation with PingAM

This page uses an [IdTokenValidationFilter](../reference/IdTokenValidationFilter.html) to validate an ID token.

1. Set up AM:

   1. Set up AM as described in [Validating PingAM access tokens with introspection](oauth2-rs-introspect.html).

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

2. Set up PingGateway:

   1. Add the following route to PingGateway:

      * Linux

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

      * Windows

        `%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>"
              }
            }
          }
        }
      }
      ```

      Source: [idtokenvalidation.json](../_attachments/config/routes/idtokenvalidation.json)

      Notice the following features of the route:

      * The route matches requests to `/idtokenvalidation`.

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

      * The property `verificationSecretId` is configured with an arbitrary value. If this property isn't configured, the filter doesn't verify the signature of tokens.

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

        * If the value of a `kid` in the JWK set matches a `kid` in 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:

      ```console
      $ 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
      ```

      Output

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

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

   3. In your browser's privacy or incognito mode, go to <https://ig.example.com:8443/idtokenvalidation>.

      The validated token is displayed.

   4. 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.
