PingGateway 2024.9

Cache access tokens

This section builds on the example in Validate access tokens with introspection to cache and then revoke access tokens.

When the access token is not cached, PingGateway calls AM to validate the access token. When the access token is cached, PingGateway doesn’t validate the access token with AM.

When an access token is revoked on AM, the CacheAccessTokenResolver can delete the token from the cache when both of the following conditions are true:

  • The notification property of AmService is enabled.

  • The delegate AccessTokenResolver provides the token metadata required to update the cache.

When a refresh_token is revoked on AM, all associated access tokens are automatically and immediately revoked.

Before you start, set up and test the example in Validate access tokens with introspection.

  1. Add the following route to PingGateway:

    • Linux

    • Windows

    $HOME/.openig/config/routes/rs-introspect-cache.json
    %appdata%\OpenIG\config\routes\rs-introspect-cache.json
    {
      "name": "rs-introspect-cache",
      "baseURI": "http://app.example.com:8081",
      "condition": "${find(request.uri.path, '^/rs-introspect-cache$')}",
      "heap": [
        {
          "name": "SystemAndEnvSecretStore-1",
          "type": "SystemAndEnvSecretStore"
        },
        {
          "name": "AmService-1",
          "type": "AmService",
          "config": {
            "url": "http://am.example.com:8088/openam",
            "realm": "/",
            "agent" : {
              "username" : "ig_agent",
              "passwordSecretId" : "agent.secret.id"
            },
            "secretsProvider": "SystemAndEnvSecretStore-1"
          }
        }
      ],
      "handler": {
        "type": "Chain",
        "config": {
          "filters": [
            {
              "name": "OAuth2ResourceServerFilter-1",
              "type": "OAuth2ResourceServerFilter",
              "config": {
                "scopes": [
                  "mail",
                  "employeenumber"
                ],
                "requireHttps": false,
                "realm": "OpenIG",
                "accessTokenResolver": {
                  "name": "CacheAccessTokenResolver-1",
                  "type": "CacheAccessTokenResolver",
                  "config": {
                    "enabled": true,
                    "defaultTimeout ": "1 hour",
                    "maximumTimeToCache": "1 day",
                    "amService":"AmService-1",
                    "delegate": {
                      "name": "TokenIntrospectionAccessTokenResolver-1",
                      "type": "TokenIntrospectionAccessTokenResolver",
                      "config": {
                        "amService": "AmService-1",
                        "providerHandler": {
                          "type": "Chain",
                          "config": {
                            "filters": [
                              {
                                "type": "HttpBasicAuthenticationClientFilter",
                                "config": {
                                  "username": "ig_agent",
                                  "passwordSecretId": "agent.secret.id",
                                  "secretsProvider": "SystemAndEnvSecretStore-1"
                                }
                              }
                            ],
                            "handler": {
                              "type": "Delegate",
                              "capture": "all",
                              "config": {
                                "delegate": "ForgeRockClientHandler"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          ],
          "handler": {
            "type": "StaticResponseHandler",
            "config": {
              "status": 200,
              "headers": {
                "Content-Type": [ "text/html; charset=UTF-8" ]
              },
              "entity": "<html><body><h2>Decoded access_token: ${contexts.oauth2.accessToken.info}</h2></body></html>"
            }
          }
        }
      }
    }

    Notice the following features of the route compared to rs-introspect.json, in Validate access tokens with introspection:

    • The OAuth2ResourceServerFilter uses a CacheAccessTokenResolver to cache the access token, and then delegate token resolution to the TokenIntrospectionAccessTokenResolver.

    • The amService property in CacheAccessTokenResolver enables WebSocket notifications from AM, for events such as token revocation.

    • The TokenIntrospectionAccessTokenResolver uses a ForgeRockClientHandler and a capture decorator to capture PingGateway’s interactions with AM.

  2. Test token caching:

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

      $ mytoken=$(curl -s \
      --user "client-application:password" \
      --data "grant_type=password&username=demo&password=Ch4ng31t&scope=mail%20employeenumber" \
      http://am.example.com:8088/openam/oauth2/access_token | jq -r ".access_token")
    2. Access the route, using the access token returned in the previous step:

      $ curl -v \
      --cacert /path/to/secrets/ig.example.com-certificate.pem \
      --header "Authorization: Bearer ${mytoken}" \
      https://ig.example.com:8443/rs-introspect-cache
      
      {
       active = true,
       scope = employeenumber mail,
       client_id = client-application,
       user_id = demo,
       token_type = Bearer,
       exp = 158...907,
       ...
      }
    3. In the route log, note that PingGateway calls AM to introspect the access token:

      POST http://am.example.com:8088/openam/oauth2/realms/root/introspect HTTP/1.1
    4. Access the route again. In the route log note that this time PingGateway doesn’t call AM, because the token is cached.

    5. Disable the cache and repeat the previous steps to cause PingGateway to call AM to validate the access token for each request.

  3. Test token revocation:

    1. In a terminal window, use a curl command similar to the following to revoke the access token obtained in the previous step:

      $ curl --request POST \
      --data "token=${mytoken}" \
      --data "client_id=client-application" \
      --data "client_secret=password" \
      "http://am.example.com:8088/openam/oauth2/realms/root/token/revoke"
    2. Access the route using the access token and and note that the request isn’t authorized because the token is revoked:

      $ curl -v \
      --cacert /path/to/secrets/ig.example.com-certificate.pem \
      --header "Authorization: Bearer ${mytoken}" \
      https://ig.example.com:8443/rs-introspect-cache
      
      ...
      HTTP/1.1 401 Unauthorized