Try Bifrost Enterprise free for 14 days. Request access

MCP Authentication: OAuth 2.1 Patterns for Agent Tool Access

MCP Authentication: OAuth 2.1 Patterns for Agent Tool Access

TL;DR

  • The MCP authorization specification requires OAuth 2.1, mandatory PKCE, and a strict separation between the authorization server that issues tokens and the resource server (the MCP server) that validates them.
  • RFC 9728 protected resource metadata and RFC 8707 resource indicators bind an access token to one specific MCP server, which is what stops the confused deputy problem and the token passthrough anti-pattern.
  • Authentication answers who is calling; it says nothing about which tools that caller can invoke, so a working deployment needs a separate authorization layer on top.
  • Bifrost, the open-source AI gateway built by Maxim AI, resolves caller identity through a priority order of signed-in user, virtual key, and session ID, then stacks that identity against three levels of tool filtering before a request ever reaches an MCP server.

Authorization is optional in the Model Context Protocol specification, but once an HTTP-based MCP server turns it on, the June 2025 revision requires OAuth 2.1 with mandatory PKCE and a strict split between the server that issues tokens and the server that serves tools. Bifrost, the open-source AI gateway built in Go by Maxim AI, sits at that boundary as both an MCP client and an MCP server, and this post covers how the specification's authorization model maps onto a production deployment: which OAuth mechanics are mandatory, how caller identity gets resolved across signed-in users, virtual keys, and sessions, and where authorization takes over once authentication is settled.

What Is MCP Authentication?

MCP authentication is the process of verifying the identity of a caller, human or agent, before an MCP server executes a tool on that caller's behalf. It is distinct from authorization, which decides what an already-authenticated caller is permitted to do. The Model Context Protocol treats authentication as optional at the protocol level, but any HTTP-based server exposed outside a local machine effectively needs it, because an unauthenticated MCP endpoint is an unauthenticated API endpoint with the ability to read files, write to databases, or call external services.

Two authentication shapes cover almost every deployment. Server-level authentication uses one shared credential, configured once by an administrator, and every caller reaches the upstream MCP server under that same identity. Per-user authentication requires each caller to supply their own credential, which the gateway stores against that caller's identity and reuses on later calls. STDIO connections, which spawn a local subprocess, inherit credentials from the parent environment instead of using either model, since there is no per-call authentication surface to protect. Bifrost's MCP overview covers how these connection types map onto its client and server roles in more detail.

A companion piece, MCP authentication explained, walks through OAuth, API keys, and token management as a foundational primer; this post assumes that background and goes deeper into the specification mechanics and identity-resolution model below. For a rundown of what goes wrong when none of this is enforced, see common MCP security risks and how to mitigate them.

The OAuth 2.1 Model Behind the MCP Specification

The MCP authorization specification requires every MCP client to implement OAuth 2.1 with PKCE, requires servers to publish protected resource metadata under RFC 9728, and recommends resource indicators under RFC 8707 to bind a token to the specific server it was issued for. Together these three mechanisms close the gap that let early MCP deployments treat one bearer token as valid everywhere.

Before the specification separated the authorization server from the resource server, an MCP server could act as both, minting and validating its own tokens. The current model keeps those roles distinct on purpose. Mandatory PKCE stops an attacker who intercepts an authorization code from redeeming it without the original code verifier. RFC 9728 protected resource metadata gives a client a standard /.well-known/oauth-protected-resource document to discover which authorization server issues valid tokens for a given MCP server, returned via the WWW-Authenticate header on a 401 response. RFC 8707 resource indicators let a client request a token scoped to one resource server, so a token minted for MCP server A cannot be replayed against MCP server B.

That audience binding is also the fix for the confused deputy problem: an MCP proxy that uses a static client ID with a third-party authorization server can be tricked into forwarding an authorization code it never should have accepted, if the proxy skips per-client consent. A related anti-pattern, token passthrough, is explicitly forbidden by the specification. It happens when an MCP server accepts a token from a client without checking that the token was issued to that server specifically, then forwards it to a downstream API unchanged. That breaks rate limiting and traffic controls that depend on token audience, and it destroys the audit trail, since the downstream API can no longer distinguish which MCP client actually made the call. Bifrost's own MCP authentication types are built around this audience-bound model rather than around a single shared bearer token.

For teams comparing how different infrastructure layers enforce this model, MCP gateway vs. MCP proxy vs. MCP server breaks down where each component sits relative to the authorization server.

Server-Level vs. Per-User MCP Server Authentication

