PingGateway 2024.11

PingOne Authorize integration

Use PingOne Authorize with PingGateway to protect APIs and web applications. When you add a PingOneApiAccessManagementFilter to a route, PingGateway requests an authorization decision from PingOne Authorize based on the request and associated access token.

This example uses PingOne as an OIDC provider issuing an ID token with an access token. PingGateway enforces authorization decisions from PingOne Authorize. In this example, the decision to authorize access to a protected application depends on an access token scope.

Before you begin

If you haven’t already done so:

  • Install PingGateway.

  • Install the sample application.

  • Prepare a PingOne test environment and a test user.

Add an OIDC client

  1. In the test environment, create a web application with the following values:

    • Application Name: oidc_client

    • Description: OIDC client

    • Application Type: OIDC Web App

  2. On the Overview tab of the details panel, click Protocol OpenID Connect.

  3. In the Redirect URIs field, enter https://ig.example.com:8443/home/sso/callback. Click Save.

  4. At the top-right of the application’s profile, click the toggle to enable the application.

  5. On the Configuration tab, make a note of the following values in the URLs list:

    • OIDC Discovery Endpoint

    • Client ID

    • Client Secret

    You need the values to set up PingGateway.

Add an API gateway

PingGateway uses a PingOne API gateway account to authenticate to PingOne Authorize:

  1. In the test environment, go to Authorization > API Gateways and click +.

  2. Enter PingGateway as the Name. Click Save.

  3. Next to Credentials, click + and copy the API gateway credential.

    You need the credential value to set up PingGateway.

Add an API service

In this example, PingOne Authorize uses a PingOne API service definition to reach authorization decisions.

The API service holds references to the following:

  • The base URL for PingGateway, which is the base URL for what a user sees in their browser.

  • The base URLs for the protected sample application, which is the object of an authorization decision.

  • The resource to protect, which is the PingGateway URL proxying for the sample application.

  • The scope required for access in this example.

    1. In the test environment, go to Authorization > API Services and click +.

    2. On the New API Service panel, add the following settings, then click Save. Accept the default values for the settings not listed here:

      Setting Value

      Name

      PingGateway

      Base URLs

      https://app.example.com:8444 (for the sample application)
      https://ig.example.com:8443 (for PingGateway)

      Enable Custom Policies

      Select this.

    3. Click Deploy so you can use the new API service.

Update the automatically created resource

When you added the PingGateway API service, PingOne automatically created a corresponding resource by default. Update the resource so its audience (the aud in access tokens) reflects the PingGateway URL and so that it requires a read scope to get it:

  1. In the test environment, go to Applications > Resources and click the PingGateway resource.

  2. On the Overview tab, click the Pencil icon, and, in the Audience field, enter https://ig.example.com:8443. Click Save.

  3. On the Scopes tab, click Add Scope, and in the Scope Name field, enter read. Click Save.

    This adds the read scope to the PingGateway service.

  4. Go to Applications > Applications and click oidc_client.

  5. On the Resources tab, edit Allowed Scopes, and select the read scope checkbox. Click Save.

