How to Secure MCP Authentication
Implement and verify authentication for one protected remote MCP server deployment.
By Agent Guard Team7 min read
How to Secure MCP Authentication
Secure MCP authentication by treating a remote MCP server as a protected OAuth resource: publish resource metadata, use an established authorization server, require authorization-code flow with PKCE where applicable, validate every access token for issuer, signature, expiry, audience, and scope, and prove that invalid requests cause no downstream effect.
Define the protected deployment
This guide is for a protected remote MCP server. Local stdio servers have a different trust boundary because the client starts a local process and the operating-system identity often supplies access. Do not bolt a bearer token onto a local command and call the deployment secured.
Map the actors before configuration: MCP client, MCP server as resource server, authorization server, user or workload identity, and downstream APIs. Record the public resource identifier, redirect URIs, token audience, required operations, and the identity that the server uses downstream. Read the MCP protocol architecture first if those roles are not yet explicit.
Pass condition: each actor and trust boundary has one owner and identifier. Failure path: stop when the server cannot distinguish its own resource identifier from an upstream API or when one shared credential represents every user and client.
Publish protected-resource metadata
The server needs a standards-based way to tell clients where authorization begins. Publish OAuth protected-resource metadata for the MCP resource and identify the supported authorization server. Keep the resource identifier stable, use HTTPS, and ensure discovery documents do not point to an attacker-controlled issuer.
The OAuth 2.0 Protected Resource Metadata specification defines the metadata format. Validate the document in deployment tests: correct content type, canonical resource value, expected authorization server, no environment mix-up, and no untrusted redirect.
When an unauthenticated request reaches the protected server, return the protocol-appropriate authorization challenge without exposing sensitive details. Do not embed long-lived tokens in a URL or configuration copied across users.
Pass condition: a clean client discovers the expected authorization server from the resource. Failure path: deny access if metadata is missing, ambiguous, fetched over an untrusted channel, or names an unapproved issuer.
Use an OAuth authorization server
Use a maintained identity provider or authorization server rather than writing password handling, token issuance, and key rotation inside the MCP server. Register redirect URIs exactly. Use authorization code flow and PKCE for user-facing public clients. Do not enable an implicit flow or accept arbitrary redirect patterns to make client onboarding easier.
Separate client authentication from user authentication. A confidential client may authenticate itself, while the authorization server also establishes the user and consent context. A workload acting without a user needs an explicitly approved machine identity and grant, not a copied human refresh token.
The current MCP Authorization specification describes the server as an OAuth resource server and sets the protocol requirements. Pin the specification version used by your implementation because MCP authorization has evolved.
Pass condition: only registered clients and redirect URIs complete the flow, and the resulting token is bound to the intended resource. Failure path: block deployment if a client can substitute a redirect, skip PKCE, or obtain a token for an unrelated audience.
Validate tokens at the MCP server
Validate access tokens at the resource server before tool discovery or execution. For a JWT access token, verify its signature with trusted keys and check the allowed algorithm, issuer, expiry, not-before value, audience or resource, and required scope. For an opaque access token, use the authorization server's approved introspection or validation path and check the same resource and permission context. Reject any active or structurally valid token issued for another service. Resource validation prevents token passthrough and confused-deputy behavior.
Cache signing keys with bounded lifetime and a safe refresh path. A key-fetch failure should fail closed for new or unverifiable tokens, while operational handling distinguishes a temporary identity outage from an invalid credential. Never accept unsigned tokens or switch algorithms based only on an untrusted header.
Bind the validated identity and scopes to the request context used by every tool handler. Authentication at the HTTP edge is ineffective if internal calls drop the identity and execute as an unrestricted service account.
Pass condition: valid tokens reach only the intended resource and invalid tokens cannot list or call protected tools. Failure path: return a consistent denial, retain the validation reason without logging the token, and verify no downstream request occurred.
Design scopes and consent
Scopes should represent meaningful capability boundaries, not one mcp:all permission. Separate read from write and ordinary work from consequential actions. Where tools reach different systems or tenants, enforce those boundaries again in the handler and downstream authorization layer.
Consent should name the requesting client, resource, and understandable capability. Do not infer consent for newly added tools from approval granted to an older tool list. Require fresh review when a server adds a write tool, requests a broader destination, or changes the resource identifier.
Use least privilege for the server's own downstream identity. A user token presented to the MCP server should not be forwarded unchanged to another API unless the architecture explicitly supports and validates that exchange.
Protect tokens and client state
Keep access and refresh tokens out of URLs, prompts, model context, shell history, source repositories, and general application logs. Store them in the platform credential store or a secret manager with access restricted to the client component that needs them. Rotate secrets and keys without requiring developers to paste values into chat or config files.
The AI agent credential leak controls guide covers the wider path from storage through tool arguments and logs. For MCP authentication, add state and PKCE verifier protection, strict redirect handling, and refresh-token rotation or revocation appropriate to the authorization server.
Pass condition: a support bundle, prompt trace, and normal debug log contain token identifiers or redacted metadata, never bearer values. Failure path: revoke exposed tokens, remove the logging path, and test the same failure before reissuing credentials.
Test failure paths
Build a deterministic matrix with: no token, malformed token, expired token, wrong issuer, wrong audience, missing scope, revoked token, substituted redirect, reused authorization code, missing PKCE verifier, and a valid least-privilege token. Add a harmless high-impact tool call that the valid low-privilege token must still fail.
Keep each test record reviewable. At minimum, capture:
- test identity, client, issuer, and resource identifier;
- credential condition without storing the bearer value;
- requested MCP tool and normalized target;
- expected protocol response and internal reason code;
- observed downstream request, if any;
- final target state and retained audit reference.
For each case record HTTP or protocol result, internal reason code, audit identity, selected tool, and target-state evidence. Do not put bearer tokens into fixtures. Generate short-lived test credentials or use a local identity test environment.
Authentication succeeds only when the allowed request completes and every denial leaves the downstream target unchanged. A generic 401 response while a queued job still runs is a control failure.
Verify and regress
Verify discovery, authorization, token validation, tool authorization, and downstream identity as one path. Then test bypasses: an alternate route, a websocket or streaming endpoint, cached tool metadata, internal service calls, and direct access to the handler. Every public path must reach the same validation decision.
Re-run after issuer, key, resource identifier, redirect URI, client registration, scope, tool, downstream credential, proxy, SDK, or protocol-version changes. Monitor denial reasons and identity outages, but do not use "monitor authentication" as a substitute for blocking invalid requests.
Define outage behavior before production. If discovery or the authorization server is unavailable, existing short-lived sessions may have a different policy from new authorization attempts. If signing keys cannot be refreshed, unverifiable new tokens must not be accepted. Record the bounded exception, owner, and expiry rather than adding a global bypass during an incident.
Rollback must preserve the last known-valid issuer, resource, redirects, and validation configuration without restoring revoked secrets. After rollback, repeat the wrong-audience, missing-scope, and alternate-route cases before reopening write tools.
AgentGuard's public documentation lists policy, audit, and runtime-related interfaces that can add evidence for supported hosts. Use the current AgentGuard documentation to verify integration depth; it does not replace OAuth token validation at the MCP server.
Frequently Asked Questions
Are API keys enough for MCP authentication?
An API key can identify a client in a narrow machine-to-machine deployment, but it lacks the discovery, user consent, resource binding, and delegated authorization model required by the current protected remote MCP flow.
Is authentication the same as authorization?
No. Authentication establishes an identity or validates a credential. Authorization decides whether that identity may perform a specific tool action on a resource.
Where should MCP tokens be stored?
In an operating-system credential store or managed secret system accessible only to the client component that needs them. Keep bearer values out of prompts, logs, repositories, and URLs.
What proves the setup is secure?
A repeatable test matrix showing valid least-privilege access, rejection of invalid issuer/audience/scope cases, no bypass route, and unchanged downstream state after every denial.
Test every token denial against the downstream state before launch.
Test access