Most production deployments end up mixing both authentication shapes across different MCP servers, because the right choice depends on whether the credential belongs to the organization or to the individual caller.

Dimension Server-level (shared) Per-user
Who authenticates An admin, once, at setup Each caller, on first tool call
Credential scope Shared across every request Bound to one caller's identity
Typical fit A company-wide GitHub App, an internal MCP with a static bearer token A personal Notion workspace, a per-seat API key
Revocation Edit or delete the MCP client config Revoke the individual credential row
Consent step One-time, by the admin Repeated per new caller

Bifrost implements this split as six selectable auth_type values: none for public or local tools, headers for a shared static credential, oauth for a shared OAuth 2.0 token with automatic refresh, PKCE, and dynamic client registration, per_user_headers and per_user_oauth for credentials tied to an individual caller, and token_exchange for enterprise deployments where every caller's identity-provider token is exchanged on each call rather than stored. The MCP authentication docs lay out a decision flow: same credential for everyone points to headers or oauth; a different credential per caller that trusts the organization's own identity provider points to token_exchange; anything else per-caller points to per_user_oauth or per_user_headers depending on whether the upstream itself speaks OAuth.

How a Gateway Resolves Caller Identity

Per-user MCP server authentication only works if the gateway can answer one question first: who is actually calling right now. That resolution has to happen before any credential lookup, and it has to be deterministic when a request carries more than one identity signal, a detail the identity-mode reference spells out in full.

Bifrost resolves identity from three possible signals, in strict priority order. A user identity comes from SSO-authenticated context, or from a virtual key that a specific person owns, which gets auto-promoted to that person's identity. A VK identity comes from a virtual key that resolves but is not owned by a specific user, the typical shape for a service integration. A session identity comes from an opaque session ID header, useful when a caller has neither SSO nor a virtual key. When more than one signal is present, such as a user-owned virtual key that both resolves to a VK and promotes to a user, the higher-priority mode wins and the credential lookup binds to that identity alone. A per-user request that carries none of the three is rejected outright, with a structured mcp_auth_required response telling the caller which identity signal to add.

That identity choice has consequences beyond the credential lookup. When Bifrost mints an OAuth consent URL for a caller who needs to authenticate to an upstream MCP server, the identity mode active at that moment gets frozen onto the resulting auth flow. A user-mode flow can only be completed by the bound SSO user, so forwarding the link to a colleague fails with a 403 after they sign in. A VK-mode or session-mode flow treats the URL itself as the credential, which is the correct behavior for a shared integration or an automated test client where the link is meant to be handed off.

Authorization After Authentication: Tool Filtering and Virtual Keys

Authentication establishes identity. It says nothing about which of an MCP server's tools that identity should be able to invoke, and treating the two as the same control is a common source of over-privileged agents. A caller who is correctly authenticated to a file-system MCP server still should not necessarily be able to call delete_file.

Bifrost separates that concern into three stacked levels of tool filtering, and a tool must clear all three to reach the model: client configuration sets the baseline list of tools an MCP client exposes at all, request-level headers narrow that set per call, and virtual key configuration applies an organization's access policy on top, independent of what any individual request asks for. Virtual keys are Bifrost's core governance entity: the same key that carries a budget, a rate limit, and a set of allowed models also carries the MCP tool allowlist, so removing a virtual key's access to an MCP server immediately flips its stored per-user credentials to an orphaned state, invisible to the runtime, without anyone having to hunt down and revoke a token by hand.

The result is that authentication and tool filtering answer different questions and enforce independently. Building only the first, and assuming a valid token implies safe access, is how an authenticated caller ends up with unauthorized reach.

Enterprise Rollout: Federated Auth and Identity-Provider Trust

Enterprise MCP deployments add a constraint that smaller setups can skip: dozens or hundreds of internal APIs that were never built with per-caller OAuth support, and a workforce identity provider that already knows who every employee and service account is. Wrapping each of those APIs in a bespoke OAuth flow does not scale, and issuing shared credentials to every internal MCP server reintroduces the accountability problem the specification's authorization model exists to prevent.

Bifrost's token_exchange auth type addresses this directly: it requires every caller's identity-provider token on every request rather than a stored per-user credential, exchanging it on a cache miss with a short in-memory TTL, so offboarding a user at the identity provider is reflected within minutes instead of requiring a manual credential revoke. On the enterprise side, MCP with federated auth turns existing internal APIs into MCP tools without custom glue code, and role-based access control layers organization-defined roles on top of the virtual-key tool filters described above. Every credential, whether OAuth-based or exchanged, surfaces on a single MCP Sessions view where an admin can see status, force re-authentication, or revoke access outright.