Configure PingGateway

  1. Prepare the secrets PingGateway needs to act as an OIDC RP and an API gateway when making requests to PingOne.

    Base64-encode the API gateway’s credential and the OIDC application’s client secret and set both values as environment variables:

    $ export GATEWAY_SECRET_ID='<base64-encoded-api-gateway-credential>'
    $ export OIDC_SECRET_ID='<base64-encoded-client-secret>'
  2. Restart PingGateway to read the environment variables.

  3. Add the following route to PingGateway, replacing the property values to match those from the test environment:

    • Linux

    • Windows

    $HOME/.openig/config/routes/oidc-ping.json
    %appdata%\OpenIG\config\routes\oidc-ping.json
    {
      "name": "pingone-aam",
      "condition": "${find(request.uri.path, '^/home/sso')}",
      "baseURI": "https://app.example.com:8444",
      "properties": {
        "gatewayServiceUrl": "https://http-access-api.pingone.eu/v1/environments/test-environment-id",
        "oidcClientId": "oidc-client-id",
        "oidcWellKnownEndpoint": "https://auth.pingone.eu/test-environment-id/as/.well-known/openid-configuration"
      },
      "heap": [
        {
          "name": "BlindTrustReverseProxyHandler",
          "type": "ReverseProxyHandler",
          "comment": "For evaluation and testing only",
          "config": {
            "tls": {
              "type": "ClientTlsOptions",
              "config": {
                "trustManager": {
                  "type": "TrustAllManager"
                },
                "hostnameVerifier": "ALLOW_ALL"
              }
            }
          }
        },
        {
          "name": "SystemAndEnvSecretStore-1",
          "type": "SystemAndEnvSecretStore"
        },
        {
          "name": "AuthenticatedRegistrationHandler-1",
          "type": "Chain",
          "config": {
            "filters": [
              {
                "name": "ClientSecretBasicAuthenticationFilter-1",
                "type": "ClientSecretBasicAuthenticationFilter",
                "config": {
                  "clientId": "&{oidcClientId}",
                  "clientSecretId": "oidc.secret.id",
                  "secretsProvider": "SystemAndEnvSecretStore-1"
                }
              }
            ],
            "handler": "ForgeRockClientHandler"
          }
        }
      ],
      "handler": {
        "type": "Chain",
        "config": {
          "filters": [
            {
              "type": "AuthorizationCodeOAuth2ClientFilter",
              "config": {
                "clientEndpoint": "/home/sso",
                "failureHandler": {
                  "type": "StaticResponseHandler",
                  "config": {
                    "status": 500,
                    "headers": {
                      "Content-Type": [
                        "text/plain"
                      ]
                    },
                    "entity": "Error: ${contexts.oauth2Failure.error}\nDescription: ${contexts.oauth2Failure.description}"
                  }
                },
                "registrations": [
                  {
                    "type": "ClientRegistration",
                    "config": {
                      "clientId": "&{oidcClientId}",
                      "issuer": {
                        "type": "Issuer",
                        "config": {
                          "wellKnownEndpoint": "&{oidcWellKnownEndpoint}"
                        }
                      },
                      "scopes": [
                        "openid",
                        "read"
                      ],
                      "authenticatedRegistrationHandler": "AuthenticatedRegistrationHandler-1"
                    }
                  }
                ]
              }
            },
            {
              "type": "PingOneApiAccessManagementFilter",
              "config": {
                "gatewayServiceUri": "&{gatewayServiceUrl}",
                "secretsProvider": "SystemAndEnvSecretStore-1",
                "gatewayCredentialSecretId": "gateway.secret.id",
                "accessToken": "${attributes.openid.access_token}",
                "_sidebandHandler": {
                  "_comment": "s/_sidebandHandler/sidebandHandler/ to troubleshoot AAM decisions",
                  "type": "ClientHandler",
                  "capture": "all"
                }
              }
            }
          ],
          "handler": "BlindTrustReverseProxyHandler"
        }
      }
    }

    Notice the following features of the route:

    • The route properties use the settings you collected from the PingOne environment.

    • The heap has:

      • A handler to trust the self-signed sample application certificate for HTTPS blindly.

        In production deployments, use certificates you don’t have to trust blindly.
      • A SystemAndEnvSecretStore to read get the base64-encoded secrets from the environment variables.

      • A handler to get an ID token as the OIDC client.

    • The AuthorizationCodeOAuth2ClientFilter gets the ID token with the read scope.

    • The PingOneApiAccessManagementFilter makes the authorization decision request to PingOne Authorize. It sends the access token from the OIDC ID token.

    • On success, the BlindTrustReverseProxyHandler gets the resource from the sample application.

Validation

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

    PingOne displays the sign-on page.

  2. Sign on to PingOne as the test user.

    PingGateway displays the sample application home page:

    sample application home page

Troubleshooting

If you get unexpected errors, try these debugging options:

  • Update the PingOneApiAccessManagementFilter configuration to enable the capture decorator.

    Change "_sidebandHandler" to "sidebandHandler" and save your work to let PingGateway reload the route.

  • In the test environment, go to Authorization > Recent Decisions, select the PingGateway decision endpoint, and select a decision to visualize.

    PingOne shows you how PingOne Authorize arrived at the authorization decision.

    On the Request and Response tabs, you find detailed traces of the parameters for each.

User experience

When PingOne Authorize denies access or the example route fails to get an access token, the browser displays text, which isn’t user-friendly.

Consider using a StaticResponseHandler to show a more friendly page to the user.