ADC

Configure NetScaler OAuth 2.1 for MCP Gateway

MCP clients, such as VS Code, Claude Desktop, and Copilot, follow the MCP authorization specification, which mandates OAuth 2.1 with RFC 9470 (WWW-Authenticate Bearer with as_uri) and RFC 9728 (Protected Resource Metadata). Previously, NetScaler® could not act as a fully compliant OAuth 2.1 Protected Resource in front of MCP servers, which blocked enterprise adoption of MCP Gateway use cases.

NetScaler MCP Gateway implements the following three OAuth 2.1 enhancements required by the MCP authorization specification.

  • RFC 9470 authorization-server discovery in 401 challenges.

NetScaler supports RFC 9470 authorization-server discovery through the 401 WWW-Authenticate: Bearer challenge.

The 401 Unauthorized response includes a standardized header:

WWW-Authenticate: Bearer realm=..., as_uri="<AS URL>", scope="...", error=..., error_description=...

Key field mapping:

  • as_uri is derived from the OAuth Action issuer.
  • scope is derived from the OAuth Action scope.

Capability: Clients can directly discover the authorization server and required scopes from a single 401 response.

  • Resource-specific OAuth protected resource metadata (RFC 9728)

NetScaler enables resource-specific OAuth protected resource metadata with /.well-known/oauth-protected-resource[/<resource>].

The endpoint /.well-known/oauth-protected-resource[/<resource>] supports resource-specific queries, for example /mcp.

It returns a JSON document with:

  • authorization_servers
  • bearer_methods_supported
  • resource
  • scopes_supported
  • resource_documentation

Capability: Each protected resource gets its own metadata, improving clarity and client discovery.

  • Richer OAuth action scope configuration

set authentication oauthAction supports multiple scopes.

Example:

-scope "openid profile email"

These scopes are automatically used in:

  • scopes_supported (metadata response)
  • scope (401 header)

Capability: Ensures consistent scope definition across all discovery mechanisms.

Together, these enhancements allow standards-compliant MCP clients, such as VS Code, to discover the authorization server and required scopes from a single 401 response. They can then proceed with the OAuth 2.1 authorization-code and PKCE flow against the configured external IdP.

This feature is applicable to:

  • The NetScaler authentication, authorization, and auditing module when NetScaler is configured as an MCP Gateway acting as an OAuth 2.1 protected resource in front of one or more MCP servers.
  • AAA-TM authentication virtual servers configured on the front end of an MCP Gateway deployment, for example an authentication virtual server bound to a load balancing virtual server with -authn401 ON or -authnVsName.
  • Both internal and external OAuth authorization servers. Resource-specific PRM (RFC 9728) is mandatory when an external IdP is used.

Key benefits

Some of the benefits are:

Seamless client integration

  • Works out-of-the-box with MCP-compatible clients, such as VS Code MCP, Claude Desktop, Cursor, and Copilot.
  • Users authenticate once using the corporate IdP.
  • NetScaler transparently controls access to multiple backend MCP servers.

Centralized enterprise control

  • NetScaler acts as a single enforcement point for authentication, scopes, and resource-level metadata.
  • No client-side changes are required.

Multi-resource and multi-tenant ready

  • A single MCP Gateway VIP in front of multiple services, for example GitHub Copilot, Atlassian, Slack, and internal MCP servers.
  • Each service exposes its own authorization server, distinct scopes, and documentation.
  • Enabled using resource-specific metadata responses.

Standards-aligned and future-proof

  • Fully aligned with the OAuth 2.1-based MCP authorization model, RFC 9470 (discovery), and RFC 9728 (metadata).
  • Reduces integration risks with evolving MCP ecosystems.

Points to note

  • When using an external authorization server, a resource-specific PRM (RFC 9728) is mandatory. MCP clients cannot bind tokens to the correct resource without per-resource metadata.
  • The scope value advertised in both the 401 header and the PRM document must match what the IdP is configured to issue. Mismatches can cause silent client-side flow failures.
  • When changing the OAuth action issuer or scope, clients with cached PRM or AS metadata may need to refresh. Consider invalidating client sessions or setting short cache TTLs during rollout.
  • Existing rewrite and responder-based workarounds that synthesized 401 headers or PRM responses must be removed once native behavior is enabled to avoid conflicting output.

