PingGateway 2024.9

Password replay

Password replay brings SSO to legacy web applications without the need to edit, upgrade, or recode them.

Use PingGateway with an appropriate PingOne Advanced Identity Cloud journey to capture and replay username password credentials. PingGateway and PingOne Advanced Identity Cloud share a secret key to encrypt and decrypt the password and keep it safe.

When running AM in a self-hosted deployment, also refer to Password replay from AM.

Request flow

Password replay sequence diagram
Figure 1. Password replay sequence diagram
  • PingGateway intercepts the browser’s HTTP GET request.

  • PingGateway redirects the user to the appropriate PingOne Advanced Identity Cloud journey for authentication.

  • PingOne Advanced Identity Cloud authenticates the user and stores the encrypted password in a JWT.

  • PingOne Advanced Identity Cloud redirects the browser back to the protected application with the JWT.

  • PingGateway intercepts the browser’s HTTP GET request again:

    • The user is authenticated.

    • PingGateway gets the password from the JWT and decrypts it.

    • PingGateway gets the username from PingOne Advanced Identity Cloud based on the user _id.

  • PingGateway replaces the request with an HTTP POST to the application, taking the credentials from the context.

  • The sample application validates the credentials from the HTTP POST request.

  • The sample application responds with the user’s profile page.

  • PingGateway passes the response from the sample application to the browser.

Tasks

Before you start

  • Make sure you can access the PingOne Advanced Identity Cloud tenant as an administrator.

  • Choose the realm to use in the PingOne Advanced Identity Cloud tenant.

    The following tasks use the alpha realm.

  • Prepare hostnames for PingGateway and the sample application.

    The following tasks use ig.example.com for PingGateway and app.example.com for the sample application.

Task 1: Run the sample application

  1. Download the sample application.

  2. Open a command-line window and start the sample application:

    $ java -jar PingGateway-sample-application-2024.9.0-jar-with-dependencies.jar

    The sample application runs in the foreground until you stop it.

  3. Check you can access the sample application by browsing http://app.example.com:8081/login.

    The sample application displays a login screen.

Task 2: Install PingGateway

This task installs PingGateway, but doesn’t configure it for password replay yet.

  1. Download and unpack PingGateway.

  2. Configure PingGateway for HTTPS.

  3. Set a top-level session field in admin.json alongside the heap and connections:

    "session": {
            "cookie": {
                "sameSite": "none",
                "secure": true
            }
        }

    PingOne Advanced Identity Cloud prompts the browser to send a session cookie on successful authentication.

    • When sameSite is strict or lax, the browser does not send the session cookie with the nonce used in validation. If PingGateway doesn’t find the nonce, it assumes that the authentication failed.

    • When secure is false, the browser is likely to reject the session cookie.

  4. Add a base configuration file.

    Comment captures in the handler to avoid flooding the PingGateway log, "_capture": "all".

  5. Configure static routes for use with the sample application.

  6. Check you can access the sample application through PingGateway by browsing https://ig.example.com:8443/static.

    You might have used a self-signed certificate for the PingGateway HTTPS configuration. If your browser doesn’t recognize the PingGateway certificate and flags it as a security risk, choose to continue.

    PingGateway displays a basic profile page for the demo user.

PingGateway now has the configuration required as a basis on which to build password replay.

Task 3: Register PingGateway with PingOne Advanced Identity Cloud

If you have not yet registered PingGateway with PingOne Advanced Identity Cloud, follow these steps:

  1. Configure an Agent journey PingGateway uses to authenticate with PingOne Advanced Identity Cloud.

  2. Register a profile for PingGateway.

    Field Value Description

    ID

    ig-agent

    The PingGateway username when connecting to the AmService in PingOne Advanced Identity Cloud.

    Password

    password

    The PingGateway password when connecting to the AmService in PingOne Advanced Identity Cloud.

    In production tenants, use a strong password.

    Redirect URLs

    https://ig.example.com:8443/replay/redirect

    The PingGateway endpoint to process the encrypted JWT from PingOne Advanced Identity Cloud.

PingOne Advanced Identity Cloud is now ready for PingGateway to connect. You will share the credentials with PingGateway in Task 6: Configure PingGateway.

Task 4: Prepare a shared secret

