Authentication Guide
Complete authentication guide including static headers, dynamic providers, AuthProvider interface, token refresh patterns, and examples.
Authentication Overview
OpenAPI MCP Server supports two main authentication methods to accommodate different API requirements:
🔗 Static Headers
Perfect for APIs with long-lived tokens, API keys, or simple authentication
- Simple configuration
- No token refresh logic needed
- Ideal for CLI usage
- Good for development and testing
🔄 Dynamic Authentication (AuthProvider)
Advanced authentication with token refresh, expiration handling, and custom logic
- Automatic token refresh
- Expiration detection
- Retry on authentication errors
- Custom authentication flows
Static Headers Authentication
Static headers are the simplest authentication method, perfect for APIs with long-lived tokens or API keys.
CLI Usage
# Single header
npx @lucid-spark/openapi-mcp-server openapi-mcp-server \
--api-base-url https://api.example.com \
--openapi-spec https://api.example.com/openapi.json \
--headers "Authorization:Bearer your-long-lived-token"
# Multiple headers
npx @lucid-spark/openapi-mcp-server openapi-mcp-server \
--api-base-url https://api.github.com \
--openapi-spec https://api.github.com/openapi.json \
--headers "Authorization:Bearer ghp_xxxxxxxxxxxx,Accept:application/vnd.github.v3+json,User-Agent:MyApp/1.0"
Environment Variables
# Set environment variables
export API_BASE_URL="https://api.example.com"
export OPENAPI_SPEC_PATH="https://api.example.com/openapi.json"
export API_HEADERS="Authorization:Bearer your-token,X-API-Key:your-api-key"
# Run server
npx @lucid-spark/openapi-mcp-server openapi-mcp-server
Library Usage
import { OpenAPIServer } from "@lucid-spark/openapi-mcp-server";
const server = new OpenAPIServer({
apiBaseUrl: "https://api.example.com",
openApiSpec: "https://api.example.com/openapi.json",
headers: {
"Authorization": "Bearer your-long-lived-token",
"X-API-Key": "your-api-key",
"Accept": "application/json",
"User-Agent": "MyApp/1.0"
},
transportType: "stdio"
});
await server.start();
Common Static Header Patterns
Bearer Token (OAuth/JWT)
--headers "Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
API Key in Header
--headers "X-API-Key:your-api-key,Authorization:ApiKey your-api-key"
Basic Authentication
# Base64 encode username:password first
--headers "Authorization:Basic dXNlcm5hbWU6cGFzc3dvcmQ="
Custom Headers
--headers "X-Client-ID:your-client-id,X-Secret:your-secret"
Dynamic Authentication (AuthProvider)
The AuthProvider interface enables sophisticated authentication scenarios with token refresh, expiration handling, and custom error recovery.
AuthProvider Interface
interface IAuthProvider {
/**
* Get authentication headers for the current request
* Called before each API request to get fresh headers
*/
getAuthHeaders(): Promise>;
/**
* Handle authentication errors from API responses
* Called when the API returns 401 or 403 errors
* Return true to retry the request, false otherwise
*/
handleAuthError(error: AxiosError): Promise;
}
Key Features
🔄 Dynamic Headers
Fresh authentication headers for each request
⏰ Token Expiration Handling
Automatic detection and handling of expired tokens
🔁 Authentication Error Recovery
Retry logic for recoverable authentication failures
💬 Custom Error Messages
Provide clear, actionable guidance to users
Basic AuthProvider Implementation
import { IAuthProvider } from "@lucid-spark/openapi-mcp-server";
import { AxiosError } from "axios";
class SimpleAuthProvider implements IAuthProvider {
constructor(private token: string) {}
async getAuthHeaders(): Promise> {
return {
Authorization: `Bearer ${this.token}`,
"User-Agent": "OpenAPI-MCP-Server/1.0.0"
};
}
async handleAuthError(error: AxiosError): Promise {
if (error.response?.status === 401) {
throw new Error("Authentication failed. Please check your token.");
}
return false; // Don't retry other errors
}
}
Token Refresh Patterns
Implement automatic token refresh for APIs with short-lived tokens:
OAuth2 Token Refresh
class OAuth2AuthProvider implements IAuthProvider {
private accessToken: string;
private refreshToken: string;
private tokenExpiry: Date;
constructor(accessToken: string, refreshToken: string, expiresIn: number) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
this.tokenExpiry = new Date(Date.now() + expiresIn * 1000);
}
async getAuthHeaders(): Promise> {
// Check if token is expired or will expire soon (60 second buffer)
if (this.isTokenExpiring()) {
await this.refreshAccessToken();
}
return {
Authorization: `Bearer ${this.accessToken}`
};
}
async handleAuthError(error: AxiosError): Promise {
if (error.response?.status === 401) {
// Token might be invalid, try to refresh
try {
await this.refreshAccessToken();
return true; // Retry with new token
} catch (refreshError) {
throw new Error("Token refresh failed. Please re-authenticate.");
}
}
return false;
}
private isTokenExpiring(): boolean {
const bufferTime = 60 * 1000; // 60 seconds
return this.tokenExpiry.getTime() <= (Date.now() + bufferTime);
}
private async refreshAccessToken(): Promise {
const response = await fetch("https://oauth.example.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: this.refreshToken,
client_id: process.env.CLIENT_ID!,
client_secret: process.env.CLIENT_SECRET!
})
});
if (!response.ok) {
throw new Error(`Token refresh failed: ${response.statusText}`);
}
const data = await response.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token || this.refreshToken;
this.tokenExpiry = new Date(Date.now() + data.expires_in * 1000);
}
}
Manual Token Management
For APIs where users must manually obtain new tokens:
class ManualTokenAuthProvider implements IAuthProvider {
private token: string | null = null;
private tokenExpiry: Date | null = null;
async getAuthHeaders(): Promise> {
if (!this.token || this.isTokenExpired()) {
throw new Error(
"Token expired. Please get a new token:\n" +
"1. Go to https://api.example.com/settings/tokens\n" +
"2. Generate a new token\n" +
"3. Update using updateToken() method"
);
}
return {
Authorization: `Bearer ${this.token}`
};
}
async handleAuthError(error: AxiosError): Promise {
if (error.response?.status === 401) {
throw new Error("Token invalid. Please generate a new token.");
}
return false;
}
updateToken(token: string, expiresInHours: number = 24): void {
this.token = token;
this.tokenExpiry = new Date(Date.now() + expiresInHours * 60 * 60 * 1000);
}
private isTokenExpired(): boolean {
return !this.tokenExpiry || this.tokenExpiry <= new Date();
}
}
Real-World Authentication Examples
GitHub API
# Using Personal Access Token
npx @lucid-spark/openapi-mcp-server openapi-mcp-server \
--api-base-url https://api.github.com \
--openapi-spec https://api.github.com/openapi.json \
--headers "Authorization:Bearer ghp_xxxxxxxxxxxx,Accept:application/vnd.github.v3+json,User-Agent:MyApp/1.0"
Stripe API
# Using Secret Key
npx @lucid-spark/openapi-mcp-server openapi-mcp-server \
--api-base-url https://api.stripe.com \
--openapi-spec https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json \
--headers "Authorization:Bearer sk_test_xxxxxxxxxxxx,Stripe-Version:2023-10-16"
OpenAI API
# Using API Key
npx @lucid-spark/openapi-mcp-server openapi-mcp-server \
--api-base-url https://api.openai.com \
--openapi-spec https://api.openai.com/openapi.json \
--headers "Authorization:Bearer sk-xxxxxxxxxxxx,OpenAI-Organization:org-xxxxxxxxxxxx"
AWS API Gateway with IAM
// Custom AuthProvider for AWS Signature V4
class AWSAuthProvider implements IAuthProvider {
constructor(
private accessKeyId: string,
private secretAccessKey: string,
private region: string = 'us-east-1'
) {}
async getAuthHeaders(): Promise> {
// Implement AWS Signature V4 signing
const signature = await this.signRequest();
return {
Authorization: signature,
'X-Amz-Date': new Date().toISOString().replace(/[:-]/g, ''),
'X-Amz-Security-Token': process.env.AWS_SESSION_TOKEN || ''
};
}
async handleAuthError(error: AxiosError): Promise {
if (error.response?.status === 403) {
throw new Error("AWS authentication failed. Check your credentials.");
}
return false;
}
private async signRequest(): Promise {
// AWS Signature V4 implementation
// This is a simplified example - use aws-sdk for production
return `AWS4-HMAC-SHA256 Credential=${this.accessKeyId}/...`;
}
}
Claude Desktop Authentication
Configure authentication for Claude Desktop using environment variables:
Basic Token Authentication
{
"mcpServers": {
"github-api": {
"command": "npx",
"args": ["-y", "@lucid-spark/openapi-mcp-server", "openapi-mcp-server"],
"env": {
"API_BASE_URL": "https://api.github.com",
"OPENAPI_SPEC_PATH": "https://api.github.com/openapi.json",
"GITHUB_TOKEN": "ghp_xxxxxxxxxxxx",
"API_HEADERS": "Authorization:Bearer ${GITHUB_TOKEN},Accept:application/vnd.github.v3+json"
}
}
}
}
Multiple Headers
{
"mcpServers": {
"stripe-api": {
"command": "npx",
"args": ["-y", "@lucid-spark/openapi-mcp-server", "openapi-mcp-server"],
"env": {
"API_BASE_URL": "https://api.stripe.com",
"OPENAPI_SPEC_PATH": "https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json",
"STRIPE_SECRET_KEY": "sk_test_xxxxxxxxxxxx",
"API_HEADERS": "Authorization:Bearer ${STRIPE_SECRET_KEY},Stripe-Version:2023-10-16,User-Agent:Claude-MCP-Client"
}
}
}
}
Security Best Practices
🔐 Token Management
- Never hardcode tokens in source code
- Use environment variables for sensitive data
- Rotate tokens regularly
- Use the minimum required permissions
🔒 Storage
- Store tokens in secure credential managers
- Avoid logging authentication headers
- Use encrypted storage for refresh tokens
- Clear tokens from memory when possible
🚨 Error Handling
- Don't expose tokens in error messages
- Implement proper token validation
- Handle network failures gracefully
- Log authentication events for monitoring
⏰ Token Lifecycle
- Implement automatic token refresh
- Use appropriate token expiration times
- Handle concurrent refresh requests
- Invalidate tokens on logout
Authentication Troubleshooting
❌ 401 Unauthorized Errors
Symptoms: API returns 401 status code
Solutions:
- Verify token is valid and not expired
- Check header format (Bearer vs Basic vs API Key)
- Ensure token has required permissions
- Test token directly with curl or Postman
# Test token directly
curl -H "Authorization: Bearer your-token" https://api.example.com/test
❌ 403 Forbidden Errors
Symptoms: API returns 403 status code
Solutions:
- Check token permissions/scopes
- Verify API endpoint access
- Review rate limiting policies
- Check IP allowlists
❌ Token Refresh Failures
Symptoms: AuthProvider refresh attempts fail
Solutions:
- Verify refresh token is valid
- Check OAuth client credentials
- Ensure refresh endpoint URL is correct
- Review token expiration timing
❌ Header Format Issues
Symptoms: Headers not recognized by API
Solutions:
- Check exact header name requirements
- Verify value format (Bearer, Basic, etc.)
- Ensure proper encoding (Base64 for Basic auth)
- Review API documentation for examples