Overview
xmcp provides built-in authentication middleware to secure your MCP server. Choose from API key authentication for simple scenarios or JWT authentication for token-based auth flows.
packages/xmcp/src/index.ts
export { apiKeyAuthMiddleware } from "./auth/api-key" ;
export { jwtAuthMiddleware } from "./auth/jwt" ;
API Key Authentication
Validate requests using API keys from custom headers.
Static API Key
Validate against a single static API key:
examples/middlewares-api-key/src/middleware.ts
import { apiKeyAuthMiddleware } from "xmcp" ;
export default apiKeyAuthMiddleware ({
headerName: "x-api-key" ,
validateApiKey : async ( apiKey ) => {
return apiKey === process . env . API_KEY ! ;
} ,
// or `apiKey: process.env.API_KEY` To set a static key
}) ;
Custom Validation
Implement custom validation logic (e.g., database lookup):
import { apiKeyAuthMiddleware } from "xmcp" ;
export default apiKeyAuthMiddleware ({
headerName: "x-api-key" ,
validateApiKey : async ( apiKey ) => {
// Query your database or external service
const user = await db . users . findByApiKey ( apiKey );
return user !== null ;
} ,
}) ;
Configuration Options
Static API key to validate against (mutually exclusive with validateApiKey)
validateApiKey
(key: string) => Promise<boolean>
Custom async function to validate the API key (mutually exclusive with apiKey)
HTTP header name to read the API key from
Implementation Details
The middleware validates requests as follows:
packages/xmcp/src/auth/api-key.ts
export function apiKeyAuthMiddleware (
config : StaticApiKeyConfig | CustomValidationConfig
) : RequestHandler {
const headerName = config . headerName ?? "x-api-key" ;
const apiKey = "apiKey" in config ? config . apiKey : undefined ;
const validateApiKey =
"validateApiKey" in config ? config . validateApiKey : undefined ;
return async ( req : Request , res : Response , next : NextFunction ) => {
const apiKeyHeader = req . header ( headerName );
if ( ! apiKeyHeader ) {
res . status ( 401 ). json ({ error: errorMessage });
return ;
}
if ( "apiKey" in config && apiKeyHeader !== apiKey ) {
res . status ( 401 ). json ({ error: errorMessage });
return ;
}
if ( validateApiKey ) {
const isValid = await validateApiKey ( apiKeyHeader );
if ( ! isValid ) {
res . status ( 401 ). json ({ error: errorMessage });
return ;
}
}
next ();
};
}
JWT Authentication
Validate JWT tokens with full support for jsonwebtoken verify options.
Basic JWT Auth
examples/middlewares-jwt/src/middleware.ts
import { jwtAuthMiddleware } from "xmcp" ;
export default jwtAuthMiddleware ({
secret: process . env . JWT_SECRET ! ,
algorithms: [ "HS256" ] ,
}) ;
Advanced JWT Configuration
Use all jsonwebtoken verify options:
import { jwtAuthMiddleware } from "xmcp" ;
export default jwtAuthMiddleware ({
secret: process . env . JWT_SECRET ! ,
algorithms: [ "HS256" ] ,
issuer: "https://example.com" ,
audience: "https://api.example.com" ,
subject: "user-id" ,
clockTolerance: 30 ,
}) ;
Configuration Options
JWT secret key for token verification
Allowed signing algorithms (e.g., ["HS256"], ["RS256"])
Clock tolerance in seconds for time-based validations
Implementation Details
The JWT middleware extracts and verifies Bearer tokens:
packages/xmcp/src/auth/jwt.ts
export function jwtAuthMiddleware (
config : JWTAuthMiddlewareConfig
) : RequestHandler {
return ( req : Request , res : Response , next : NextFunction ) => {
const authHeader = req . header ( "Authorization" );
if ( ! authHeader || ! authHeader . startsWith ( "Bearer " )) {
res . status ( 401 ). json ({
error: "Unauthorized: Missing or malformed Authorization header" ,
});
return ;
}
const token = authHeader . slice ( "Bearer " . length ). trim ();
if ( ! token ) {
res . status ( 401 ). json ({ error: "Unauthorized: Missing access token" });
return ;
}
try {
const { secret , ... verifyOptions } = config ;
const decoded = jwt . verify ( token , secret , verifyOptions ) as JwtPayload ;
( req as any ). user = decoded ;
next ();
} catch ( err ) {
res . status ( 401 ). json ({ error: "Unauthorized: Invalid or expired token" });
}
};
}
The decoded JWT payload is attached to req.user for access in your tools.
Access authentication information in your tool handlers.
Auth0 Example
The auth0 provider example shows how to access user info:
examples/auth0-http/src/tools/whoami.ts
import type { ToolMetadata } from "xmcp" ;
import { getAuthInfo } from "@xmcp-dev/auth0" ;
export const metadata : ToolMetadata = {
name: "whoami" ,
description: "Returns the full Auth0 user session and account information" ,
};
export default function whoami () : string {
const authInfo = getAuthInfo ();
const userInfo = {
user: {
id: authInfo . user . sub ,
email: authInfo . user . email ?? "N/A" ,
name: authInfo . user . name ?? "N/A" ,
clientId: authInfo . clientId ,
},
token: {
scopes: authInfo . scopes ,
expiresAt: authInfo . expiresAt
? new Date ( authInfo . expiresAt * 1000 ). toISOString ()
: "N/A" ,
},
};
return JSON . stringify ( userInfo , null , 2 );
}
Custom JWT Access
Access the decoded JWT from the request object:
import { InferSchema , type ToolMetadata } from "xmcp" ;
import { z } from "zod" ;
export const schema = {
message: z . string (). describe ( "Message to log" ),
};
export const metadata : ToolMetadata = {
name: "log-message" ,
description: "Log a message with user context" ,
};
export default function handler (
{ message } : InferSchema < typeof schema >,
{ req } : any
) {
// Access the decoded JWT
const user = req . user ;
console . log ( `User ${ user . sub } : ${ message } ` );
return `Logged message for user ${ user . sub } ` ;
}
Auth Providers
For OAuth and OpenID Connect flows, use community auth providers:
examples/auth0-http/src/middleware.ts
import { auth0Provider } from "@xmcp-dev/auth0" ;
export default auth0Provider ({
domain: process . env . DOMAIN ! ,
audience: process . env . AUDIENCE ! ,
baseURL: process . env . BASE_URL ! ,
clientId: process . env . CLIENT_ID ! ,
clientSecret: process . env . CLIENT_SECRET ! ,
}) ;
The @xmcp-dev/auth0 package provides a complete Auth0 integration with OAuth flows and user session management.
Error Responses
All built-in auth middleware returns consistent error responses:
{
"error" : "Unauthorized: Missing or invalid API key"
}
HTTP status code: 401 Unauthorized
Best Practices
Use Environment Variables Never hardcode secrets. Always use process.env for API keys and JWT secrets
Validate in Production Use custom validation functions to check against databases or external services
Set Clock Tolerance For JWT, set clockTolerance to handle clock skew between servers
Combine Middleware Use middleware arrays to combine auth with logging, rate limiting, etc.
Next Steps
Middleware Learn more about middleware architecture
Transports Configure HTTP and STDIO transports