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.
Learn more in the overview page for these examples.
Add an OIDC client
Follow these steps to create a PingOne OIDC web application:
-
In the test environment, create a web application with the following values:
-
Application Name:
oidc_client
-
Description:
OIDC client
-
Application Type: OIDC Web App
-
-
On the Overview tab of the details panel, click Protocol OpenID Connect.
-
In the Redirect URIs field, enter
https://ig.example.com:8443/home/sso/callback
. Click Save. -
At the top-right of the application’s profile, click the toggle to enable the application.
-
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:
-
In the test environment, go to Authorization > API Gateways and click +.
-
Enter PingGateway as the Name. Click Save.
-
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.
-
In the test environment, go to Authorization > API Services and click +.
-
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.
-
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:
-
In the test environment, go to Applications > Resources and click the PingGateway resource.
-
On the Overview tab, click the Pencil icon, and, in the Audience field, enter
https://ig.example.com:8443
. Click Save. -
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. -
Go to Applications > Applications and click oidc_client.
-
On the Resources tab, edit Allowed Scopes, and select the read scope checkbox. Click Save.
Configure PingGateway
-
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>'
-
Restart PingGateway to read the environment variables.
-
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 theread
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
-
In your browser’s privacy or incognito mode, go to https://ig.example.com:8443/home/sso.
PingOne displays the sign-on page.
-
Sign on to PingOne as the test user.
PingGateway displays the 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.