Configuring PingOne
The MCP Gateway can use PingOne as the identity provider behind its downstream
OAuth flow. The mcp-ping-oauth-inbound policy is a PingOne-friendly wrapper
around the generic mcp-oauth-inbound policy: provide a PingOne environment ID
(or a custom domain), a client ID, and a client secret, and the policy derives
the OIDC issuer, JWKS URL, and authorize and token URLs for you.
This guide walks through the PingOne admin console setup, then wires the policy into a gateway project. Read the authentication overview first for the two-layer OAuth model.
This policy is for PingOne cloud. For PingFederate deployments — which
can customize issuer hosts, issuer paths, and endpoint paths — use the generic
mcp-oauth-inbound policy instead.
Set up PingOne
The MCP Gateway acts as an OAuth 2.1 authorization server in front of PingOne. PingOne handles browser login; the gateway issues its own access tokens that bind to MCP routes.
Create an OIDC application
- In the PingOne admin console, switch to the environment the gateway should use, then open Applications → Applications.
- Click + Add Application, name it (for example,
Zuplo MCP Gateway), choose OIDC Web App as the application type, and click Save. - Open the application's Configuration tab.
- Set Redirect URIs to
https://<gateway-host>/oauth/callback. Addhttp://localhost:9000/oauth/callbackfor local development. - Set Grant Types to Authorization Code.
- Save.
Note the credentials
Open the application's Profile tab. Copy the Client ID and Client Secret.
Find your environment ID and region
Open Settings → Environment in the PingOne admin console. Copy the
Environment ID (a UUID like 11111111-1111-4111-8111-111111111111). Note
the Geography of the environment — North America, Canada, Europe, Singapore,
Australia, or Asia-Pacific. You'll pass these to the policy.
Optional: custom domain
If your PingOne environment uses a custom domain (configured under Settings →
Domains), copy the bare host (such as login.example.com) and use it instead
of environmentId + region. The wrapper switches to the custom-domain
endpoint shape when customDomain is set.
Wire the policy into the gateway
Add the policy to config/policies.json:
Code
For a custom domain:
Code
Attach the policy to each MCP route in config/routes.oas.json and register the
gateway plugin in modules/zuplo.runtime.ts (see
Configuring Auth0
for the route and plugin patterns — they're identical across all wrappers).
Available regions
region value | PingOne auth host |
|---|---|
north-america | auth.pingone.com (default) |
canada | auth.pingone.ca |
europe | auth.pingone.eu |
singapore | auth.pingone.sg |
australia | auth.pingone.com.au |
asia-pacific | auth.pingone.asia |
What the wrapper derives
For the default region (north-america) and environment ID ENV_ID:
| Generic field | Derived value |
|---|---|
oidc.issuer | https://auth.pingone.com/{ENV_ID}/as |
oidc.jwksUrl | https://auth.pingone.com/{ENV_ID}/as/jwks |
browserLogin.url | https://auth.pingone.com/{ENV_ID}/as/authorize |
browserLogin.tokenUrl | https://auth.pingone.com/{ENV_ID}/as/token |
With customDomain set, the host changes to your custom domain and the
{environmentId} segment is removed.
Test the configuration
The fastest sanity check is to connect an MCP client:
- Open Claude Desktop, Cursor, Claude Code, or another OAuth-aware MCP client.
- Add a remote MCP server pointing at one of your
/mcp/{slug}routes. - The client should redirect you to the PingOne sign-in page. After login, the gateway's consent screen renders. Approve it.
- The client receives an access token and can call
tools/list.
If something fails partway through, walk the flow manually using the
manual OAuth testing guide — it exercises every
endpoint with curl so you can see the raw responses.
Common issues
environmentIdrejected at boot. The wrapper expects a UUID. Don't pass the issuer URL, the auth domain, or the client ID.- Browser login lands on a PingOne error page. The redirect URI on the
application doesn't match
https://<gateway-host>/oauth/callback. invalid_client. The application is set to Public instead of Confidential. Confidential is required so the gateway can authenticate with the client secret.
Related
- Authentication overview
- Configuring a generic OIDC provider — use this for PingFederate.
- Per-user OAuth to upstream MCP servers