Configure NetScaler OAuth 2.1 for MCP Gateway
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.
-
RFC 9470 authorization-server discovery in 401 challenges.
401 WWW-Authenticate: Bearer challenge.
401 Unauthorized response includes a standardized header:
WWW-Authenticate: Bearer realm=..., as_uri="<AS URL>", scope="...", error=..., error_description=...
-
as_uriis derived from the OAuth Action issuer. -
scopeis derived from the OAuth Action scope.
401 response.
-
Resource-specific OAuth protected resource metadata (RFC 9728)
/.well-known/oauth-protected-resource[/<resource>].
/.well-known/oauth-protected-resource[/<resource>] supports resource-specific queries, for example /mcp.
-
authorization_servers -
bearer_methods_supported -
resource -
scopes_supported -
resource_documentation
-
Richer OAuth action scope configuration
set authentication oauthAction supports multiple scopes.
-scope "openid profile email"
-
scopes_supported(metadata response) -
scope(401 header)
401 response. They can then proceed with the OAuth 2.1 authorization-code and PKCE flow against the configured external IdP.
-
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-TMauthentication 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 ONor-authnVsName. -
Both internal and external OAuth authorization servers. Resource-specific PRM (RFC 9728) is mandatory when an external IdP is used.
Key benefits
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
-
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, andscopepopulated. -
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_vsin front of the MCP backend. -
The configured
issuermatches the canonicalAS URLused 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
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
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
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
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"]
}
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
Configure OAuth by using the GUI
-
Navigate to Security > AAA - Application Traffic > Authentication > Authentication Actions > OAuth.
-
Create or edit the OAuth action.
-
Add an Issuer (same as
as_uri) and Scope (space-separated values). -
Bind the OAuth policy to the authentication, authorization, and auditing virtual server used as the MCP Gateway front end.
-
On the load balancing virtual server in front of the MCP backend, enable
401-based authenticationand set authentication virtual server to the AAA virtual server.
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_urivalue in the 401 header is sourced exclusively from the OAuth actionissuerfield. Environments withoutissuerconfigured do not advertiseas_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_documentationURLs must be configured externally and are returned verbatim. NetScaler does not validate whether the URL is active or reachable.
Monitoring and troubleshooting
-
nstrace.shplus Wireshark to confirm the 401WWW-Authenticateheader carriesas_uri,scope,error, anderror_description. -
aaad.debugorns.logto validate OAuth actionissuerandscopeare loaded and used at challenge time. -
curl -kvagainst/.well-known/oauth-protected-resourceand/.well-known/oauth-protected-resource/<resource>to verify per-resource PRM JSON.
Troubleshooting
Problem: An MCP client shows authorization required but fails IdP discovery
issuer is unset, so as_uri is not injected into the 401 response header.
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
Problem: Scope appears as a single token instead of space-separated values
-scope parameter was set with comma separation or a single value.
set authentication oauthAction <name> -scope "openid profile email".
Authentication for MCP Gateway
-
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.
-
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
-
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
-
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.
"headers": {
"X-Netscaler-Target-MCP-Server": "<mcp-server-fqdn>/<mcp-path>/",
"X-Netscaler-Backend-Auth-Token": "Bearer <user_pat_or_token>"
}
-insertHeaderInClientRequest on an MCP profile bound to a service group or service.
set mcpprofile m1 -insertHeaderInClientRequest ENABLED
Scenario 2: Service-account access using a shared (global) PAT or Bearer token for a backend MCP server
-
Configure the backend token in the MCP profile using
mcpTokenOrApi. -
Enable
insertHeaderInClientRequestif the backend requires authorization header injection. -
Bind the backend MCP profile to the service or service group assigned to the MCP server.
add mcpProfile <backend_profile_name> \
-mcpTokenOrApi <token_value> \
-insertHeaderInClientRequest ENABLED
Scenario 3: OAuth-only access where tokens are obtained or validated via OAuth flows
Scenario 4: OAuth 2.1 frontend authentication with backend access token injection (hybrid)
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>
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-Authenticateresource metadata.