---
title: Passing PingAM data along the chain
description: Retrieve user profile attributes of an AM user, and provide them in the UserProfileContext to downstream filters and handlers. Profile attributes that are enabled in AM can be retrieved, except the roles attribute.
component: pinggateway
version: 2026
page_id: pinggateway:gateway-guide:data-downstream
canonical_url: https://docs.pingidentity.com/pinggateway/2026/gateway-guide/data-downstream.html
revdate: 2025-04-01T17:53:34Z
keywords: ["Configuration", "Single Sign-on (SSO)", "OAuth 2.0", "Sessions", "Passwords", "JSON"]
section_ids:
  profile-am: Pass profile data downstream
  UserProfileFilter-retrieve-attrib: Retrieve profile attributes for a user authenticated with an SSO token
  UserProfileFilter-retrieve-username-sessinfo: Retrieve a username from the sessionInfo context
  UserProfileFilter-retrieve-username-oauth2: Retrieve a username from the OAuth2Context
  runtime: Passing runtime data downstream
  runtime-signed-pem-example: Pass runtime data in a JWT signed with a PEM
  runtime-sign-then-encrypt: Pass runtime data in a JWT signed with PEM then encrypted with a symmetric key
  runtime-encrypt-symm: Pass runtime data in JWT encrypted with a symmetric key
  runtime-encrypt-asymm: Pass runtime data in JWT encrypted with an asymmetric key
---

# Passing PingAM data along the chain

## Pass profile data downstream

Retrieve user profile attributes of an AM user, and provide them in the UserProfileContext to downstream filters and handlers. Profile attributes that are enabled in AM can be retrieved, except the `roles` attribute.

The `userProfile` property of AmService is configured to retrieve `employeeNumber` and `mail`. When the property is not configured, all available attributes in `rawInfo` or `asJsonValue()` are displayed.

### Retrieve profile attributes for a user authenticated with an SSO token

In this example, the user is authenticated with AM through the SingleSignOnFilter, which stores the SSO token and its validation information in the `SsoTokenContext`. The UserProfileFilter retrieves the user's mail and employee number, as well as the `username`, `_id`, and `_rev`, from that context.