PingGateway and PingOne Advanced Identity Cloud share a secret symmetric key to encrypt and decrypt the password.

  1. Generate a random AES 256-bit key to use as a shared secret.

    How you generate the secret key is up to you; for example:

    $ openssl rand -base64 32
    YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd5eHowMTIzNDU=

    Keep the secret safe.

  2. Store the shared secret key as an ESV in PingOne Advanced Identity Cloud.

    1. Log in to the PingOne Advanced Identity Cloud admin UI as an administrator and go to Tenant Settings > Global Settings > Environment Secrets & Variables.

    2. Click Variables > + Add Variable.

    3. In the Add a Variable modal window, enter the following settings:

      Field Value Description

      Name

      esv-ig-shared-secret

      The ESV name for the secret.

      Value

      YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd5eHowMTIzNDU=

      The base64-encoded random AES 256-bit key.

    4. Click Save to create the variable.

    PingOne Advanced Identity Cloud can now access the secret through the esv.ig.shared.secret system property. You will share the secret with PingGateway in Task 6: Configure PingGateway.

Task 5: Prepare PingOne Advanced Identity Cloud

Update the PingOne Advanced Identity Cloud validation service for PingGateway, create a script to capture the password, and a journey to use the script.

  1. Log in to the PingOne Advanced Identity Cloud admin UI as an administrator and go to open_in_new Native Consoles > Access Management, select Services, and the following Valid goto URL Resources to the Validation Service:

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

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

  2. Go to Scripts > Auth Scripts, click + New Script and create a Journey Decision Node script with the Legacy engine named Password replay and the following JavaScript content.

    The script adds a JWT encrypted with the shared secret key to the session:

    Show script
    var fr = JavaImporter(
      org.forgerock.openam.auth.node.api.Action,
      org.forgerock.openam.auth.node.api,
      javax.security.auth.callback.TextOutputCallback,
      org.forgerock.json.jose.builders.JwtBuilderFactory,
      org.forgerock.json.jose.jwt.JwtClaimsSet,
      org.forgerock.json.jose.jwe.JweAlgorithm,
      org.forgerock.json.jose.jwe.EncryptionMethod,
      javax.crypto.spec.SecretKeySpec,
      org.forgerock.secrets.SecretBuilder,
      org.forgerock.util.encode.Base64
    );
    
    var NodeOutcome = {
      ERROR: 'false',
      SUCCESS: 'true'
    };
    
    var config = {
      encryptionKey: systemEnv.getProperty("esv.ig.shared.secret")
    };
    
    function getKey () {
      logger.message("encKey: " + config.encryptionKey)
      return new fr.SecretKeySpec(fr.Base64.decode(config.encryptionKey), 'AES');
    }
    
    function buildJwt (claims) {
      logger.message('Building response JWT');
      var encryptionKey = getKey();
      var jwtClaims = new fr.JwtClaimsSet;
      jwtClaims.setClaims(claims);
      var jwt = new fr.JwtBuilderFactory()
                      .jwe(encryptionKey)
                      .headers()
                      .alg(fr.JweAlgorithm.DIRECT)
                      .enc(fr.EncryptionMethod.A128CBC_HS256)
                      .done()
                      .claims(jwtClaims)
                      .build();
      return jwt;
    }
    
    try {
      password=nodeState.get("password").asString()
      var registrationClaims = { password: password };
      passwordJwt = buildJwt(registrationClaims);
      action = fr.Action.goTo(NodeOutcome.SUCCESS).putSessionProperty("sunIdentityUserPassword", passwordJwt).build();
    } catch (e) {
      logger.error('ERROR ' + e);
      action = fr.Action.send(new fr.TextOutputCallback(fr.TextOutputCallback.ERROR, e.toString())).build();
    }

    You can download the script as password-replay.js.

  3. Go to Journeys, click + New Journey, and create a journey named Password replay whose Identity Object is managed/alpha_user.

  4. Configure and save the journey with the nodes shown in this screenshot:

    Password replay journey

    • The Page node presents a page with input fields to prompt for the username and password.

      • The Platform Username node collects and injects the userName into the shared node state.

      • The Platform Password node collects and injects the password into the shared node state.

    • The Data Store Decision node uses the username and password to determine whether authentication is successful.

    • The Scripted Decision node references your script and has the same outcomes: true and false.

    • The Increment Login Count node updates the login count on successful authentication.

  5. In your browser’s privacy or incognito mode, go to the new journey and log in with the credentials of a known user in the alpha realm.

    PingOne Advanced Identity Cloud authenticates you and displays the user profile page.

