SSOGEN / Learn / MCP Authentication: What the Specification Requires
Learn
MCP Authentication: What the Specification Requires
A protected MCP server acts as an OAuth resource server, answers an unauthenticated request with a 401 pointing to its metadata, and accepts only tokens issued for it.
Updated

MCP authentication is how a client proves it may call a protected server in the Model Context Protocol. It is built on OAuth:
- Resource server. The MCP server.
- OAuth client. The client.
- Authorization server. A separate server that issues the tokens.
This page follows the authorization section of the specification, revision 2026-07-28, which was current when we read it.
Check the revision you build against.
When authorization applies at all
Authorization is optional in MCP, and the transport decides whether it applies:
- HTTP-based transport. A transport that supports authorization should follow the specification.
- Stdio. A stdio server should not; it takes credentials from its environment.
In practice, “MCP authentication” means HTTP servers you expose to more than one trusted user.
The specification builds on an IETF draft, OAuth 2.1 (draft-ietf-oauth-v2-1-13), plus several published RFCs. Because OAuth 2.1 is still a draft, some products describe their own behavior as OAuth 2.0 with extensions.
The flow, step by step
| Step | Who | What happens | Standard |
|---|---|---|---|
| 1 | Client | Calls the MCP server with no token | |
| 2 | Server | Replies 401 with a WWW-Authenticate header naming its metadata URL in resource_metadata |
RFC 9728, section 5.1 |
| 3 | Client | Fetches that metadata and reads authorization_servers |
RFC 9728 |
| 4 | Client | Fetches the authorization server’s metadata | RFC 8414 or OpenID Connect Discovery |
| 5 | Client | Gets a client ID (a metadata document URL, pre-registration or dynamic registration) | Client ID Metadata Documents, RFC 7591 |
| 6 | Client | Starts authorization with PKCE and a resource parameter naming the server |
PKCE, RFC 8707 |
| 7 | Person | Signs in and approves | |
| 8 | Client | Exchanges the code for a token, again sending resource |
|
| 9 | Client | Sends Authorization: Bearer <token> on every request |
RFC 6750 |
| 10 | Server | Checks the token was issued for this server, then runs the tool |
An annotated 401 and metadata document
This is a server’s answer at step 2, with fictional values (Acme Corp).
HTTP/1.1 401 UnauthorizedWWW-Authenticate: Bearer resource_metadata="https://mcp.acme.example/.well-known/oauth-protected-resource", scope="invoices:read"resource_metadata. Tells the client where to look next.scope. The server’s guidance for the current operation. Servers should include it, and clients treat it as authoritative for that request.
The metadata document the URL returns:
{ "resource": "https://mcp.acme.example", "authorization_servers": ["https://login.acme.example"], "scopes_supported": ["invoices:read", "invoices:write"], "bearer_methods_supported": ["header"]}resourcemust match. It must match the identifier the metadata URL was built from, as RFC 9728 requires. This prevents anyone from feeding a client another server’s metadata.authorization_servers. The document must include it with at least one entry.
A server that can’t send the WWW-Authenticate hint may publish the document at /.well-known/oauth-protected-resource instead, at the root or at the path of the MCP endpoint.
What an MCP server must do
- Publish metadata. Publish Protected Resource Metadata so clients can find the authorization server.
- Return 401. Return
401for a missing, invalid or expired token. - Check the audience. Check that each token was issued for this server as its audience.
- Accept only its own tokens. Accept only tokens meant for its own resources. Never accept or pass on any other token.
- Return 403. Return
403witherror="insufficient_scope"and the neededscopewhen a valid token lacks permission for the operation, so the client can ask for more. - Header only. Never read the token from the URL query string. The client sends it in the
Authorizationheader on every HTTP request.
The authorization server must offer metadata through RFC 8414 or OpenID Connect Discovery.
It must advertise PKCE in code_challenge_methods_supported. If that field is missing, the client must stop.
Common mistakes
| Mistake | Why it hurts | Fix |
|---|---|---|
| No audience check | A token issued for another service works here | Verify the audience against your resource identifier on every request |
| Token passthrough | The server forwards the client’s token to an upstream API, so the upstream cannot tell who really called | Use a separate token issued to the server for the upstream API |
403 where 401 belongs (or the reverse) |
Clients do not know whether to re-authenticate or ask for more scope | 401 for no or bad token, 403 for insufficient scope |
Metadata resource does not match the URL |
Clients reject it | Make the identifier exact, including path |
| No PKCE advertised | Compliant clients refuse to connect | Publish code_challenge_methods_supported |
| One broad scope for every tool | A single grant opens everything | Define scopes per tool or group of tools |
| Checking only at session start | Later calls run unchecked | Validate on every request |
The specification’s security section also covers redirect URIs (exact match, localhost or HTTPS), short-lived access tokens and token theft. Read it with the authorization section, not after.
What a scope can and cannot do
- What a scope does. A scope decides whether a tool may be called; it does not inspect what the call contains.
- What it does not do. An agent with the
approve_invoicescope can approve any invoice the server allows, so enforce limits on amounts and records in the server as well.
How the SSOGEN MCP Gateway handles authentication
SSOGEN’s MCP Gateway sits in front of your MCP server and checks each tool call.
- No token. A request without a token gets
401with aWWW-Authenticateheader carryingresource_metadata. - Metadata. Its protected-resource metadata lists the SSOGEN sign-in server in
authorization_serversand the scopes inscopes_supported. - Scope mapping. You map scopes to the exact tool names your server advertises.
- Out-of-scope calls. A call to a tool outside the agent’s scopes gets
403withinsufficient_scope.
SSOGEN is stricter than the baseline in two places:
- It accepts only tokens bound to the agent’s key through DPoP, sent as
Authorization: DPoPwith a proof on every request. It refusesBearertokens, so a client that can’t send DPoP proofs can’t connect. - An administrator registers every agent in advance, in place of dynamic client registration.
- No token forwarding. The Gateway doesn’t forward the agent’s token to your server; it passes identity headers instead.
- Minimal reading. It reads only the method and tool name, never the arguments.
MCP Gateway explains the checks, and the Oracle EBS MCP guide covers setup.
Questions people ask
What is MCP authentication?
It’s the OAuth-based way an MCP client proves it may call a protected MCP server, using a token an authorization server issued for that server.
Does MCP require OAuth?
Authorization is optional. If an HTTP-based server supports it, follow the specification’s OAuth-based flow. Stdio servers should read credentials from the environment.
How does a client find the authorization server?
From the server’s Protected Resource Metadata document, found through the resource_metadata value in the 401 response or a well-known URL.
Can an MCP server pass the client’s token to another API?
No. The specification forbids it. The server must get its own token for the upstream API.
Where to go next
- The attack these checks stop: the confused deputy problem.
- If you are new to MCP, start with MCP vs API.
- For the wider picture, read agentic AI security.
- To see what a token is bound to, read non-human identity.
- How SSOGEN issues credentials to agents: AI agent identity.
- Applied to an ERP: AI agent access to Oracle EBS without a password.
- The client’s PKCE step, with a generator, is in what is PKCE.
Sources, all read 2026-10-09: MCP specification, Authorization (2026-07-28), RFC 9728, RFC 8707.