1. Set up AM:

   1. Select Services > Add a Service and add a Validation Service with the following Valid goto URL Resources:

      * `https://ig.example.com:8443/*`

      * `https://ig.example.com:8443/*?*`

   2. Register a PingGateway agent with the following values, as described in [Register a PingGateway agent in AM](preface.html#register-agent-am):

      * Agent ID: `ig_agent`

      * Password: `password`

        |   |                                                                                                                   |
        | - | ----------------------------------------------------------------------------------------------------------------- |
        |   | Use secure passwords in a production environment. Consider using a password manager to generate secure passwords. |

   3. Select Services > Add a Service, and add a Validation Service with the following Valid goto URL Resources:

      * `http://ig.example.com:8080/*`

      * `http://ig.example.com:8080/?`

2. Set up PingGateway:

   1. Set up PingGateway for HTTPS, as described in [Configure PingGateway for TLS (server-side)](../installation-guide/securing-connections.html#server-side-tls).

   2. Set an environment variable for the PingGateway agent password, and then restart PingGateway:

      ```console
      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      ```

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

   3. Add the following route to PingGateway:

      * Linux

        `$HOME/.openig/config/routes/user-profile-sso.json`

      * Windows

        `%appdata%\OpenIG\config\routes\user-profile-sso.json`

      ```json
      {
        "name": "user-profile-sso",
        "condition": "${find(request.uri.path, '^/user-profile-sso')}",
        "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",
              "amHandler": "ForgeRockClientHandler"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "SingleSignOnFilter",
                "type": "SingleSignOnFilter",
                "config": {
                  "amService": "AmService-1"
                }
              },
              {
                "name": "UserProfileFilter-1",
                "type": "UserProfileFilter",
                "config": {
                  "username": "${contexts.ssoToken.info.uid}",
                  "userProfileService": {
                    "type": "UserProfileService",
                    "config": {
                      "amService": "AmService-1",
                      "profileAttributes": [ "employeeNumber", "mail" ]
                    }
                  }
                }
              }
            ],
            "handler": {
              "type": "StaticResponseHandler",
              "config": {
                "status": 200,
                "headers": {
                  "Content-Type": [ "text/html; charset=UTF-8" ]
                },
                "entity": "<html><body>username: ${contexts.userProfile.username}<br><br>rawInfo: <pre>${contexts.userProfile.rawInfo}</pre></body></html>"
              }
            }
          }
        }
      }
      ```

      Source: [user-profile-sso.json](../_attachments/config/routes/user-profile-sso.json)

3. Test the setup:

   1. In your browser's privacy or incognito mode, go to <https://ig.example.com:8443/user-profile-sso>.

   2. Log in to AM with username `demo` and password `Ch4ng31t`.

      The UserProfileFilter retrieves the user's profile data and stores it in the UserProfileContext. The StaticResponseHandler displays the username and the profile data available in `rawInfo`:

      ```
      username: demo

      rawInfo:

      {_id=demo, _rev=-1, mail=[demo@example.com], username=demo}
      ```

### Retrieve a username from the sessionInfo context

In this example, the UserProfileFilter retrieves AM profile information for the user identified by the SessionInfoContext, at `${contexts.amSession.username}`. The SessionInfoFilter validates an SSO token without redirecting the request to an authentication page.

1. Set up AM:

   1. Select Services > Add a Service and add a Validation Service with the following Valid goto URL Resources:

      * `https://ig.example.com:8443/*`

      * `https://ig.example.com:8443/*?*`

   2. Register a PingGateway agent with the following values, as described in [Register a PingGateway agent in AM](preface.html#register-agent-am):

      * Agent ID: `ig_agent`

      * Password: `password`

        |   |                                                                                                                   |
        | - | ----------------------------------------------------------------------------------------------------------------- |
        |   | Use secure passwords in a production environment. Consider using a password manager to generate secure passwords. |

2. Set up PingGateway:

   1. Set up PingGateway for HTTPS, as described in [Configure PingGateway for TLS (server-side)](../installation-guide/securing-connections.html#server-side-tls).

   2. Set an environment variable for the PingGateway agent password, and then restart PingGateway:

      ```console
      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      ```

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

   3. Add the following route to PingGateway:

      * Linux

        `$HOME/.openig/config/routes/user-profile-ses-info.json`

      * Windows

        `%appdata%\OpenIG\config\routes\user-profile-ses-info.json`

      ```json
      {
        "name": "user-profile-ses-info",
        "condition": "${find(request.uri.path, '^/user-profile-ses-info')}",
        "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",
              "amHandler": "ForgeRockClientHandler"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "capture": "all",
          "config": {
            "filters": [
              {
                "name": "SessionInfoFilter-1",
                "type": "SessionInfoFilter",
                "config": {
                  "amService": "AmService-1"
                }
              },
              {
                "name": "UserProfileFilter-1",
                "type": "UserProfileFilter",
                "config": {
                  "username": "${contexts.amSession.username}",
                  "userProfileService": {
                    "type": "UserProfileService",
                    "config": {
                      "amService": "AmService-1",
                      "profileAttributes": [ "employeeNumber", "mail" ]
                    }
                  }
                }
              }
            ],
            "handler": {
              "type": "StaticResponseHandler",
              "config": {
                "status": 200,
                "headers": {
                  "Content-Type": [ "application/json" ]
                },
                "entity": "{ \"username\": \"${contexts.userProfile.username}\", \"user_profile\":  ${contexts.userProfile.asJsonValue()} }"
              }
            }
          }
        }
      }
      ```

      Source: [user-profile-ses-info.json](../_attachments/config/routes/user-profile-ses-info.json)

3. Test the setup:

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

      ```console
      $ curl --request POST \
      --url http://am.example.com:8088/openam/json/realms/root/authenticate \
      --header 'accept-api-version: resource=2.0' \
      --header 'content-type: application/json' \
      --header 'x-openam-username: demo' \
      --header 'x-openam-password: Ch4ng31t' \
      --data '{}'
      ```

      ```none
      {"tokenId":"AQI...AA*","successUrl":"/openam/console"}
      ```

   2. Access the route, providing the path to the certificate and token ID retrieved in the previous step:

      ```console
      $ curl -v \
      --cacert /path/to/secrets/ig.example.com-certificate.pem \
      --cookie 'iPlanetDirectoryPro=tokenID'  \
      https://ig.example.com:8443/user-profile-ses-info | jq .
      ```

      ```none
      {
        "username": "demo",
        "user_profile": {
          "_id": "demo",
          "_rev": "123...456",
          "employeeNumber": ["123"],
          "mail": ["demo@example.com"],
          "username": "demo"
        }
      }
      ```

      iPlanetDirectoryPro is the name of the AM session cookie. For more information, refer to [Find the AM session cookie name](preface.html#am-session-cookie).

      The UserProfileFilter retrieves the user's profile data and stores it in the UserProfileContext. The StaticResponseHandler displays the username and the profile data available in `asJsonValue()`.

### Retrieve a username from the OAuth2Context

In this example, the OAuth2ResourceServerFilter validates a request containing an OAuth 2.0 access token, using the introspection endpoint, and injects the token into the OAuth2Context context. The UserProfileFilter retrieves AM profile information for the user identified by this context.

Before you start, set up and test the example in [Validating PingAM access tokens with introspection](oauth2-rs-introspect.html).

1. Add the following route to PingGateway:

   * Linux

     `$HOME/.openig/config/routes/user-profile-oauth.json`

   * Windows

     `%appdata%\OpenIG\config\routes\user-profile-oauth.json`

   ```json
   {
     "name": "user-profile-oauth",
     "condition": "${find(request.uri.path, '^/user-profile-oauth')}",
     "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",
           "amHandler": "ForgeRockClientHandler"
         }
       }
     ],
     "handler": {
       "type": "Chain",
       "config": {
         "filters": [
           {
             "name": "OAuth2ResourceServerFilter-1",
             "type": "OAuth2ResourceServerFilter",
             "config": {
               "scopes": [
                 "mail",
                 "employeenumber"
               ],
               "requireHttps": false,
               "accessTokenResolver": {
                 "name": "token-resolver-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": "ForgeRockClientHandler"
                     }
                   }
                 }
               }
             }
           },
           {
             "name": "UserProfileFilter-1",
             "type": "UserProfileFilter",
             "config": {
               "username": "${contexts.oauth2.accessToken.info.sub}",
               "userProfileService": {
                 "type": "UserProfileService",
                 "config": {
                   "amService": "AmService-1",
                   "profileAttributes": [ "employeeNumber", "mail" ]
                 }
               }
             }
           }
         ],
         "handler": {
           "type": "StaticResponseHandler",
           "config": {
             "status": 200,
             "headers": {
               "Content-Type": [ "application/json" ]
             },
             "entity": "{ \"username\": \"${contexts.userProfile.username}\", \"user_profile\":  ${contexts.userProfile.asJsonValue()} }"
           }
         }
       }
     }
   }
   ```

   Source: [user-profile-oauth.json](../_attachments/config/routes/user-profile-oauth.json)

2. Test the setup:

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

      ```none
      $ 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. Validate the access token returned in the previous step:

      ```none
      $ curl -v \
      --cacert /path/to/secrets/ig.example.com-certificate.pem \
      --header "Authorization: Bearer ${mytoken}" \
      https://ig.example.com:8443/user-profile-oauth | jq .
      ```

      Output

      ```none
      {
        "username": "demo",
        "user_profile": {
          "_id": "demo",
          "_rev": "123...456",
          "employeeNumber": ["123"],
          "mail": ["demo@example.com"],
          "username": "demo"
        }
      }
      ```

      The UserProfileFilter retrieves the user's profile data and stores it in the UserProfileContext. The StaticResponseHandler displays the username and the profile data that is available in `asJsonValue()`.

## Passing runtime data downstream

The following sections describe how to pass identity or other runtime information in a JWT, downstream to a protected application:

The examples in this section use the following objects:

* [JwtBuilderFilter](../reference/JwtBuilderFilter.html) to collect runtime information and pack it into a JWT

* [HeaderFilter](../reference/HeaderFilter.html) to add the information to the forwarded request

To help with development, the sample application includes a `/jwt` endpoint to display the JWT, verify its signature, and decrypt the JWT.

### Pass runtime data in a JWT signed with a PEM

1. Set up secrets

   1. Locate a directory for secrets and go to it:

      ```console
      $ cd /path/to/secrets
      ```

   2. Generate PEM files to sign and verify the JWT:

      ```console
      $ openssl req \
      -newkey rsa:2048 \
      -new \
      -nodes \
      -x509 \
      -days 3650 \
      -subj "/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
      -keyout id.key.for.signing.jwt.pem \
      -out id.key.for.verifying.jwt.pem
      ```

2. Set up AM:

   1. Select Services > Add a Service and add a Validation Service with the following Valid goto URL Resources:

      * `https://ig.example.com:8443/*`

      * `https://ig.example.com:8443/*?*`

   2. Register a PingGateway agent with the following values, as described in [Register a PingGateway agent in AM](preface.html#register-agent-am):

      * Agent ID: `ig_agent`

      * Password: `password`

        |   |                                                                                                                   |
        | - | ----------------------------------------------------------------------------------------------------------------- |
        |   | Use secure passwords in a production environment. Consider using a password manager to generate secure passwords. |

3. Set up PingGateway:

   1. Set up PingGateway for HTTPS, as described in [Configure PingGateway for TLS (server-side)](../installation-guide/securing-connections.html#server-side-tls).

   2. Set an environment variable for the PingGateway agent password, and then restart PingGateway:

      ```console
      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      ```

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

   3. Make sure PingGateway connects to the sample application over HTTPS with a route to access static resources.

      Learn more in [Using the sample application](../getting-started/start-sampleapp.html).

   4. Add the following route to PingGateway, replacing value of the property `secretsDir` with the directory for the PEM file:

      * Linux

        `$HOME/.openig/config/routes/jwt-builder-sign-pem.json`

      * Windows

        `%appdata%\OpenIG\config\routes\jwt-builder-sign-pem.json`

      ```json
      {
        "name": "jwt-builder-sign-pem",
        "condition": "${find(request.uri.path, '/jwt-builder-sign-pem')}",
        "baseURI": "https://app.example.com:8444",
        "properties": {
          "secretsDir": "/path/to/secrets"
        },
        "capture": "all",
        "heap": [
          {
            "name": "pemPropertyFormat",
            "type": "PemPropertyFormat"
          },
          {
            "name": "FileSystemSecretStore-1",
            "type": "FileSystemSecretStore",
            "config": {
              "format": "PLAIN",
              "directory": "&{secretsDir}",
              "suffix": ".pem",
              "mappings": [{
                "secretId": "id.key.for.signing.jwt",
                "format": "pemPropertyFormat"
              }]
            }
          },
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "url": "http://am.example.com:8088/openam"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [{
              "name": "SingleSignOnFilter",
              "type": "SingleSignOnFilter",
              "config": {
                "amService": "AmService-1"
              }
            }, {
              "name": "UserProfileFilter",
              "type": "UserProfileFilter",
              "config": {
                "username": "${contexts.ssoToken.info.uid}",
                "userProfileService": {
                  "type": "UserProfileService",
                  "config": {
                    "amService": "AmService-1"
                  }
                }
              }
            }, {
              "name": "JwtBuilderFilter-1",
              "type": "JwtBuilderFilter",
              "config": {
                "template": {
                  "name": "${contexts.userProfile.commonName}",
                  "email": "${contexts.userProfile.rawInfo.mail[0]}"
                },
                "secretsProvider": "FileSystemSecretStore-1",
                "signature": {
                  "secretId": "id.key.for.signing.jwt",
                  "algorithm": "RS512"
                }
              }
            }, {
              "name": "HeaderFilter-1",
              "type": "HeaderFilter",
              "config": {
                "messageType": "REQUEST",
                "add": {
                  "x-openig-user": ["${contexts.jwtBuilder.value}"]
                }
              }
            }],
            "handler": "ReverseProxyHandler"
          }
        }
      }
      ```

      Source: [jwt-builder-sign-pem.json](../_attachments/config/routes/jwt-builder-sign-pem.json)

      Notice the following features of the route:

      * The route matches requests to `/jwt-builder-sign-pem`.

      * The agent password for AmService is provided by a SystemAndEnvSecretStore.

      * If the request doesn't have a valid AM session cookie, the SingleSignOnFilter redirects the request to authenticate with AM. If the request already has a valid AM session cookie, the SingleSignOnFilter passes the request to the next filter, and stores the cookie value in an SsoTokenContext.

      * The UserProfileFilter reads the username from the SsoTokenContext, uses it to retrieve the user's profile info from AM, and places the data into the UserProfileContext.

      * The JwtBuilderFilter refers to the secret ID of the PEM, and uses the FileSystemSecretStore to manage the secret.

      * The FileSystemSecretStore mapping refers to the secret ID of the PEM, and uses the PemPropertyFormat to define the format.

      * The HeaderFilter retrieves the JWT from the JwtBuilderContext, and adds it to the header field `x-openig-user` in the request, so that the sample app can display the JWT.

      * The ClientHandler passes the request to the sample app, which displays the JWT.

4. Test the setup:

   1. In your browser's privacy or incognito mode, go to <https://ig.example.com:8443/jwt-builder-sign-pem> and accept the server certificate.

   2. Sign on to AM as user `demo`, password `Ch4ng31t`. The sample application displays the signed JWT along with its header and payload.

   3. In `USE PEM FILE` in the sample app, enter the path to `id.key.for.verifying.jwt.pem` to verify the JWT signature.

### Pass runtime data in a JWT signed with PEM then encrypted with a symmetric key

This example passes runtime data in a JWT that is signed with a PEM, and then encrypted with a symmetric key.

1. Set up secrets

   1. Locate a directory for secrets and go to it:

      ```console
      $ cd /path/to/secrets
      ```

   2. From the secrets directory, generate PEM files to sign and verify the JWT:

      ```none
      $ openssl req \
      -newkey rsa:2048 \
      -new \
      -nodes \
      -x509 \
      -days 3650 \
      -subj "/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
      -keyout id.key.for.signing.jwt.pem \
      -out id.key.for.verifying.jwt.pem
      ```

   3. Encrypt the PEM file used to sign the JWT:

      ```console
      $ openssl pkcs8 \
      -topk8 \
      -inform PEM \
      -outform PEM \
      -in id.key.for.signing.jwt.pem \
      -out id.encrypted.key.for.signing.jwt.pem \
      -passout pass:encryptedpassword \
      -v1 PBE-SHA1-3DES
      ```

      The encrypted PEM file used for signatures is `id.encrypted.key.for.signing.jwt.pem`. The password to decode the file is `encryptedpassword`.

      |   |                                                                                                                      |
      | - | -------------------------------------------------------------------------------------------------------------------- |
      |   | If encryption fails, make sure your encryption methods and ciphers are supported by the Java Cryptography Extension. |

   4. Generate a symmetric key to encrypt the JWT:

      ```console
      $ openssl rand -base64 32 > symmetric.key.for.encrypting.jwt
      ```

   5. Make sure you have the following keys in your secrets directory:

      * `id.encrypted.key.for.signing.jwt.pem`

      * `id.key.for.signing.jwt.pem`

      * `id.key.for.verifying.jwt.pem`

      * `symmetric.key.for.encrypting.jwt`

2. Set up AM:

   1. Select Services > Add a Service and add a Validation Service with the following Valid goto URL Resources:

      * `https://ig.example.com:8443/*`

      * `https://ig.example.com:8443/*?*`

   2. Register a PingGateway agent with the following values, as described in [Register a PingGateway agent in AM](preface.html#register-agent-am):

      * Agent ID: `ig_agent`

      * Password: `password`

        |   |                                                                                                                   |
        | - | ----------------------------------------------------------------------------------------------------------------- |
        |   | Use secure passwords in a production environment. Consider using a password manager to generate secure passwords. |

3. Set up PingGateway:

   1. Set up PingGateway for HTTPS, as described in [Configure PingGateway for TLS (server-side)](../installation-guide/securing-connections.html#server-side-tls).

   2. Set an environment variable for the PingGateway agent password, and then restart PingGateway:

      ```console
      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      ```

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

   3. Make sure PingGateway connects to the sample application over HTTPS with a route to access static resources.

      Learn more in [Using the sample application](../getting-started/start-sampleapp.html).

   4. In PingGateway, create an environment variable for the base64-encoded password to decrypt the PEM file used to sign the JWT:

      ```console
      $ export ID_DECRYPTED_KEY_FOR_SIGNING_JWT='ZW5jcnlwdGVkcGFzc3dvcmQ='
      ```

   5. Add the following route to PingGateway, replacing the value of `secretsDir` with your secrets directory:

      * Linux

        `$HOME/.openig/config/routes/jwtbuilder-sign-then-encrypt.json`

      * Windows

        `%appdata%\OpenIG\config\routes\jwtbuilder-sign-then-encrypt.json`

      ```json
      {
        "name": "jwtbuilder-sign-then-encrypt",
        "condition": "${find(request.uri.path, '/jwtbuilder-sign-then-encrypt')}",
        "baseURI": "https://app.example.com:8444",
        "properties": {
          "secretsDir": "/path/to/secrets"
        },
        "capture": "all",
        "heap": [
          {
            "name": "SystemAndEnvSecretStore",
            "type": "SystemAndEnvSecretStore",
            "config": {
              "mappings": [{
                "secretId": "id.decrypted.key.for.signing.jwt",
                "format": "BASE64"
              }]
            }
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore",
              "url": "http://am.example.com:8088/openam"
            }
          },
          {
            "name": "pemPropertyFormat",
            "type": "PemPropertyFormat",
            "config": {
              "decryptionSecretId": "id.decrypted.key.for.signing.jwt",
              "secretsProvider": "SystemAndEnvSecretStore"
            }
          },
          {
            "name": "FileSystemSecretStore-1",
            "type": "FileSystemSecretStore",
            "config": {
              "format": "PLAIN",
              "directory": "&{secretsDir}",
              "mappings": [{
                "secretId": "id.encrypted.key.for.signing.jwt.pem",
                "format": "pemPropertyFormat"
              }, {
                "secretId": "symmetric.key.for.encrypting.jwt",
                "format": {
                  "type": "SecretKeyPropertyFormat",
                  "config": {
                    "format": "BASE64",
                    "algorithm": "AES"
                  }
                }
              }]
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [{
              "name": "SingleSignOnFilter",
              "type": "SingleSignOnFilter",
              "config": {
                "amService": "AmService-1"
              }
            }, {
              "name": "UserProfileFilter",
              "type": "UserProfileFilter",
              "config": {
                "username": "${contexts.ssoToken.info.uid}",
                "userProfileService": {
                  "type": "UserProfileService",
                  "config": {
                    "amService": "AmService-1"
                  }
                }
              }
            }, {
              "name": "JwtBuilderFilter-1",
              "type": "JwtBuilderFilter",
              "config": {
                "template": {
                  "name": "${contexts.userProfile.commonName}",
                  "email": "${contexts.userProfile.rawInfo.mail[0]}"
                },
                "secretsProvider": "FileSystemSecretStore-1",
                "signature": {
                  "secretId": "id.encrypted.key.for.signing.jwt.pem",
                  "algorithm": "RS512",
                  "encryption": {
                    "secretId": "symmetric.key.for.encrypting.jwt",
                    "algorithm": "dir",
                    "method": "A128CBC-HS256"
                  }
                }
              }
            }, {
              "name": "AddBuiltJwtToHeader",
              "type": "HeaderFilter",
              "config": {
                "messageType": "REQUEST",
                "add": {
                  "x-openig-user": ["${contexts.jwtBuilder.value}"]
                }
              }
            },
              {
                "name": "AddBuiltJwtAsCookie",
                "type": "HeaderFilter",
                "config": {
                  "messageType": "RESPONSE",
                  "add": {
                    "set-cookie": ["my-jwt=${contexts.jwtBuilder.value};PATH=/"]
                  }
                }
              }],
            "handler": "ReverseProxyHandler"
          }
        }
      }
      ```

      Source: [jwtbuilder-sign-then-encrypt.json](../_attachments/config/routes/jwtbuilder-sign-then-encrypt.json)

      Notice the following features of the route:

      * The route matches requests to `/jwtbuilder-sign-then-encrypt`.

      * The SystemAndEnvSecretStore provides the PingGateway agent password and the password to decode the PEM file for the signing keys.

      * The FileSystemSecretStore maps the secret IDs of the encrypted PEM file used to sign the JWT, and the symmetric key used to encrypt the JWT.

      * After authentication, the UserProfileFilter reads the username from the SsoTokenContext, uses it to retrieve the user's profile info from AM, and places the data into the UserProfileContext.

      * The JwtBuilderFilter takes the username and email from the UserProfileContext, and stores them in a JWT in the JwtBuilderContext. It uses the secrets mapped in the FileSystemSecretStore to sign then encrypt the JWT.

      * The `AddBuiltJwtToHeader` HeaderFilter retrieves the JWT from the JwtBuilderContext, and adds it to the header field `x-openig-user` in the request so that the sample app can display the JWT.

      * The `AddBuiltJwtAsCookie` HeaderFilter adds the JWT to a cookie called `my-jwt` so that it can be retrieved by the JwtValidationFilter in [JWT validation with PingAM](validate-jwt.html). The cookie is ignored in this example.

      * The ClientHandler passes the request to the sample app.

4. Test the setup:

   1. In your browser's privacy or incognito mode, go to <https://ig.example.com:8443/jwtbuilder-sign-then-encrypt> and accept the server certificate.

   2. Sign on to AM as user `demo`, password `Ch4ng31t`. The sample app displays the encrypted JWT. The payload is concealed because the JWT is encrypted.

   3. In the `ENTER SECRET` box, enter the value of `symmetric.key.for.encrypting.jwt` to decrypt the JWT. The signed JWT and its payload are now displayed.

   4. In the `USE PEM FILE` box, enter the path to `id.key.for.verifying.jwt.pem` to verify the JWT signature.

### Pass runtime data in JWT encrypted with a symmetric key

1. Set up secrets:

   1. Locate a directory for secrets and go to it:

      ```console
      $ cd /path/to/secrets
      ```

   2. In the secrets folder, generate an AES 256-bit key:

      ```none
      $ openssl rand -base64 32
      ```

      Output

      ```
      loH...UFQ=
      ```

   3. In the secrets folder, create a file called `symmetric.key.for.encrypting.jwt` containing the AES key:

      ```none
      $ echo -n 'loH...UFQ=' > symmetric.key.for.encrypting.jwt
      ```

      Make sure the password file contains only the password, with no trailing spaces or carriage returns.

2. Set up AM:

   1. Select Services > Add a Service and add a Validation Service with the following Valid goto URL Resources:

      * `https://ig.example.com:8443/*`

      * `https://ig.example.com:8443/*?*`

   2. Register a PingGateway agent with the following values, as described in [Register a PingGateway agent in AM](preface.html#register-agent-am):

      * Agent ID: `ig_agent`

      * Password: `password`

        |   |                                                                                                                   |
        | - | ----------------------------------------------------------------------------------------------------------------- |
        |   | Use secure passwords in a production environment. Consider using a password manager to generate secure passwords. |

3. Set up PingGateway:

   1. Set up PingGateway for HTTPS, as described in [Configure PingGateway for TLS (server-side)](../installation-guide/securing-connections.html#server-side-tls).

   2. Set an environment variable for the PingGateway agent password, and then restart PingGateway:

      ```console
      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      ```

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

   3. Make sure PingGateway connects to the sample application over HTTPS with a route to access static resources.

      Learn more in [Using the sample application](../getting-started/start-sampleapp.html).

   4. Add the following route to PingGateway, replacing the value of the property `secretsDir` with your value:

      * Linux

        `$HOME/.openig/config/routes/jwtbuilder-encrypt-symmetric.json`

      * Windows

        `%appdata%\OpenIG\config\routes\jwtbuilder-encrypt-symmetric.json`

      ```json
      {
        "name": "jwtbuilder-encrypt-symmetric",
        "condition": "${find(request.uri.path, '/jwtbuilder-encrypt-symmetric')}",
        "baseURI": "https://app.example.com:8444",
        "properties": {
          "secretsDir": "/path/to/secrets"
        },
        "heap": [
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "url": "http://am.example.com:8088/openam"
            }
          },
          {
            "name": "FileSystemSecretStore-1",
            "type": "FileSystemSecretStore",
            "config": {
              "format": "PLAIN",
              "directory": "&{secretsDir}",
              "mappings": [{
                "secretId": "symmetric.key.for.encrypting.jwt",
                "format": {
                  "type": "SecretKeyPropertyFormat",
                  "config": {
                    "format": "BASE64",
                    "algorithm": "AES"
                  }
                }
              }]
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [{
              "name": "SingleSignOnFilter-1",
              "type": "SingleSignOnFilter",
              "config": {
                "amService": "AmService-1"
              }
            }, {
              "name": "UserProfileFilter-1",
              "type": "UserProfileFilter",
              "config": {
                "username": "${contexts.ssoToken.info.uid}",
                "userProfileService": {
                  "type": "UserProfileService",
                  "config": {
                    "amService": "AmService-1"
                  }
                }
              }
            }, {
              "name": "JwtBuilderFilter-1",
              "type": "JwtBuilderFilter",
              "config": {
                "template": {
                  "name": "${contexts.userProfile.commonName}",
                  "email": "${contexts.userProfile.rawInfo.mail[0]}"
                },
                "secretsProvider": "FileSystemSecretStore-1",
                "encryption": {
                  "secretId": "symmetric.key.for.encrypting.jwt",
                  "algorithm": "dir",
                  "method": "A128CBC-HS256"
                }
              }
            }, {
              "name": "HeaderFilter-1",
              "type": "HeaderFilter",
              "config": {
                "messageType": "REQUEST",
                "add": {
                  "x-openig-user": ["${contexts.jwtBuilder.value}"]
                }
              }
            }],
            "handler": "ReverseProxyHandler"
          }
        }
      }
      ```

      Source: [jwtbuilder-encrypt-symmetric.json](../_attachments/config/routes/jwtbuilder-encrypt-symmetric.json)

      Notice the following features of the route:

      * The route matches requests to `/jwtbuilder-encrypt-symmetric`.

      * The JWT encryption key is managed by the FileSystemSecretStore in the heap, which defines the SecretKeyPropertyFormat.

      * The JwtBuilderFilter `encryption` property refers to key in the FileSystemSecretStore.

      * The HeaderFilter retrieves the JWT from the JwtBuilderContext, and adds it to the header field `x-openig-user` in the request, so that the sample app can display the JWT.

4. Test the setup:

   1. In your browser's privacy or incognito mode, go to <https://ig.example.com:8443/jwtbuilder-encrypt-symmetric> and accept the server certificate.

   2. Sign on to AM as user `demo`, password `Ch4ng31t`, or as another user. The JWT is displayed in the sample app.

   3. In the `ENTER SECRET` field, enter the value of the AES 256-bit key to decrypt the JWT and display its payload.

### Pass runtime data in JWT encrypted with an asymmetric key

The asymmetric key in this example is a PEM, but you can equally use a keystore.

1. Set up secrets:

   1. Locate a directory for secrets and go to it:

      ```console
      $ cd /path/to/secrets
      ```

   2. Generate an encrypted PEM file:

      ```console
      $ openssl req \
      -newkey rsa:2048 \
      -new \
      -nodes \
      -x509 \
      -days 3650 \
      -subj "/CN=ig.example.com/OU=example/O=com/L=fr/ST=fr/C=fr" \
      -keyout id.key.for.encrypting.jwt.pem \
      -out id.key.for.decrypting.jwt.pem
      ```

2. Set up AM:

   1. Select Services > Add a Service and add a Validation Service with the following Valid goto URL Resources:

      * `https://ig.example.com:8443/*`

      * `https://ig.example.com:8443/*?*`

   2. Register a PingGateway agent with the following values, as described in [Register a PingGateway agent in AM](preface.html#register-agent-am):

      * Agent ID: `ig_agent`

      * Password: `password`

        |   |                                                                                                                   |
        | - | ----------------------------------------------------------------------------------------------------------------- |
        |   | Use secure passwords in a production environment. Consider using a password manager to generate secure passwords. |

3. Set up PingGateway:

   1. Set up PingGateway for HTTPS, as described in [Configure PingGateway for TLS (server-side)](../installation-guide/securing-connections.html#server-side-tls).

   2. Set an environment variable for the PingGateway agent password, and then restart PingGateway:

      ```console
      $ export AGENT_SECRET_ID='cGFzc3dvcmQ='
      ```

      The password is retrieved by a SystemAndEnvSecretStore, and must be base64-encoded.

   3. Make sure PingGateway connects to the sample application over HTTPS with a route to access static resources.

      Learn more in [Using the sample application](../getting-started/start-sampleapp.html).

   4. Add the following route to PingGateway, replacing value of the property `secretsDir` with the directory for the PEM file:

      * Linux

        `$HOME/.openig/config/routes/jwtbuilder-encrypt-asymmetric.json`

      * Windows

        `%appdata%\OpenIG\config\routes\jwtbuilder-encrypt-asymmetric.json`

      ```json
      {
        "name": "jwtbuilder-encrypt-asymmetric",
        "condition": "${find(request.uri.path, '/jwtbuilder-encrypt-asymmetric')}",
        "baseURI": "https://app.example.com:8444",
        "properties": {
          "secretsDir": "/path/to/secrets"
        },
        "capture": "all",
        "heap": [
          {
            "name": "pemPropertyFormat",
            "type": "PemPropertyFormat"
          },
          {
            "name": "FileSystemSecretStore-1",
            "type": "FileSystemSecretStore",
            "config": {
              "format": "PLAIN",
              "directory": "&{secretsDir}",
              "suffix": ".pem",
              "mappings": [{
                "secretId": "id.key.for.decrypting.jwt",
                "format": "pemPropertyFormat"
              }]
            }
          },
          {
            "name": "SystemAndEnvSecretStore-1",
            "type": "SystemAndEnvSecretStore"
          },
          {
            "name": "AmService-1",
            "type": "AmService",
            "config": {
              "agent": {
                "username": "ig_agent",
                "passwordSecretId": "agent.secret.id"
              },
              "secretsProvider": "SystemAndEnvSecretStore-1",
              "url": "http://am.example.com:8088/openam"
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [{
              "name": "SingleSignOnFilter",
              "type": "SingleSignOnFilter",
              "config": {
                "amService": "AmService-1"
              }
            }, {
              "name": "UserProfileFilter",
              "type": "UserProfileFilter",
              "config": {
                "username": "${contexts.ssoToken.info.uid}",
                "userProfileService": {
                  "type": "UserProfileService",
                  "config": {
                    "amService": "AmService-1"
                  }
                }
              }
            }, {
              "name": "JwtBuilderFilter-1",
              "type": "JwtBuilderFilter",
              "config": {
                "template": {
                  "name": "${contexts.userProfile.commonName}",
                  "email": "${contexts.userProfile.rawInfo.mail[0]}"
                },
                "secretsProvider": "FileSystemSecretStore-1",
                "encryption": {
                  "secretId": "id.key.for.decrypting.jwt",
                  "algorithm": "RSA-OAEP-256",
                  "method": "A128CBC-HS256"
                }
              }
            }, {
              "name": "HeaderFilter-1",
              "type": "HeaderFilter",
              "config": {
                "messageType": "REQUEST",
                "add": {
                  "x-openig-user": ["${contexts.jwtBuilder.value}"]
                }
              }
            }],
            "handler": "ReverseProxyHandler"
          }
        }
      }
      ```

      Source: [jwtbuilder-encrypt-asymmetric.json](../_attachments/config/routes/jwtbuilder-encrypt-asymmetric.json)

      Notice the following features of the route:

      * The route matches requests to `/jwtbuilder-encrypt-asymmetric`.

      * The JwtBuilderFilter refers to the secret ID of the PEM, and uses the FileSystemSecretStore to manage the secret.

      * The FileSystemSecretStore mapping refers to the secret ID of the PEM, and uses the default PemPropertyFormat.

      * The HeaderFilter retrieves the JWT from the JwtBuilderContext, and adds it to the header field `x-openig-user` in the request, so that the sample app can display the JWT.

4. Test the setup:

   1. In your browser's privacy or incognito mode, go to <https://ig.example.com:8443/jwtbuilder-encrypt-asymmetric> and accept the server certificate.

   2. Sign on to AM as user `demo`, password `Ch4ng31t`, or as another user. The JWT is displayed in the sample app.

   3. In the `USE PEM FILE` field, enter the path to `id.key.for.encrypting.jwt.pem` to decrypt the JWT and display its payload.