Prerequisites

Ensure that the following requirements are met:

  • An OAuth authorization server (internal NetScaler OAuth IdP, Okta, Entra ID, GitHub, or any RFC 8414-compliant OAuth 2.1 or OIDC provider) is configured.
  • A frontend MCP client that implements OAuth 2.1 as per the MCP authorization specification, such as VS Code MCP client, Claude Desktop, Cursor, or Copilot is available.
  • TLS termination on NetScaler is enabled with a server certificate trusted by the MCP client.
  • An authentication, authorization, and auditing virtual server is configured as the MCP Gateway front end.
  • An OAuth action is created with at least authorizationEndpoint, tokenEndpoint, clientID, clientSecret, issuer, and scope populated.
  • An OAuth policy is bound to the authentication virtual server that selects the OAuth action.
  • A load balancing virtual server is configured with -authn401 ON -authnVsName av_vs in front of the MCP backend.
  • The configured issuer matches the canonical AS URL used by the IdP metadata document.

Configure OAuth by using the CLI

Step 1: Configure the AAA frontend virtual server and OAuth action

add authentication vserver av_vs SSL 0.0.0.0

# Use space-separated scopes.
add authentication OAuthAction idp_default -type GENERIC -clientID <client-id> -clientSecret <client-secret> -authorizationEndpoint "https://idp.com/oauth2/v1/authorize" -tokenEndpoint "https://idp.com/oauth2/v1/token" -introspectURL "https://idp.com/oauth2/v1/introspect" -issuer "https://idp.com" -scope "openid profile copilot:access" -userNameField sub

add authentication Policy oauth_pol -rule true -action idp_default
bind authentication vserver av_vs -policy oauth_pol -priority 100
<!--NeedCopy-->

Step 2: Configure a load balancing virtual server acting as an MCP Gateway and offload authentication to the authentication virtual server

add lb vserver lb_vs_ssl SSL <VIP> 443 -authn401 ON -authnVsName av_vs
add service service-mcp-backend <backend-ip> SSL 443
bind lb vserver lb_vs_ssl service-mcp-backend
<!--NeedCopy-->

Step 3: Update and verify an existing OAuth action to use multi-scope

set authentication oauthAction idp_default -scope "openid profile email copilot:access"

show oauthaction idp_default
<!--NeedCopy-->

Verify that the scopes line appears as a space-separated list.

Sample 401 response as per RFC 9470

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="OAuth Protected Resource",
as_uri="https://idp.com",
error="invalid_request",
error_description="The request is missing a required parameter or is otherwise malformed",
scope="openid profile copilot:access"
Content-Length: 0
Cache-Control: no-cache,no-store,must-revalidate
<!--NeedCopy-->

Sample resource-specific PRM response as per RFC 9728

GET /.well-known/oauth-protected-resource/mcp

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-cache, no-store, must-revalidate

{
"authorization_servers": ["https://idp.com"],
"bearer_methods_supported": ["header"],
"resource": "https://proxy.aaanetscaler.local/mcp",
"resource_documentation": "https://docs.github.com/copilot",
"scopes_supported": ["openid", "profile", "copilot:access"]
}
<!--NeedCopy-->

Step 4: Verify the active configuration

show authentication vserver av_vs
show oauthAction idp_default
show lb vserver lb_vs_ssl

# From any MCP client host
curl -kv https://proxy.aaanetscaler.local/mcp
curl -k https://proxy.aaanetscaler.local/.well-known/oauth-protected-resource
curl -k https://proxy.aaanetscaler.local/.well-known/oauth-protected-resource/mcp
<!--NeedCopy-->

