MCP Auth
LoginRadius provides a complete OAuth 2.1 Authorization Server implementation for securing MCP (Model Context Protocol) servers. Tool calls in MCP can carry user-delegated permissions — any MCP server exposed without authentication has no identity boundary, no scope enforcement, and no revocation surface. Securing MCP with a proper authorization layer ensures that every tool call is tied to a verified identity, operates within granted scopes, and can be audited or revoked at any time.
LoginRadius handles token issuance, client registration, and PKCE enforcement for your MCP-connected applications. At a high level, the flow works as follows: the MCP Client registers with LoginRadius (via CIMD or DCR), then initiates an OAuth 2.1 authorization code flow with PKCE — the User Agent completes interactive authentication in the browser, the client exchanges the authorization code for a short-lived access token, and all subsequent tool calls to the MCP Server carry that token as a Bearer credential. The MCP Server validates the token (directly or via introspection) before executing any tool.
Actor Relationship
Participants
These four actors are canonical across all MCP auth flows. Use these terms verbatim when reading the CIMD and DCR flow pages.
| Actor | Role |
|---|---|
| MCP Client | The AI agent or host initiating tool calls (e.g., Claude Desktop, a custom agent) |
| MCP Server | The service exposing tools; acts as the OAuth resource server |
| Authorization Server | The OAuth 2.1 / OIDC provider; LoginRadius in this context |
| User Agent | The browser that completes the interactive authorization step |
Protocol Requirements
- OAuth 2.1 — MCP mandates OAuth 2.1. Authorization code flow only; implicit grant forbidden. → MCP Authorization spec
- PKCE (RFC 7636) — S256 code challenge required on every authorization request. → RFC 7636
- Bearer tokens (RFC 6750) — Short-lived access tokens; refresh token rotation; no long-lived credentials. → RFC 6750
- Resource indicators (RFC 8707) — The client declares the target resource server in the authorization request. → LoginRadius resource indicators docs
Auth Server Discovery
Before a client can request tokens, it must locate the Authorization Server's endpoints. MCP defines a structured discovery sequence:
-
Attempt an unauthenticated tool call. The MCP Client sends an initial request to the MCP Server without a token.
The MCP Server responds with
401 Unauthorizedand aWWW-Authenticateheader containing aresource_metadataURL pointing to the server's protected resource metadata document.Header: WWW-Authenticate: Bearer realm="mcp", resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource" -
Fetch the resource metadata. The MCP Client GETs the resource metadata URL — typically at
{server-origin}/.well-known/oauth-protected-resource.The response identifies the Authorization Server's issuer URI:
Finding Your IssuerYour issuer URL is displayed in the LoginRadius Dashboard under your app's Endpoints tab in the OIDC Endpoint URLs section. Copy it directly from there.
{"resource": "https://mcp.example.com/mcp","authorization_servers": ["https://<tenant>.hub.loginradius.com/service/oidc/{app}"]} -
Fetch the Authorization Server metadata. Using the issuer URI from step 2, the MCP Client GETs:
https://<tenant>.hub.loginradius.com/.well-known/oauth-authorization-server/service/oidc/{app}If this returns no result, the client falls back to
/.well-known/openid-configurationper spec.LoginRadius metadata URL pattern:
{issuer}/.well-known/oauth-authorization-serverThe response contains all endpoints and capability flags needed to proceed. The four fields most critical to MCP auth:
{"registration_endpoint": "https://<tenant>.hub.loginradius.com/service/oidc/{app}/register","code_challenge_methods_supported": ["S256"],"client_id_metadata_document_supported": true,"authorization_response_iss_parameter_supported": true}Important Considerations- Entry point for both DCR client registration. All clients must register here before requesting tokens.
- Confirms PKCE enforcement. Only
S256is accepted — plain challenge method is rejected. - Signals that this Authorization Server supports CIMD. When
true, a public client can use an HTTPS URL as itsclient_idinstead of registering inline. - Mix-up attack protection per RFC 9207. The
issparameter in the authorization response lets the client verify the response came from the expected Authorization Server.
-
Proceed to client registration. With
registration_endpointin hand, the client registers using DCR or directly present the Metadata Document URL during authorize call in case of CIMD - see Client Registration below.
Client Registration
Before a client can request tokens, it must register with the Authorization Server. LoginRadius supports two registration methods — Client Initiated Metadata Document (CIMD) for public clients and Dynamic Client Registration (DCR) for confidential clients — which can be independently toggled per OIDC application.
Configure MCP Auth on LoginRadius
Setting up MCP Auth on your LoginRadius tenant requires four steps: creating an OIDC application with MCP features enabled, registering an API resource that represents your MCP server, and linking the two together with the appropriate scopes, Implement OAuth server discovery and Protected Resource Metadata endpoint on your MCP server.
Step 1: Create an App
-
In the LoginRadius Dashboard, navigate to Applications and click Create App. Enter a name for your application.
-
Inside the app, open the General tab. Under Credentials and Security, set Token Endpoint Auth Method to
Client Secret Post. -
In the Grant Settings section, confirm that
Authorization Codeis enabled as a grant type. This is the only grant type permitted under OAuth 2.1 for MCP.
Before proceeding, you will need to enable MCP feature on your LoginRadius tenant. Contact Loginradius Support
- Under MCP Registration, enable CIMD, DCR, or both depending on which client registration method(s) your MCP server will support. See Client Registration for guidance on choosing between them.
Step 2: Create an API Resource
An API resource represents your MCP server as a protected resource in LoginRadius. The identifier you configure here is what clients pass as the resource parameter in their authorization requests.
-
Navigate to Authorization → APIs and click Add API.
-
Enter a Name for your API resource. This is for your reference only and does not appear in tokens.
-
Enter the Identifier URL — the exact URL of your MCP server (e.g.,
https://mcp.example.com/mcp). This value is used as theresourceparameter in the authorization request: step 7 of the CIMD flow and step 9 of the DCR flow. -
Configure additional API settings as needed such as scopes, user access policies and token lifetime. refer to Configure API Settings documentation.
Step 3: Link the API Resource to Your App
With the app and API resource created, attach them so the Authorization Server knows which resources your app is permitted to request tokens for.
-
Open your app and attach the API resource created in Step 2 and optionally define scopes on your API resource. → Attach an API Resource to Your App
-
To understand how Scope intersection work with RBAC disbaled or enabled on API resources, refer to Scope Resolution documentation
Step 4: Configure the Protected Resource Metadata Endpoint
Your MCP server must expose the Protected Resource Metadata document at a well-known endpoint so that clients can discover your Authorization Server during the Auth Server Discovery phase. This metadata tells clients where to find the Authorization Server and what scopes are available.
Metadata Endpoint Location
The Protected Resource Metadata endpoint must be accessible at one of these locations on your MCP server:
- At the root:
https://mcp.example.com/.well-known/oauth-protected-resource - At the endpoint path:
https://mcp.example.com/mcp/.well-known/oauth-protected-resource/mcp
See RFC 9728 Section 5 (OAuth 2.0 Protected Resource Metadata) for full details on the well-known endpoint structure. The MCP protocol mandates this discovery mechanism — see the MCP Authorization specification: Protected Resource Metadata Discovery Requirements.
Construct the Metadata Document
Configure your MCP server to return a JSON document at the endpoint above with the following required and recommended fields:
Required fields:
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": [
"https://<tenant>.hub.loginradius.com/service/oidc/{app}"
]
}
| Field | Description | Example |
|---|---|---|
resource | The canonical URI of your MCP server. Must exactly match the Identifier URL configured in Step 2. Used by clients as the resource parameter in authorization requests per RFC 8707. | https://mcp.example.com/mcp |
authorization_servers | Array of issuer URIs pointing to your LoginRadius Authorization Server(s). At minimum, include one issuer URL. | ["https://<tenant>.hub.loginradius.com/service/oidc/{app}"] |
Recommended fields:
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": [
"https://<tenant>.hub.loginradius.com/service/oidc/{app}"
],
"scopes_supported": [
"openid",
"profile",
"email",
"custom:read",
"custom:write"
]
}
| Field | Description | Example |
|---|---|---|
scopes_supported | Array of scopes available for this resource. Clients use this list during the scope selection strategy when requesting authorization. Include all scopes defined on your API resource in Step 2. | ["openid", "profile", "email", "files:read", "files:write"] |
The scopes_supported array must include scopes you've defined on your API resource in LoginRadius. These scopes are what clients request during authorization and what the Authorization Server enforces on tool calls. Refer to Configure API Settings to see how to define scopes on your resource.
Authorization Server Issuer URL Format
Replace the placeholders in your issuer URL:
{tenant}— Your LoginRadius tenant subdomain (e.g.,mycompany-prod){app}— Your OIDC application ID from Step 1
LoginRadius issuer URL pattern:
https://{tenant}.hub.loginradius.com/service/oidc/{app}
Example:
https://acme-prod.hub.loginradius.com/service/oidc/mcp-app-12345
Implementation Example
Here's a minimal server implementation that serves the Protected Resource Metadata endpoint:
Node.js
FastAPI (Python)
const express = require('express');
const app = express();
// Serve Protected Resource Metadata at /.well-known/oauth-protected-resource
app.get('/.well-known/oauth-protected-resource', (req, res) => {
res.json({
resource: "https://mcp.example.com/mcp",
authorization_servers: [
"https://<tenant>.hub.loginradius.com/service/oidc/<app>"
],
scopes_supported: [
"openid",
"profile",
"email",
"tools:execute"
]
});
});
// Your MCP tool endpoint
app.post('/mcp', (req, res) => {
// Validate bearer token here
// Execute tools
res.json({ result: "..." });
});
from fastapi import FastAPI, HTTPException, Header
from typing import Optional
app = FastAPI()
@app.get("/.well-known/oauth-protected-resource")
async def protected_resource_metadata():
return {
"resource": "https://mcp.example.com/mcp",
"authorization_servers": [
"https://<tenant>.hub.loginradius.com/service/oidc/<app>"
],
"scopes_supported": [
"openid",
"profile",
"email",
"tools:execute"
]
}
@app.post("/mcp")
async def mcp_endpoint(authorization: Optional[str] = Header(None)):
# Extract and validate bearer token from Authorization header
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Unauthorized")
token = authorization[7:] # Strip "Bearer " prefix
# Validate token...
# Execute tools
return {"result": "..."}
Verify the Endpoint
Test that your endpoint is accessible and returns valid JSON:
curl -i https://mcp.example.com/.well-known/oauth-protected-resource
You should see:
HTTP/1.1 200 OK
Content-Type: application/json
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": ["https://<tenant>.hub.loginradius.com/service/oidc/<app>"],
"scopes_supported": ["openid", "profile", "email", "tools:execute"]
}
The endpoint must be served over HTTPS per OAuth 2.1 Section 1.5 (Communication Security).
WWW-Authenticate Header (Optional but Recommended)
When a client makes an unauthenticated request to your MCP server (step 1 of Auth Server Discovery), return a 401 Unauthorized response with a WWW-Authenticate header pointing to your metadata endpoint:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp", resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource", scope="tools:execute"
| Parameter | Description |
|---|---|
realm | Identifies the protection space (e.g., "mcp") |
resource_metadata | URL to your Protected Resource Metadata endpoint |
scope | (Optional) Space-separated list of scopes required for the current request. Guides clients on what to request. |
The WWW-Authenticate header mechanism is defined in RFC 9728 Section 5.1 and the MCP spec: Protected Resource Metadata Discovery Requirements.
Node.js/Express example with WWW-Authenticate:
app.post('/mcp', (req, res) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res
.status(401)
.set('WWW-Authenticate', 'Bearer realm="mcp", resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource", scope="tools:execute"')
.json({ error: "unauthorized" });
}
// Validate token and execute tools
res.json({ result: "..." });
});
CIMD vs DCR
| CIMD | DCR | |
|---|---|---|
| Client type | Public | Confidential |
| Metadata location | Hosted at client URL | Inline in POST body |
| Hosted URL required | Yes | No |
| Client secret issued | No | Optional (client_secret_post / client_secret_basic) |
| Best for | Open/distributed clients | Server-side / enterprise clients |
Best Practices
- In your MCP server, make sure you include
WWW-Authenticateheaders for all 401 responses with the URL where you host the PRM. - Make sure you serve metadata (PRM) at a publicly accessible URL.
- When configuring Resource Indicator for your MCP server, make sure the identifier matches your MCP server URI exactly.
- Ensure the access token audience (
aud) claim matches your MCP server URI exactly.