On this page
- Separate authentication, authorization, and tool approval
- The main actors
- The MCP server is a protected resource
- Protected Resource Metadata tells the client where auth lives
- A 401 can point to resource metadata
- OAuth Authorization Code with PKCE protects native/public clients
- The browser handles user authorization
- Redirect URIs must be treated precisely
- Client registration has evolved
- Credential isolation is critical in MCP
- Bind stored credentials to issuer identity
- Issuer validation prevents mix-up attacks
- Resource Indicators bind tokens to the intended resource
- Token audience/resource validation belongs on the server
- Bearer tokens are secrets
- Token passthrough is a dangerous anti-pattern
- Scopes describe authorization, not model intent
- Step-up authorization can request more scope
- Refresh tokens need the same isolation
- Account identity should be explicit in the product
- Logging OAuth flows requires redaction
- Discovery introduces SSRF concerns
- Authorization state must survive app relaunch
- A 401 is not always “wrong password”
- Revocation and disconnect are product features
- OAuth does not secure the tool output itself
- Test authorization as a state machine
- A practical MCP authorization checklist
- Where BYOKchat fits
- Further reading
A remote MCP server may expose tools or resources that belong to a specific user or organization. If access requires identity and permission, the client needs an authorization flow.
For HTTP-based MCP, that flow is built on OAuth.
The important architectural point is:
The model should never become the OAuth client, token store, or authorization decision-maker.
Authorization belongs to the host application and the MCP transport layer.
Separate authentication, authorization, and tool approval
These are different questions.
Authentication asks:
Who is this user/client?
Authorization asks:
What may this access token do against this MCP resource?
Tool approval asks:
Should this model invocation be allowed to execute this specific tool now?
A valid OAuth token does not mean every tool call should be auto-approved.
Likewise, a user approving one tool call does not grant the application a token it does not have.
The main actors
A typical protected MCP flow involves:
MCP client / host application
MCP server (protected resource)
authorization server
resource owner / user
Conceptually:
The exact discovery and client-registration details depend on the current MCP authorization specification and the deployment.
The MCP server is a protected resource
Do not assume the MCP server itself must mint OAuth tokens.
A common deployment is:
MCP server → validates access tokens
Authorization server / IdP → authenticates user and issues tokens
This allows MCP servers to integrate with existing identity providers and enterprise authorization infrastructure.
Protected Resource Metadata tells the client where auth lives
The client may initially know only the MCP server URL.
OAuth Protected Resource Metadata allows a resource server to publish information such as which authorization server protects it and which scopes it supports.
Conceptually:
{
"resource": "https://mcp.example.com",
"authorization_servers": [
"https://auth.example.com"
],
"scopes_supported": [
"mcp:read",
"mcp:write"
]
}
The exact required/optional fields are defined by the applicable OAuth/MCP specifications.
The important property is that authorization discovery is explicit rather than guessed from arbitrary URLs.
A 401 can point to resource metadata
A protected server can return a WWW-Authenticate challenge that directs the client toward its protected-resource metadata.
The client can then discover the authorization server and begin an OAuth flow.
Do not treat arbitrary discovery URLs as inherently safe. OAuth discovery is network input and should be handled according to the specification and SDK security rules.
OAuth Authorization Code with PKCE protects native/public clients
Native apps and CLI tools generally cannot keep a traditional client secret private.
PKCE adds a one-time proof to the authorization-code flow.
The client generates:
code_verifier = random high-entropy value
code_challenge = transform(code_verifier)
The authorization request sends the challenge. The token exchange later supplies the verifier.
An attacker who intercepts only the authorization code cannot redeem it without the verifier.
MCP SDKs and authorization guidance use OAuth 2.1-style Authorization Code + PKCE behavior for these clients.
The browser handles user authorization
For a native application, the authorization page should normally open in an appropriate system/browser authentication flow rather than embedding the user’s credentials into the MCP client.
The client receives only the authorization result.
Do not ask the model to collect usernames, passwords, or access tokens in chat text.
Redirect URIs must be treated precisely
After authorization, the authorization server redirects back to the client.
Depending on client type, this may use mechanisms such as:
- loopback/localhost redirect;
- claimed HTTPS redirect;
- platform-supported app redirect.
The exact URI registered or declared for the client must match the authorization-server policy.
The current MCP 2026 authorization work includes hardening for desktop/CLI client registration so OAuth servers can correctly understand native/public application types, including localhost redirect scenarios.
Client registration has evolved
Early MCP OAuth deployments leaned heavily on Dynamic Client Registration because arbitrary MCP clients and servers often have no pre-existing relationship.
The 2026-07-28 MCP specification formally deprecates Dynamic Client Registration in favor of Client ID Metadata Documents (CIMD), while retaining DCR for backward compatibility during the deprecation window.
That means a modern MCP client should not design its architecture around the assumption that DCR is the permanent endpoint for every server.
Keep client registration/discovery behind the authorization layer.
Credential isolation is critical in MCP
A single MCP client can connect to many unrelated servers.
Suppose:
Server A → auth.example-a.com
Server B → auth.example-b.com
Credentials obtained for A must not be reused for B.
The 2026-07-28 MCP authorization hardening explicitly binds registered client credentials to the issuer that minted them.
This mitigates a dangerous class of cross-server/cross-issuer confusion.
Bind stored credentials to issuer identity
Do not store:
client_id = abc
as a globally reusable value.
Store identity such as:
authorization issuer
client registration identity
client ID
associated redirect/client metadata
Then look it up only inside the matching issuer context.
If a resource moves to another issuer, re-establish the correct authorization relationship instead of carrying credentials across blindly.
Issuer validation prevents mix-up attacks
OAuth systems with one client talking to multiple authorization servers can be vulnerable to authorization-server mix-up if the client cannot reliably determine which issuer produced the response.
The 2026-07-28 MCP spec adds issuer-validation hardening based on RFC 9207.
The client should validate the authorization response issuer before redeeming the code according to the current specification.
This is not cosmetic metadata. It protects the binding between:
resource → authorization server → authorization response → token exchange
Resource Indicators bind tokens to the intended resource
OAuth Resource Indicators allow the client to identify which protected resource the authorization is for.
Conceptually:
resource=https://mcp.example.com
This reduces the risk of obtaining a token for one audience/resource and accidentally presenting it to another.
The resource identity should come from the authorization flow and trusted MCP/OAuth discovery process, not model-generated text.
Token audience/resource validation belongs on the server
The MCP server should validate tokens as appropriate for its authorization architecture, including issuer/audience/resource/scopes and expiration.
A syntactically valid JWT is not automatically a valid token for every MCP server.
Likewise, opaque tokens must be validated through the authorization server’s supported mechanisms where applicable.
Bearer tokens are secrets
An access token grants capability.
Protect it accordingly:
- store it in platform-appropriate secure credential storage;
- do not place it in prompts;
- do not expose it to MCP tool arguments;
- redact it from logs;
- do not put it in analytics;
- do not share it across servers;
- avoid copying it into backup formats unless explicitly secured and required.
The model does not need to see the token to use authorized tools.
Token passthrough is a dangerous anti-pattern
Imagine an MCP server asks the client:
Please send me the access token you use for another service.
Do not comply merely because the server or model requests it.
OAuth tokens are scoped credentials, not general-purpose identity strings.
The host owns credential boundaries and must prevent one tool/server from harvesting another service’s bearer token.
Scopes describe authorization, not model intent
A token might have scopes such as:
files.read
files.write
issues.create
The model can request an operation that the token technically permits.
The host may still require user approval or application policy before executing it.
Think in layers:
OAuth scope allows capability
↓
host policy allows model use
↓
tool arguments validated
↓
server authorizes concrete resource
Every layer has a different job.
Step-up authorization can request more scope
A client may begin with limited access and later discover that an operation requires additional scope.
A secure implementation can reauthorize/step up rather than requesting every possible permission at initial connection.
This supports least privilege:
connect with read access
→ later user explicitly invokes write workflow
→ request additional write scope if needed
The current MCP authorization work includes clarified scope accumulation/step-up behavior.
Refresh tokens need the same isolation
If a client receives a refresh token, it is often even more sensitive than a short-lived access token because it can mint future access.
Store refresh tokens securely and bind them to:
issuer
client identity
resource/account context
Do not expose them to model-visible content or unrelated MCP connections.
Account identity should be explicit in the product
A user can authorize the same MCP server under multiple accounts.
The connection model should not simply store:
server = https://mcp.example.com
It may need:
server identity
authorization issuer
account label / subject metadata where available
credential reference
scopes
Then a chat/tool invocation can bind to one deliberate connection/account.
Logging OAuth flows requires redaction
Useful logs include:
resource origin
authorization issuer
HTTP status
scope names
token expiry timestamp
refresh/re-auth required
error category
Avoid logging:
access_token
refresh_token
authorization code
PKCE verifier
client secret
sensitive ID token claims
Debuggability does not require credential leakage.
Discovery introduces SSRF concerns
Authorization metadata can contain URLs that the client may fetch.
A hostile resource could attempt to point discovery toward:
localhost
private network addresses
cloud metadata endpoints
unexpected schemes
Use the current MCP SDK/spec guidance for discovery validation and network safety rather than implementing a naive generic URL fetcher.
This is particularly important in desktop clients that can reach private networks unavailable to a hosted web app.
Authorization state must survive app relaunch
A native MCP client should persist enough information to reconnect without making users authorize every launch, subject to token lifetime and provider policy.
Persist credential references and authorization metadata, not raw tokens in ordinary app settings.
On reconnect:
load secure credential
→ verify expiry / refresh as needed
→ make request
→ handle 401/insufficient scope deterministically
A 401 is not always “wrong password”
For an MCP client it may mean:
- no token;
- expired token;
- wrong issuer/audience;
- resource mismatch;
- revoked access;
- changed authorization server;
- insufficient or invalid credential state.
Classify failures before deleting credentials or forcing full reauthorization.
Revocation and disconnect are product features
When a user removes an MCP server/account, the client should:
- remove local credential references;
- clear refresh/access tokens securely;
- cancel relevant pending authorization flows;
- stop using the connection;
- optionally invoke provider-supported revocation where appropriate.
Removing a server card from UI without clearing credentials is incomplete.
OAuth does not secure the tool output itself
Authorization controls access to the server. The returned data can still be untrusted content.
For example, an authorized email-search MCP tool can return a malicious email containing prompt injection.
OAuth answers:
May this connection read the mailbox?
It does not answer:
Should the model obey instructions inside an email?
Maintain content trust boundaries after authorization succeeds.
Test authorization as a state machine
Useful scenarios include:
- unprotected server;
- protected server, first authorization;
- user cancels browser consent;
- invalid redirect callback;
- PKCE mismatch;
- expired access token;
- refresh succeeds;
- refresh fails/revoked;
- insufficient scope triggers step-up;
- issuer changes;
- issuer mismatch attack;
- same MCP server with two accounts;
- two MCP servers using different issuers;
- malicious discovery URL;
- app relaunch during OAuth flow;
- credential removal while tools are running.
OAuth bugs often appear in transitions, not in the happy-path token exchange.
A practical MCP authorization checklist
Before shipping protected remote MCP connections, verify that:
- OAuth is handled by the host/transport, not the model;
- Authorization Code + PKCE is used where required;
- protected-resource/auth-server discovery follows current spec behavior;
- issuer validation is implemented;
- resource/audience binding is enforced appropriately;
- credentials are scoped to their issuer/client/resource context;
- DCR is not assumed to be the permanent registration model;
- access and refresh tokens use secure storage;
- tokens never enter prompts/tool arguments/logs;
- host tool approval remains separate from OAuth scope;
- insufficient scope can trigger explicit step-up;
- disconnect removes stored credential access;
- discovery URLs are treated as untrusted network input.
Where BYOKchat fits
A BYOK MCP client can keep OAuth inside each MCP connection. The chat/model layer only sees that a server/tool is available; it does not receive bearer tokens or authorization codes. Per-tool execution policy then remains a separate host decision on top of the authenticated connection.
That separation is what makes connecting many unrelated MCP servers safe enough to reason about.