Configure OAuth by using the GUI

  1. Navigate to Security > AAA - Application Traffic > Authentication > Authentication Actions > OAuth.
  2. Create or edit the OAuth action.
  3. Add an Issuer (same as as_uri) and Scope (space-separated values).
  4. Bind the OAuth policy to the authentication, authorization, and auditing virtual server used as the MCP Gateway front end.
  5. On the load balancing virtual server in front of the MCP backend, enable 401-based authentication and set authentication virtual server to the AAA virtual server.

Note:

The feature is enabled automatically when an OAuth action with issuer and scope is bound to an authentication virtual server that fronts an MCP Gateway load balancing virtual server (-authn401 ON). No new feature flag is required. If issuer is empty, RFC 9470 falls back to prior behavior (no as_uri in the 401), which is still RFC-compliant but less convenient for MCP clients.

Limitations

  • The as_uri value in the 401 header is sourced exclusively from the OAuth action issuer field. Environments without issuer configured do not advertise as_uri. The 401 challenge is still returned without the discovery hint, which aligns with RFC 9470 optional fallback behavior.
  • Native Dynamic Client Registration (DCR) is not supported. DCR (RFC 7591) for MCP clients can be handled using a responder policy returning a canned 201 response.
  • PRM resource_documentation URLs must be configured externally and are returned verbatim. NetScaler does not validate whether the URL is active or reachable.

Monitoring and troubleshooting

Useful diagnostics using existing authentication, authorization, and auditing, and ns trace tooling. No new counters are introduced.

  • nstrace.sh plus Wireshark to confirm the 401 WWW-Authenticate header carries as_uri, scope, error, and error_description.
  • aaad.debug or ns.log to validate OAuth action issuer and scope are loaded and used at challenge time.
  • curl -kv against /.well-known/oauth-protected-resource and /.well-known/oauth-protected-resource/<resource> to verify per-resource PRM JSON.

Troubleshooting

Problem: An MCP client shows authorization required but fails IdP discovery

Cause: The OAuth action issuer is unset, so as_uri is not injected into the 401 response header.

Solution: Run set authentication oauthAction <name> -issuer <AS URL> and retry the client request.

Problem: An MCP client fetches resource-specific PRM but receives a default non-resource document

Cause: Resource suffix routing cannot reach the AAA module. Check the AAA virtual server binding and verify that the suffix is passed through any front side load balancing or content switching policies.

Solution: Test directly against the AAA virtual server IP address. If the test succeeds, audit the upstream content switching and responder policy bindings.

Problem: Scope appears as a single token instead of space-separated values

Cause: The OAuth action -scope parameter was set with comma separation or a single value.

Solution: Correct the syntax with set authentication oauthAction <name> -scope "openid profile email".

Authentication for MCP Gateway

Enterprises using MCP must connect to multiple MCP servers that require different authentication methods, such as PAT, OAuth, or a combination of both. Previously, without a gateway, clients had to implement per-server authentication, manage tokens securely, and handle OAuth discovery flows, which increased complexity and governance risk.

NetScaler MCP Gateway supports multiple authentication patterns to securely access backend MCP servers. Depending on the deployment scenario, NetScaler can pass through client-provided tokens, inject backend tokens, or integrate with OAuth 2.1 flows to ensure that MCP requests are authenticated end-to-end.

By using NetScaler as an MCP Gateway, users can centralize authentication handling for MCP traffic. This enables consistent policy enforcement, reduces client-side complexity, and improves security by preventing token exposure in client policies.

Some of the benefits are:

  • Reduces client configuration by providing a single gateway endpoint.
  • Secures token handling using MCP profile and authentication, authorization, and auditing attributes.
  • Supports common MCP authentication patterns, such as VS Code.

Points to note:

  • Avoid embedding secrets in rewrite or policy expressions. Instead, use an MCP profile (mcpTokenOrApi) or AAA-derived attributes.
  • For per-user flows, ensure that the user identity token is available to NetScaler (for example, headers or AAA sessions).
  • Optional: For multi-backend deployments, pair your authentication server with a server allow list and persistence.

Prerequisites

