The security.authentication.user_auth configuration adds end-user context to requests. It allows the MCP server to accept and forward user credentials to downstream services. This feature supports two modes:

  • Token mode—Forwards an opaque token string (default).
  • JWT mode—Accepts and validates user JWTs with optional cryptographic verification.

Token exchange flow

Token exchange provides an additional security layer by replacing the original user token with a new token obtained from an external endpoint. The token exchange works as follows:
  • When enabled, the user token is sent to an exchange endpoint using a POST request.
  • The response is parsed using a JSON path expression.
  • The new token is forwarded downstream using the same header and prefix configured in user_auth.
  • Failures abort the request without falling back to the original token for deterministic security.
Note: The token exchange configuration is nested under user_auth, not at the authentication level. The runtime automatically applies user_auth.header and user_auth.prefix settings to the exchanged token.

JWT Mode and JWKS Validation

Starting with OpenEdge MCP Server 1.1.0, JWT mode supports cryptographic validation of user tokens through JWKS (JSON Web Key Set) endpoints. This enables OAuth2/OIDC-compliant token verification, ensuring that only cryptographically valid tokens from trusted identity providers can access your MCP tools.

JWT mode operates at two validation levels:
  • Passthrough mode (default)—Structural JWT parsing without signature verification. The server extracts claims from the JWT but does not validate the signature. This mode is suitable for development or environments where token validation is performed elsewhere. When user_auth.mode: "jwt" is used without user_auth.jwks_uri, the server performs structural validation only and does not establish trusted claims or scopes.
  • JWKS validation mode—Full cryptographic validation using your identity provider's JWKS endpoint. When user_auth.jwks_uri is configured, the server validates the JWT signature using public keys from the JWKS endpoint, verifies expiration and timing claims, validates the issuer and audience, checks the algorithm against your allowlist, and extracts scopes for authorization. Tokens that fail validation are rejected with no fallback.

When to use JWKS validation:

Configure JWKS validation when you need authenticated user authorization with cryptographically verified tokens from identity providers such as Keycloak, Auth0, Azure AD, or Okta. Without JWKS validation, JWT mode provides structural parsing only and should not be relied upon for security-critical authorization decisions.

The following table lists the properties available in security.authentication.user_auth:
Property Purpose
enabled Enables or disables user credential ingestion
mode Specifies the mode: token (opaque) or jwt (parsed claims only)
header Defines the incoming header name. The default value is Authorization.
prefix Adds a prefix when forwarding credentials. The default value is Bearer.
forward_downstream Forces forwarding even if otherwise suppressed
token_exchange.* Configures an external POST request to swap the token before forwarding
jwks_uri JWKS endpoint URL for cryptographic JWT validation. This property is required for JWKS validation mode. For example, https://your-idp.com/.well-known/jwks.json
issuer Expects JWT issuer claim (iss) validated against the iss claim in incoming JWTs
audience Expects JWT audience claim (aud) validated against the aud claim in incoming JWTs
algorithms Algorithms allowlist for JWT signature verification. Only NIST-approved RSA algorithms are supported. The default values are ["RS256","RS384","RS512"]. Tokens with any other alg header are rejected.
jwks_cache_seconds Cache lifetime for JWKS keys in seconds, which controls how long public keys are cached. The default value is 300 seconds.

JWKS validation configuration

When user_auth.jwks_uri is configured, the MCP server validates incoming user JWTs against public keys from your identity provider's JWKS endpoint. This provides cryptographic assurance that tokens are issued by a trusted authority and have not been tampered with.

Configuration example:

{
"security": {
	"authentication": {
		"enabled": true,
		"user_auth": {
			"enabled": true,
			"mode": "jwt",
			"header": "Authorization",
			"prefix": "Bearer ",
			"jwks_uri": ""https://auth.example.com/realms/my-realm/protocol/openid-connect/certs",
			"issuer": "https://auth.example.com/realms/my-realm",
			"audience": "mcp-api",
			"algorithms": ["RS256"],
			"jwks_cache_seconds": 3600
			}
		}
	}
}
When jwks_uri is configured (mode: "jwt"), on each user token request:
  1. The JWT header kid is extracted.
  2. JWKS is fetched from jwks_uri and cached for the configured cache interval.
  3. The JWK matching kid is selected and converted to an RSA public key.
  4. JWT verification enforces signature validity, validates exp and nbf claims, applies the algorithm allowlist, and validates iss and aud claims when issuer and audience are configured.
  5. On any verification failure, the server returns hard HTTP 401; tool handlers are not reached.
  6. On success, scopes are extracted from the token and used for tool gating.
If JWT validation fails for any reason, including use of alg:none, the server returns HTTP 401 Unauthorized and does not invoke tool handlers. When JWKS validation is enabled, there is no fallback to passthrough mode.

JWKS cache behavior:

Public keys from the JWKS endpoint are cached for the duration specified by jwks_cache_seconds. This reduces the number of requests to your identity provider while maintaining security. The cache is automatically refreshed when keys expire or when a JWT references a key ID (kid) that is not in the cache.

Algorithms allowlist:

The algorithms configuration restricts which JWT signing algorithms are accepted. For JWKS validation, only NIST-approved RSA algorithms are supported:
  • RS256 (RSA with SHA-256)
  • RS384 (RSA with SHA-384)
  • RS512 (RSA with SHA-512)
Asymmetric algorithms such as HS256 (HMAC) are not supported in JWKS validation mode because they require shared secrets rather than public keys.
Note: Existing deployments using user_auth.mode: "jwt" without user_auth.jwks_uri continue to operate in passthrough mode. No configuration changes are required for existing deployments unless you opt for JWKS validation. To enable JWKS validation, add the jwks_uri configuration along with issuer, audience, and algorithms settings appropriate for your identity provider.

Authentication enablement levels

Authentication behavior is controlled by three distinct switches. These switches determine whether authentication middleware, scope gating, and discovery filtering are active.

The following table lists the enablement levels, their configuration paths, and their purpose:
Level Key path Scope Purpose Typical use
Global security.authentication.enabled Top-level Master gate that enables or disables all authentication features. When false, both service account and user authentication settings are ignored. Temporarily disable all authentication for debugging
Service account security.authentication.service_account.enabled Sub-section Enables server credential injection using JWT or JWKS. Provides stable scopes for tool gating and discovery filtering. Backend identity and consistent authorization context
User authentication security.authentication.user_auth.enabled Sub-section Accepts end-user credentials and optionally performs token exchange before forwarding downstream. Per-request user context and least-privilege exchange
The interaction rules define how the enablement levels interact with each other. The following points summarize the interaction rules:
  • Global must be set to true for either sub-section to take effect.
  • You can enable either sub-section independently. Combined mode forwards both credentials unless suppressed.
  • Scope gating and discovery filtering activate only when global is true and at least one enabled credential provides scopes.
  • Token exchange runs only when user authentication is enabled and token_exchange.enabled=true.