Common MCP Authentication Failure Modes

Most MCP authentication incidents trace back to a handful of repeated mistakes rather than novel attacks, and they line up closely with what the security risks of ungoverned MCP server access documents in production deployments:

  • Treating STDIO like HTTP. STDIO transports inherit credentials from the spawning process's environment. Applying an HTTP-style OAuth flow to a local STDIO tool adds complexity without adding security, since the trust boundary is already the local machine.
  • Skipping resource indicators. Omitting RFC 8707 resource indicators when the authorization server supports them leaves a token valid across every MCP server the authorization server issues for, not just the one the caller intended to reach.
  • Passing tokens through unchanged. Forwarding a caller's token to a downstream API without validating that it was issued to the MCP server first is the token passthrough anti-pattern the specification explicitly forbids, and it erases the audit trail needed to attribute a downstream call to a specific caller.
  • Confusing authentication with authorization. A valid, correctly scoped token proves identity. It does not imply the caller should reach every tool the MCP server exposes; that requires an independent filtering or RBAC layer.
  • Leaving orphaned credentials live. When access to an MCP server is revoked at the policy layer, per-user credentials that are not automatically flipped to an inactive state remain usable until someone manually finds and deletes them.

MCP Authentication FAQ

Does MCP require OAuth?

No. Authorization is optional in the MCP specification. When an HTTP-based MCP server does implement it, the specification requires OAuth 2.1 with mandatory PKCE rather than leaving the choice of auth framework open, since STDIO servers rely on the local process environment instead.

Do MCP servers have authentication?

Not by default. Local STDIO servers inherit credentials from their environment, and remote HTTP or SSE servers only enforce authentication if the operator configures it, using either a shared server-level credential or a per-user credential tied to each caller's identity.

What is MCP in cyber security?

In a security context, MCP (Model Context Protocol) is the open standard that lets AI agents discover and call external tools at runtime. Its security surface centers on authenticating the caller, authorizing which tools that caller can invoke, and preventing tokens from being replayed against a server they were not issued for.

How does MCP authentication work?

An MCP client authenticates against an authorization server, separate from the MCP server itself, using OAuth 2.1 with PKCE. The authorization server issues a token scoped to that specific MCP server, which the MCP server validates on every request before executing a tool call. Bifrost's auth-type configuration applies this per MCP server rather than globally.

What is the difference between MCP authentication and authorization?

Authentication verifies who is calling. Authorization determines what that verified caller is permitted to do, typically through tool filtering, role-based access control, or a virtual key policy applied independently of the authentication step.

Why is token passthrough forbidden in MCP?

Token passthrough breaks security controls like rate limiting and traffic monitoring that depend on a token's audience, and it removes the MCP server's ability to attribute a downstream API call to a specific client, since the token was never validated as belonging to that server.

Can one MCP gateway support multiple authentication types at once?

Yes. Authentication type is typically configured per MCP server rather than globally, so a single gateway can run a public tool with no authentication, an internal API with a shared credential, and a personal integration with per-user OAuth, side by side, each layered with its own tool filtering rules.

Every one of these failure modes gets harder to catch by hand as the number of connected MCP servers grows, which is why identity resolution, tool filtering, and credential lifecycle need to live in the gateway layer rather than in each individual MCP server's own code. Governance practices that extend beyond authentication, including audit logging and policy rollout, are covered in MCP server governance best practices. For Bifrost's own auth-type configuration walkthrough, MCP server authentication: how to secure agent tool access covers the setup steps this post did not repeat.

The OWASP Top 10 for Agentic Applications is a useful external reference for how identity and tool-access weaknesses fit into the broader agentic risk picture beyond MCP specifically. Teams running agents that also lean on Code Mode or Agent Mode for multi-tool orchestration inherit the same authentication and tool-filtering rules described above; neither mode bypasses them.

Getting MCP authentication right means treating it as one layer in a stack, not the whole stack: OAuth 2.1 and resource indicators establish who is calling, identity resolution decides which credential applies, and tool filtering or RBAC decides what happens next. For the full walkthrough of that stack again from the OAuth-and-token-management side, see MCP authentication explained, OAuth, API keys, and token management. Bifrost implements identity resolution, tool filtering, and credential lifecycle as configuration rather than custom code across every connected MCP server. To see it running against a real deployment, book a demo with the Bifrost team.