How to secure your MCP server with OAuth resource indicators
How audience-bound tokens keep your MCP servers secure.
As the Model Context Protocol (MCP) matures into a serious standard for connecting AI systems to external services, its authentication story has had to keep pace. One of the more consequential additions to MCP's auth layer is support for resource indicators, a mechanism borrowed from OAuth 2.0 that lets clients and servers be precise about which protected resources a token is valid for. This article explains what resource indicators are, why they matter in the MCP context, and how to implement them correctly.
What are resource indicators?
Resource indicators are a feature of OAuth 2.0 defined in RFC 8707. In short, they allow an OAuth client to declare, at the time of requesting a token, which specific resource server (or servers) the token is intended for. This declaration is passed as a resource parameter in the authorization or token request.
Without resource indicators, an access token is often a general-purpose credential. Any resource server that can validate the token might accept it, regardless of whether the client ever intended that server to have access. With resource indicators, the authorization server binds the issued token to a specific audience, and resource servers can verify that binding before accepting a request.
A simple example looks like this:
The resulting token will carry an aud (audience) claim restricted to https://api.example.com/mcp. If that token is later presented to a different resource server, the server should reject it.
Why resource indicators matter for MCP
MCP clients frequently operate in environments where multiple MCP servers are available, potentially from different organizations and with different trust levels. A single client might connect to an internal company server, a third-party data tool, and a public API all within the same session.
In this kind of multi-server environment, token confusion attacks become a real concern. If a token intended for Server A can be used against Server B, a compromised or malicious Server B can replay or misuse it. Resource indicators close this gap by ensuring that each token is scoped to the server it was issued for.
There are three related risks that resource indicators directly address:
- Token reuse across servers. Without audience binding, nothing stops a credential obtained for one MCP server from being replayed at another that trusts the same authorization server. Resource indicators make the intended audience explicit and enforceable.
- Confused deputy attacks. An MCP server that acts as a client to other services (a common pattern in agentic workflows) could, without proper audience scoping, forward a token it received in an upstream context. Resource indicators help ensure tokens flow only to their intended destinations.
- Over-permissioned tokens. Even when a token's scopes look narrow, a token valid across many resource servers represents a broader attack surface than one that's audience-restricted. Resource indicators support the principle of least privilege at the token level.
How MCP uses resource indicators
MCP's authorization framework builds on OAuth 2.0 and OpenID Connect. When an MCP client initiates authentication, it is expected to include a resource parameter identifying the MCP server it wants to access. The value is a URI, typically the canonical base URL of the MCP server.
The authorization server processes this parameter and, if supported, issues a token whose aud claim contains (or is equal to) the requested resource URI. The MCP server, upon receiving a request, validates not just the token's signature and expiry but also that its aud claim matches the server's own identity.
The flow in practice looks like this:
- The MCP client discovers the MCP server's authorization metadata, including the authorization server endpoint.
- The client initiates an OAuth authorization request, passing
resource=https://mcp.example.comalongside the requested scopes. - The authorization server authenticates the user and issues an authorization code.
- The client exchanges the code for an access token. The resulting token carries
"aud": "https://mcp.example.com". - The client presents this token to the MCP server in the
Authorizationheader. - The MCP server validates the token and checks that its
audclaim matches its own base URL. If it does not match, the request is rejected with a 401.
Implementation guidance
For MCP server developers
When building an MCP server that enforces resource indicators, there are a few key responsibilities.
First, advertise your server's resource URI consistently. The URI you publish in your OAuth protected resource metadata (the resource field) must match exactly what clients will put in their resource parameter and what you will compare against the incoming token's aud claim. Inconsistencies here are a common source of hard-to-debug auth failures.
Second, always validate the aud claim. Token validation that checks signature and expiry but ignores audience is incomplete. Many JWT libraries support audience validation as a configuration option; enable it and set the expected audience to your server's resource URI.
Third, return informative errors. When a token is rejected because of an audience mismatch, a 401 response with a WWW-Authenticate header that includes an error=invalid_token and a meaningful error_description will save client developers significant debugging time.
For MCP client developers
Clients are responsible for including the resource parameter in both authorization requests and token requests. Some authorization servers will ignore the parameter if it is not also present at the token exchange step, so including it in both places is the safe practice.
If you are building a client that connects to multiple MCP servers, you will typically need a separate access token for each server, each obtained with that server's resource URI. Managing this token pool correctly (including refresh and expiry handling per token) is an important part of robust client implementation.
If you encounter a 401 with error=invalid_token after what you believe was a successful auth flow, the first thing to check is whether the aud claim in your token matches the resource URI you are trying to access.
For authorization server operators
To support resource indicators properly, your authorization server needs to implement RFC 8707. This means accepting and validating the resource parameter, rejecting requests for unsupported or unauthorized resource URIs, and embedding the resource URI in the aud claim of issued tokens.
Some authorization servers support resource indicators only for certain grant types or only when the feature is explicitly enabled. Verify your server's configuration and test the behavior before relying on it in production.
You should also document the expected resource URI format for each MCP server you protect. Clients need to know the exact string to use, and even minor differences (trailing slashes, HTTP vs HTTPS, path casing) can cause mismatches.
Common pitfalls
- URI canonicalization.
https://mcp.example.comandhttps://mcp.example.com/are technically different strings. Pick a canonical form and use it everywhere: in your metadata document, in client configuration, and in your token validation logic. - Falling back to a permissive audience check. Under time pressure, it can be tempting to accept tokens with a missing or mismatched
audclaim rather than debug the problem. This undermines the entire point of resource indicators. Use a permissive check only in a sandboxed development environment, never in production. - Assuming all authorization servers support RFC 8707. Some widely used auth platforms do not yet support resource indicators, or support them only in enterprise tiers. If you are evaluating an authorization server for MCP deployment, check for explicit RFC 8707 support in the documentation.
- Not testing token rejection. It is easy to test the happy path where a valid, audience-matched token is accepted. It is equally important to test that tokens with the wrong audience are rejected, and that the error responses are correct and informative.
Relationship to other MCP auth concepts
Resource indicators work alongside, not instead of, OAuth scopes. Scopes define what actions a token permits; the resource URI defines where those actions are permitted. A well-formed MCP token will have both narrow scopes and a specific audience.
Resource indicators also interact with token introspection and dynamic client registration, both of which are relevant in larger MCP deployments. When using introspection, the introspection response should include the aud claim so that resource servers can verify it without parsing a JWT. When using dynamic registration, clients should be registered with an explicit list of resource URIs they are permitted to request tokens for.
Conclusion
Resource indicators are a straightforward addition to the OAuth token request, but they close a meaningful security gap in multi-server environments like those MCP is designed to support. By binding each token to a specific resource URI, they prevent token reuse across servers, limit blast radius in the event of token compromise, and make the intended flow of credentials explicit and auditable. For anyone building or operating MCP servers and clients, implementing resource indicators is a practical step toward a more secure and predictable auth architecture.
Get MCP auth right with WorkOS AuthKit
If you would rather not build and maintain the authorization server infrastructure described in this article, WorkOS AuthKit handles it for you. AuthKit supports resource indicators for MCP auth natively. When an MCP client requests a token, AuthKit scopes it to the specific MCP server the client asked for, so a token issued for one server cannot be used with another. You register your MCP server's resource URI in the WorkOS dashboard, and AuthKit takes care of embedding the correct aud claim. Your MCP server only needs to verify those tokens and expose a /.well-known/oauth-protected-resource metadata endpoint.
AuthKit also supports Client ID Metadata Document and Dynamic Client Registration for backwards compatibility, so your server works with the widest range of MCP clients without extra configuration. For teams that already have an existing auth system, Standalone Connect lets you plug AuthKit's OAuth infrastructure in alongside your current login flow rather than replacing it.
Read the AuthKit MCP integration guide to get started.