You can now configure PingGateway to use the journey to capture the password.

Task 6: Configure PingGateway

The password replay configuration includes the PingGateway password to connect to PingOne Advanced Identity Cloud, the shared secret, and a route.

  1. Set an environment variable locally on the computer running PingGateway to access the agent password:

    # The base64-encoded PingGateway agent "password":
    $ export AGENT_SECRET_ID='cGFzc3dvcmQ='

    PingGateway retrieves the password with a SystemAndEnvSecretStore, which requires it to be base64-encoded.

    The password must match the agent profile password you set in PingOne Advanced Identity Cloud. PingGateway uses the password to connect to PingOne Advanced Identity Cloud.

  2. Set an environment variable locally on the computer running PingGateway to access the shared secret key:

    # The base64-encoded shared secret key:
    $ export AES_KEY='YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd5eHowMTIzNDU='

    PingGateway retrieves the shared secret with a SystemAndEnvSecretStore, which requires it to be base64-encoded.

    The shared secret key must match the secret key you stored in the PingOne Advanced Identity Cloud ESV. PingOne Advanced Identity Cloud uses the secret key to encrypt the password to replay. PingGateway uses the secret key to decrypt the password to replay.

  3. Add a route for password replay.

    • Linux

    • Windows

    $HOME/.openig/config/routes/04-replay-aic.json
    %appdata%\OpenIG\config\routes\04-replay-aic.json
    Show route
    {
        "name": "04-replay",
        "condition": "${find(request.uri.path, '^/replay')}",
        "properties": {
            "amInstanceUrl": "https://myTenant.forgeblocks.com/am/"
        },
        "heap": [
            {
                "name": "SystemAndEnvSecretStore-1",
                "type": "SystemAndEnvSecretStore",
                "config": {
                    "mappings": [
                        {
                            "secretId": "aes.key",
                            "format": {
                                "type": "SecretKeyPropertyFormat",
                                "config": {
                                    "format": "BASE64",
                                    "algorithm": "AES"
                                }
                            }
                        }
                    ]
                }
            },
            {
                "name": "AmService-1",
                "type": "AmService",
                "config": {
                    "url": "&{amInstanceUrl}",
                    "realm": "/alpha",
                    "agent": {
                        "username": "ig-agent",
                        "passwordSecretId": "agent.secret.id"
                    },
                    "secretsProvider": "SystemAndEnvSecretStore-1"
                },
                "sessionCache": {
                    "enabled": false
                }
            },
            {
                "name": "CapturedUserPasswordFilter-1",
                "type": "CapturedUserPasswordFilter",
                "config": {
                    "ssoToken": "${contexts.ssoToken.value}",
                    "keySecretId": "aes.key",
                    "keyType": "AES",
                    "secretsProvider": "SystemAndEnvSecretStore-1",
                    "amService": "AmService-1"
                }
            }
        ],
        "handler": {
            "type": "Chain",
            "config": {
                "filters": [
                    {
                        "name": "CrossDomainSingleSignOnFilter-1",
                        "type": "CrossDomainSingleSignOnFilter",
                        "config": {
                            "redirectEndpoint": "/replay/redirect",
                            "authCookie": {
                                "path": "/replay",
                                "name": "ig-token-cookie"
                            },
                            "amService": "AmService-1",
                            "authenticationService": "Password replay"
                        }
                    },
                    {
                        "name": "UserProfileFilter-1",
                        "type": "UserProfileFilter",
                        "config": {
                            "username": "${contexts.ssoToken.info.uid}",
                            "userProfileService": {
                                "type": "UserProfileService",
                                "config": {
                                    "amService": "AmService-1",
                                    "profileAttributes": [
                                        "username"
                                    ]
                                }
                            }
                        }
                    },
                    {
                        "name": "PasswordReplayFilter-1",
                        "type": "PasswordReplayFilter",
                        "config": {
                            "loginPage": "${true}",
                            "credentials": "CapturedUserPasswordFilter-1",
                            "request": {
                                "method": "POST",
                                "uri": "http://app.example.com:8081/login",
                                "form": {
                                    "username": [
                                        "${contexts.userProfile.username}"
                                    ],
                                    "password": [
                                        "${contexts.capturedPassword.value}"
                                    ]
                                }
                            }
                        },
                        "capture": [
                            "all"
                        ]
                    }
                ],
                "handler": "ReverseProxyHandler"
            }
        }
    }

    You can download the route as 04-replay.json.

    The route:

    • Matches requests whose path starts with /replay.

    • Sets an amInstanceUrl property to the access management service in PingOne Advanced Identity Cloud.

      Update the URL to target your tenant.

    • Loads the aes.key secret key from the local AES_KEY environment variable.

    • Connects to PingOne Advanced Identity Cloud as ig-agent with the agent.secret.id password from the local AGENT_SECRET_ID environment variable.

    • Extracts the captured password from the SSO token context and decrypts it with the aes.key secret key.

    • Uses a CrossDomainSingleSignOnFilter to redirect to the PingOne Advanced Identity Cloud Password replay journey for authentication, getting the authentication information from the ig-token-cookie.

    • Queries the access management service in PingOne Advanced Identity Cloud to retrieve the username for logging in.

      You can retrieve other profile attributes with the UserProfileFilter, such as the email address or first and last names. The sample application expects the username in this example, so the route gets the username.

    • Logs in to the sample application with the username and password.

    • Returns the result to the browser.

    In production, remove "capture": ["all"] from the PasswordReplayFilter to avoid recording credentials in the logs.

  4. Restart PingGateway to read the secrets from the environment and load the new route.

    In the PingGateway output, make sure the route loaded successfully with no errors or warnings: @system - Loaded the route with id '04-replay-aic' registered with the name '04-replay-aic'.

