ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference

Upstream OAuth 2.0 Client Credentials Auth Policy

Secure your origin server with OAuth 2.0 authentication by automatically adding an Authorization header to upstream requests. This policy enables your Zuplo gateway to authenticate with any OAuth 2.0 identity provider that supports the client credentials grant — such as Auth0, Okta, Keycloak, Microsoft Entra ID, or your own authorization server.

With this policy, you'll benefit from:

  • Enhanced Backend Security: Restrict access to your origin servers to only your Zuplo gateway
  • Simplified Authentication: Delegate authentication and authorization to your gateway without backend code changes
  • Automatic Token Management: Handle token acquisition, caching, and renewal automatically
  • Provider Flexibility: Works with any standards-compliant OAuth 2.0 token endpoint, no provider-specific policy required
  • Credential Security: Store sensitive client credentials securely in your Zuplo environment

Configuration

The configuration shows how to configure the policy in the 'policies.json' document.

Code
{ "name": "my-upstream-oauth-client-credentials-inbound-policy", "policyType": "upstream-oauth-client-credentials-inbound", "handler": { "export": "UpstreamOAuthClientCredentialsInboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "audience": "https://api.example.com", "clientId": "my-client-id", "clientSecret": "$env(OAUTH_CLIENT_SECRET)", "credentialsIn": "body", "expirationOffsetSeconds": 300, "headerName": "Authorization", "headerScheme": "Bearer", "scope": "read:orders write:orders", "tokenRetries": 3, "tokenUrl": "https://your-tenant.us.auth0.com/oauth/token" } } }

Policy Configuration

  • name <string> - The name of your policy instance. This is used as a reference in your routes.
  • policyType <string> - The identifier of the policy. This is used by the Zuplo UI. Value should be upstream-oauth-client-credentials-inbound.
  • handler.export <string> - The name of the exported type. Value should be UpstreamOAuthClientCredentialsInboundPolicy.
  • handler.module <string> - The module containing the policy. Value should be $import(@zuplo/runtime).
  • handler.options <object> - The options for this policy. See Policy Options below.

Policy Options

The options for this policy are specified below. All properties are optional unless specifically marked as required.

  • tokenUrl (required) <string> - The URL of the OAuth 2.0 token endpoint that issues the access token.
  • clientId (required) <string> - The client ID used to authenticate with the token endpoint.
  • clientSecret (required) <string> - The client secret used to authenticate with the token endpoint.
  • scope <string> - Space-delimited list of scopes to request. When not set, the scope parameter is omitted from the token request.
  • audience <string> - The value of the audience form parameter sent to the token endpoint. Required by some identity providers such as Auth0. When not set, the parameter is omitted from the token request.
  • credentialsIn <string> - Where the client credentials are sent on the token request. body sends client_id and client_secret as form parameters. header sends them with HTTP Basic authentication as described in RFC 6749 section 2.3.1. Allowed values are body, header. Defaults to "body".
  • additionalParameters <object> - Additional form parameters to include in the token request, for example resource for identity providers that require it.
  • headerName <string> - The name of the header on the upstream request that the access token is set on. Defaults to "Authorization".
  • headerScheme <string> - The scheme that prefixes the access token in the header. When the token response includes a token_type, that value is used instead. Defaults to "Bearer".
  • tokenRetries <number> - The number of times to retry fetching the token in the event of a failure. Defaults to 3.
  • expirationOffsetSeconds <number> - The number of seconds less than the token expiration to cache the token. Defaults to 300.

Using the Policy

This policy authenticates your Zuplo gateway to OAuth 2.0-protected backend services by automatically adding an access token to the Authorization header (or a custom header) of upstream requests. It uses the OAuth 2.0 client credentials grant against any token endpoint you configure, so it works with Auth0, Okta, Keycloak, Microsoft Entra ID, and any other standards-compliant identity provider.

How It Works

The policy performs the following operations:

  1. Requests an access token from the configured token endpoint using the client credentials grant
  2. Caches the token for subsequent requests until it nears expiration
  3. Adds the token to the configured header (default Authorization) using the token_type returned by the token endpoint, falling back to the configured headerScheme (default Bearer)
  4. Automatically handles token renewal when needed

Tokens are cached for expires_in - expirationOffsetSeconds seconds. If the token endpoint doesn't return an expires_in value, the policy caches the token conservatively as if it expired after 10 minutes (5 minutes with the default expirationOffsetSeconds of 300) rather than caching it indefinitely.

Policy Configuration

Configure the policy with your identity provider's token endpoint and client credentials:

Code
{ "name": "upstream-oauth-client-credentials", "export": "UpstreamOAuthClientCredentialsInboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "tokenUrl": "https://your-tenant.us.auth0.com/oauth/token", "clientId": "$env(OAUTH_CLIENT_ID)", "clientSecret": "$env(OAUTH_CLIENT_SECRET)", "audience": "https://api.example.com", "scope": "read:orders write:orders" } }

Sending Client Credentials

By default (credentialsIn: "body"), the client credentials are sent as client_id and client_secret form parameters in the token request body. While RFC 6749 section 2.3.1 recommends HTTP Basic authentication, body is the default because it's the method most widely accepted across identity providers (including Auth0, Okta, Entra ID, and Keycloak) and avoids credential-encoding differences between providers' Basic authentication implementations.

Set credentialsIn to header to send the credentials with HTTP Basic authentication instead:

Code
{ "options": { "tokenUrl": "https://idp.example.com/oauth2/token", "clientId": "$env(OAUTH_CLIENT_ID)", "clientSecret": "$env(OAUTH_CLIENT_SECRET)", "credentialsIn": "header" } }

Provider-Specific Parameters

Some identity providers require extra form parameters on the token request. Use additionalParameters to send them, for example the resource parameter:

Code
{ "options": { "tokenUrl": "https://idp.example.com/oauth2/token", "clientId": "$env(OAUTH_CLIENT_ID)", "clientSecret": "$env(OAUTH_CLIENT_SECRET)", "additionalParameters": { "resource": "https://api.example.com" } } }

The grant_type parameter is always client_credentials and the dedicated clientId, clientSecret, scope, and audience options always take precedence over entries in additionalParameters.

Usage Example

Apply the policy to routes that need to call your OAuth-protected backend:

Code
{ "paths": { "/api/orders": { "get": { "x-zuplo-route": { "policies": { "inbound": ["jwt-auth", "upstream-oauth-client-credentials"] }, "handler": { "export": "forwardToOrigin", "module": "$import(@zuplo/runtime)", "options": { "baseUrl": "https://api.internal.example.com" } } } } } } }

Security Considerations

  • Store the client secret as an environment variable using $env(VARIABLE_NAME) syntax
  • Grant the OAuth client the minimum scopes required to access your backend services
  • Regularly rotate your client secrets according to your security policies

Read more about how policies work

Edit this page
Last modified on July 30, 2026
On this page
  • Configuration
    • Policy Configuration
    • Policy Options
  • Using the Policy
    • How It Works
    • Policy Configuration
    • Sending Client Credentials
    • Provider-Specific Parameters
    • Usage Example
    • Security Considerations
JSON
JSON
JSON
JSON
JSON