Configuring PingGateway for FAPI
To support FAPI, PingGateway plays the role of reverse proxy for the Advanced Identity Cloud authorization server and the role of resource server for protected applications.
PingGateway uses the accounts and routes described here to connect to and protect Advanced Identity Cloud, lets FAPI clients register dynamically, and protects resources for sensitive applications.
Advanced Identity Cloud configuration
Before you begin
-
Sign on to the Advanced Identity Cloud admin UI as an administrator.
-
Switch to the realm you use for FAPI.
This tutorial uses the
alpha
realm. Adapt the realm name to your deployment.
OAuth 2.0 client accounts
Create as many OAuth 2.0 client accounts as needed for the resource server roles PingGateway plays.
In this tutorial, PingGateway acts as a resource server only as an example, so use the account you created when configuring access management.
PingGateway configuration
The PingGateway routes for FAPI depend on definitions in config.json
.
Adapt these settings as necessary for your deployment. In particular, this tutorial uses a test trusted directory service. In production deployments, use your FAPI ecosystem’s official trusted directory service, such as the Open Banking Directory, for example.
Base configuration
-
Prepare keys for PingGateway server-side TLS and mutual TLS.
For server-side TLS, HTTPS clients must trust the PingGateway certificate. You can use a CA or generate your own keys as described in Configure PingGateway for TLS (server-side).
For mutual TLS, PingGateway must trust the test trusted directory’s CA certificate. The test trusted directory uses its CA certificate to sign API client TLS certificates.
The following example generates a TLS key pair in a keystore named
secrets/keystore.p12
, exports the PingGateway self-signed certificate assecrets/gateway.pem
, and imports the test trusted directory’s CA certificate from a file namedsecrets/test-trusted-dir-ca.pem
:touch secrets/keystore.pin chmod 600 secrets/keystore.pin echo -n password > secrets/keystore.pin keytool -keystore secrets/keystore.p12 -storetype PKCS12 \ -genkeypair -alias ssl-key-pair -dname "CN=Gateway TLS keys" \ -keyalg RSA -keysize 2048 -validity 3650 -ext "san=dns:gateway.example.com" \ -storepass:file secrets/keystore.pin \ -keypass:file secrets/keystore.pin keytool -keystore secrets/keystore.p12 -storetype PKCS12 \ -exportcert -alias ssl-key-pair -rfc -file secrets/gateway.pem -storepass:file secrets/keystore.pin keytool -keystore secrets/keystore.p12 -storetype PKCS12 \ -importcert -trustcacerts -alias ca -rfc \ -file secrets/test-trusted-dir-ca.pem -storepass:file secrets/keystore.pin -noprompt
Adjust the hostname in
-ext "san=dns:gateway.example.com"
to match your deployment. -
Add a
admin.json
file such as the following to use the TLS keys, adapting the port numbers and secrets to match the deployment:FAPI
admin.json
(Source: admin-fapi.json)
{ "mode": "DEVELOPMENT", "heap": [ { "name": "TlsConf", "type": "ServerTlsOptions", "config": { "clientAuth": "REQUEST", "keyManager": { "type": "SecretsKeyManager", "config": { "signingSecretId": "key.manager.secret.id", "secretsProvider": "KeyStoreSecretStore" } }, "trustManager": { "type": "SecretsTrustManager", "config": { "verificationSecretId": "trust.manager.secret.id", "secretsProvider": "KeyStoreSecretStore" } } } }, { "name": "KeyStoreSecretStore", "type": "KeyStoreSecretStore", "config": { "file": "&{ig.instance.dir}/../secrets/keystore.p12", "storePasswordSecretId": "keystore.pin", "secretsProvider": { "type": "FileSystemSecretStore", "config": { "directory": "&{ig.instance.dir}/../secrets/", "format": "PLAIN" } }, "mappings": [ { "secretId": "key.manager.secret.id", "aliases": [ "ssl-key-pair" ] }, { "secretId": "trust.manager.secret.id", "aliases": [ "ca" ] } ] } } ], "connectors": [ { "port": 8080, "maxTotalHeadersSize": 24576, "vertx": { "maxInitialLineLength": 8192 } }, { "port": 8443, "maxTotalHeadersSize": 24576, "tls": "TlsConf", "vertx": { "maxInitialLineLength": 8192 } } ], "adminConnector": { "port": 9443, "host": "localhost", "tls": "TlsConf" } }
-
Add a
config.json
file with these settings, adapting the Advanced Identity Cloud tenant hostname and other properties to your deployment:FAPI
config.json
(Source: config-fapi.json)
{ "properties": { "asHostname": "myTenant.forgeblocks.com", "gatewayOAuth2ClientId": "gateway-oauth2-client", "gatewayIdmUsername": "gateway-idm-user", "realm": "alpha", "tenantHostname": "myTenant.forgeblocks.com", "trustedDirectoryJwksUrl": "http://trustdir.example.com:9080/jwkms/testdirectory/jwks" }, "handler": "_router", "heap": [ { "name": "_router", "type": "Router", "config": { "directory": "${openig.configDirectory}/routes", "defaultHandler": { "type": "DispatchHandler", "config": { "bindings": [ { "condition": "${request.method == 'GET' and request.uri.path == '/'}", "handler": { "type": "WelcomeHandler" } }, { "condition": "${request.uri.path == '/'}", "handler": { "type": "StaticResponseHandler", "config": { "status": 405, "reason": "Method Not Allowed" } } }, { "handler": { "type": "StaticResponseHandler", "config": { "status": 404, "reason": "Not Found" } } } ] } } } }, { "name": "capture", "type": "CaptureDecorator", "config": { "captureEntity": true } }, { "name": "PlatformReverseProxyHandler", "comment": "Add a transaction ID header for calls to platform services", "type": "Chain", "config": { "filters": [ "TransactionIdOutboundFilter" ], "handler": "ReverseProxyHandler" } }, { "name": "SystemAndEnvSecretStore", "type": "SystemAndEnvSecretStore" }, { "name": "IdmClientHandler", "type": "Chain", "config": { "filters": [ { "type": "ResourceOwnerOAuth2ClientFilter", "config": { "tokenEndpoint": "https://&{tenantHostname}/am/oauth2/realms/root/realms/&{realm}/access_token", "username": "&{gatewayIdmUsername}", "passwordSecretId": "gateway.idm.password", "secretsProvider": "SystemAndEnvSecretStore", "scopes": [ "fr:idm:*" ], "endpointHandler": { "type": "Chain", "config": { "handler": "ForgeRockClientHandler", "filters": [ { "type": "ClientSecretBasicAuthenticationFilter", "config": { "clientId": "&{gatewayOAuth2ClientId}", "clientSecretId": "gateway.oauth2.client.secret", "secretsProvider": "SystemAndEnvSecretStore" } } ] } } } } ], "handler": "ForgeRockClientHandler" } }, { "name": "JwkSetService", "type": "CachingJwkSetService", "config": { "cacheMaxSize": 500, "cacheTimeout": "24 hours" } }, { "name": "TrustedDirectoryService", "type": "TrustedDirectoryService", "config": { "trustedDirectories": [ "TestTrustedDirectory" ] } }, { "name": "TestTrustedDirectory", "type": "TrustedDirectory", "config": { "issuer": "FAPI Test Trusted Directory", "softwareStatementClaims": { "organisationIdClaimName": "org_id", "organisationNameClaimName": "org_name", "softwareIdClaimName": "software_id", "clientNameClaimName": "software_client_name", "redirectUrisClaimName": "software_redirect_uris", "rolesClaimName": "software_roles", "_comment": "If your clients publish JWKs, use jwksUriClaimName instead of jwksClaimName.", "jwksClaimName": "software_jwks" }, "secretsProvider": { "type": "SecretsProvider", "config": { "stores": [ { "type": "JwkSetSecretStore", "config": { "jwkUrl": "&{trustedDirectoryJwksUrl}" } } ] } } } }, { "name": "IdmService", "type": "IdmService", "config": { "baseEndpoint": "https://&{tenantHostname}/openidm", "endpointHandler": "IdmClientHandler" } }, { "name": "IdmApiClientService", "type": "IdmApiClientService", "config": { "idmService": "IdmService", "jwkSetService": "JwkSetService" } }, { "name": "IdmApiClientOrganisationService", "type": "IdmApiClientOrganisationService", "config": { "idmService": "IdmService" } }, { "name": "AsJwkSecretsProvider", "type": "SecretsProvider", "config": { "stores": [ { "type": "JwkSetSecretStore", "config": { "jwkUrl": "https://&{tenantHostname}/am/oauth2/realms/root/realms/&{realm}/connect/jwk_uri" } } ] } } ], "session": { "type": "JwtSessionManager" } }
This file includes the settings for the trusted directory and access to Advanced Identity Cloud identity management services. If your clients publish JWKs, use
jwksUriClaimName
instead ofjwksClaimName
in the trusted directory configuration. This example usesjwksClaimName
and static JWKs for the test client profiles, where the clients aren’t publicly accessible.Whenever you change this file, restart PingGateway to reload the configuration.
-
Export secrets the PingGateway configuration takes from the environment.
export GATEWAY_OAUTH2_CLIENT_SECRET='cGFzc3dvcmQ=' # Base64-encoding of `password` export GATEWAY_IDM_PASSWORD='U2VjcmV0MTIh' # Base64-encoding of `Secret12!`
These secrets correspond to the following properties in
config.json
:-
gateway.oauth2.client.secret
for the OAuth 2.0 client accountgatewayOAuth2ClientId
. -
gateway.idm.password
for the identity management user accountgatewayIdmUsername
.
In production deployments, use a secure secret store.
-
-
Restart PingGateway to load the new configuration.
You have successfully configured the base settings for PingGateway to support FAPI.
Routes to protect the authorization server
PingGateway protects access to these Advanced Identity Cloud authorization server endpoints to enforce FAPI compliance:
-
The dynamic client registration endpoint (
/oauth2/register
) -
The access token endpoint (
/oauth2/access_token
) -
The Pushed Authorization Request (PAR) endpoint (
/oauth2/par
) -
The authorization endpoint (
/oauth2/authorize
) -
The well-known configuration endpoint (
/.well-known/openid-configuration
)
Each of these corresponds to a PingGateway route for access to the authorization server. Make sure API clients and conformance tests go through these routes, not directly to Advanced Identity Cloud.
To the routes
folder of the PingGateway configuration:
-
Add a route to ensure compliant registration requests:
When using a public trusted directory rather than the test trusted directory, don’t use
allowPingIssuedTestCerts
in theFapiDcrFilterChain
. -
Add a route to ensure compliant access token requests:
-
Add a route to ensure compliant PAR requests:
-
Add a route to ensure compliant authorize requests:
-
Add a route to ensure compliant well-known metadata requests: fapi-28-as-metadata.json.
You have successfully configured the routes for PingGateway to protect the Advanced Identity Cloud authorization server.
Resource server routes
In production systems, as shown in Deployment, use a separate PingGateway server with separate routes for each resource server role.
In this tutorial, this PingGateway server also acts as a resource server. You create a resource server route for this role.
-
Add this script to the
scripts/groovy
folder of the PingGateway configuration: ExampleRsApiResponseHandler.groovy. -
Add this route to the
routes
folder of the PingGateway configuration: fapi-01-rs-example-protected-api.json.
You have successfully configured the resource server route for PingGateway. Create as many routes as needed for the resource server roles PingGateway plays.
Default route
Add the following default route to the routes
folder of the PingGateway configuration:
fapi-99-platform-passthrough.json.
This route lets requests like those for end-user sign on pass through unchanged.