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