Task 7: Create a test user in PingOne Advanced Identity Cloud

The sample application validates the credentials for password replay. It must recognize the username and password you use.

The sample application has built-in username-password combinations. The username and password credentials shown in the following steps are one of the built-in pairs.

  1. In your browser’s privacy or incognito mode, go to the default login journey for the realm you’re using.

    For example, https://myTenant.forgeblocks.com/am/XUI/?realm=/alpha#/.

  2. Click the Create an account link and enter the following settings in the Sign Up page:

    Field Value

    Username

    wolkig

    First name

    Wilhelm

    Last name

    Wolkig

    Email Address

    Your email. PingOne Advanced Identity Cloud sends a confirmation mail to this address.

    Password

    Geh3imnis!

    Select a security question

    What’s your favorite color?

    Answer

    Red

  3. Click Next to complete account creation and view the user profile.

  4. Sign out.

Validation

  1. In your browser’s privacy or incognito mode, go to https://ig.example.com:8443/replay/.

    PingGateway redirects to the PingOne Advanced Identity Cloud journey.

  2. Log in as user wolkig with password Geh3imnis!.

    PingGateway successfully replays the credentials against the sample application. The sample application displays its user profile page:

    Successful password replay
  3. Review the PingGateway output.

    On success, the output displays the credentials and the profile page:

    ...INFO  o.f.o.d.c.C.c.PasswordReplayFilter-1 @04-replay-aic -
    
    [CONTINUED]--- (filtered-request) exchangeId:<id> - transactionId:<id> --->
    
    [CONTINUED]POST http://app.example.com:8081/login HTTP/1.1
    [CONTINUED]Content-Length: 37
    [CONTINUED]Content-Type: application/x-www-form-urlencoded
    
    [CONTINUED]password=Geh3imnis%21&username=wolkig
    
    ...INFO  o.f.o.d.c.C.c.PasswordReplayFilter-1 @04-replay-aic -
    
    [CONTINUED]<--- (response) exchangeId:<id> - transactionId:<id> ---
    
    [CONTINUED]HTTP/1.1 200 OK
    ...
    
    [CONTINUED]<!DOCTYPE html>
    ...

You have successfully demonstrated password replay with PingGateway and PingOne Advanced Identity Cloud.

If password replay fails, consider the outcome of the HTTP POST from PingGateway to the sample application:

HTTP 401 Unauthorized

PingGateway is not replaying the credentials.

Review the PingGateway output to determine whether the username or password is missing when PingGateway replays the credentials.

If the password is missing, make sure PingGateway and PingOne Advanced Identity Cloud share the same AES secret key.

HTTP 403 Forbidden

PingGateway is not replaying the right credentials.

Make sure you’re using a username-password combination known to the sample application.