Ensure that the following requirements are met:

  • MCP Gateway routing must be configured for content switching and load balancing and backend MCP servers must be reachable.
  • MCP profiles must be created for the relevant frontend and backend servers.
  • When using OAuth-based flows, OAuth endpoints (authorization, token, and introspect) and client credentials must be configured.

Limitation

  • OAuth discovery and endpoint behaviors might require additional configuration depending on MCP client expectations.

Use cases

Scenario 1: Per-user access to an MCP server using user-specific PAT or Bearer tokens

In this scenario, each user uses a user-specific PAT or Bearer token for the backend MCP server. The gateway routes the request to the target MCP server and ensures that the backend receives the correct per-user token.

  • The client includes the target MCP server in a header.
  • The client includes the per-user backend token (Bearer or PAT) in a header.
  • NetScaler passes the backend token or maps it to an internal form based on the gateway policy.

Example (header-driven):

"headers": {
   "X-Netscaler-Target-MCP-Server": "<mcp-server-fqdn>/<mcp-path>/",
   "X-Netscaler-Backend-Auth-Token": "Bearer <user_pat_or_token>"
}
<!--NeedCopy-->

Enable -insertHeaderInClientRequest on an MCP profile bound to a service group or service.

set mcpprofile m1 -insertHeaderInClientRequest ENABLED
<!--NeedCopy-->

Scenario 2: Service-account access using a shared (global) PAT or Bearer token for a backend MCP server

In this scenario, a single global token is used to access the backend MCP server, such as a service account token. The token is configured in NetScaler and users do not need to provide credentials.

Perform the following steps:

  1. Configure the backend token in the MCP profile using mcpTokenOrApi.
  2. Enable insertHeaderInClientRequest if the backend requires authorization header injection.
  3. Bind the backend MCP profile to the service or service group assigned to the MCP server.

Example: MCP profile backend token injection:

add mcpProfile <backend_profile_name> \
  -mcpTokenOrApi <token_value> \
  -insertHeaderInClientRequest ENABLED
<!--NeedCopy-->

Scenario 3: OAuth-only access where tokens are obtained or validated via OAuth flows

In this scenario, NetScaler does not act as an OAuth client for backend communication. The backend MCP server is responsible for handling OAuth authentication. Requests are forwarded to the backend MCP server without token injection or authentication handling by NetScaler.

This model is typically used when the MCP client is already configured to handle OAuth, or when the backend MCP server performs its own authentication workflows independently of the gateway.

Configure rewrite policies to ensure that authentication flows do not bypass MCP Gateway.

Scenario 4: OAuth 2.1 frontend authentication with backend access token injection (hybrid)

In this scenario, NetScaler handles the frontend OAuth authentication and injects a bearer token into the backend request. This deployment is commonly used by interactive MCP clients.

Configure an OAuth virtual server, load balancer with authn401 and bearer insertion.

add authentication vserver <auth_vserver> SSL 0.0.0.0

add authentication OAuthAction <oauth_action> -authorizationEndpoint "https://<idp>/oauth/idp/login" -tokenEndpoint "https://<idp>/oauth/token" -clientID <client-id> -clientSecret <client-secret> -Attributes mcptoken -userNameField sub -issuer "https://<idp>" -introspectURL "https://<idp>/oauth/idp/introspect" -scopes openid

add authentication Policy <oauth_pol> -rule true -action <oauth_action>

bind authentication vserver <auth_vserver> -policy <oauth_pol> -priority 100 -gotoPriorityExpression NEXT

add service <backend_svc> <backend_ip_or_fqdn> SSL 443

add lb vserver <lb_vs> SSL -authn401 ON -authnVsName <auth_vserver>

bind lb vserver <lb_vs> <backend_svc>
<!--NeedCopy-->

Troubleshooting

If backend authentication fails

  • Verify token source (client header vs MCP profile vs AAA attribute).

If OAuth flows fail, verify OAuth action endpoints

  • Redirect URL configuration and scopes.

If clients are redirected to backend endpoints instead of gateway

  • Apply required response rewrites for WWW-Authenticate resource metadata.
Configure NetScaler OAuth 2.1 for MCP Gateway