npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcpresso-oauth-server

v1.2.17

Published

Production-ready OAuth 2.1 server implementation for Model Context Protocol (MCP) with PKCE support

Readme

mcpresso-oauth-server

A production-ready OAuth 2.1 server implementation designed specifically for Model Context Protocol (MCP) servers. Provides seamless authentication integration with mcpresso servers.

Features

  • OAuth 2.1 Compliant - Full implementation of OAuth 2.1 draft specification
  • MCP Integration - Designed specifically for Model Context Protocol
  • PKCE Support - Proof Key for Code Exchange (required for MCP)
  • Custom User Authentication - Pluggable login logic and UI customization
  • Production Ready - Security headers, rate limiting, compression, CORS
  • Flexible Deployment - Run standalone or integrated with MCP servers
  • Dynamic Client Registration - RFC 7591 compliant
  • Multiple Grant Types - Authorization code, refresh token, client credentials
  • Token Introspection - RFC 7662 compliant
  • Discovery Endpoints - RFC 8414 and RFC 9728 compliant

Quick Start

Installation

npm install mcpresso-oauth-server
# or
bun add mcpresso-oauth-server

Basic Usage

import { 
  MCPOAuthServer, 
  MemoryStorage 
} from 'mcpresso-oauth-server'

// Initialize storage with demo data
const storage = new MemoryStorage()
await storage.createClient({
  id: 'demo-client',
  secret: 'demo-secret',
  name: 'Demo Client',
  type: 'confidential',
  redirectUris: ['http://localhost:4000/callback'],
  scopes: ['read', 'write'],
  grantTypes: ['authorization_code']
})

// Create OAuth server with custom authentication
const oauthServer = new MCPOAuthServer({
  issuer: 'http://localhost:4000',
  serverUrl: 'http://localhost:4000',
  jwtSecret: 'your-secret-key',
  auth: {
    authenticateUser: async (credentials, context) => {
      // Your login logic here
      const user = users.find(u => u.username === credentials.username)
      return user && validatePassword(credentials.password, user.password) ? user : null
    },
    getCurrentUser: async (sessionData, context) => {
      // Check if user is already logged in via session/cookies
      return null // Force login for this example
    },
    renderLoginPage: async (context, error) => {
      // Optional: Customize login page
      return `<html>...custom login form...</html>`
    }
  }
}, storage)

MCP Integration

Two Deployment Modes

Mode 1: Integrated (Same Port)

Run OAuth and MCP server together:

import { createMCPServer } from 'mcpresso'
import { MCPOAuthServer } from 'mcpresso-oauth-server'

// Create OAuth server
const oauthServer = new MCPOAuthServer({...}, storage)

// Create MCP server with integrated OAuth
const app = createMCPServer({
  name: "integrated_server",
  resources: [userResource],
  auth: {
    oauth: oauthServer,              // Integrate OAuth server
    userLookup: async (jwtPayload) => {
      // Fetch full user profiles from your database
      return await db.users.findById(jwtPayload.sub)
    }
  }
})

app.listen(4000) // Both OAuth and MCP on port 4000

Architecture:

┌─────────────────────────────────┐
│        Integrated Server        │
│         (Port 4000)             │
│                                 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │OAuth Service│ │ MCP Service │ │
│ │• /authorize │ │• MCP API    │ │
│ │• /token     │ │• Resources  │ │
│ │• Login UI   │ │• Tools      │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────┘

Mode 2: Separate Servers

Run OAuth and MCP on different ports:

// OAuth Server (Port 4001)
const oauthApp = express()
oauthApp.use(cors()) // Important for cross-origin requests
registerOAuthEndpoints(oauthApp, oauthServer)
oauthApp.listen(4001)

// MCP Server (Port 4000)
const mcpApp = createMCPServer({
  name: "api_server",
  resources: [userResource],
  auth: {
    issuer: "http://localhost:4001",      // OAuth server URL
    serverUrl: "http://localhost:4000",   // This MCP server URL
    jwtSecret: "shared-secret",           // Same secret as OAuth server
    userLookup: async (jwtPayload) => {
      return await db.users.findById(jwtPayload.sub)
    }
  }
})
mcpApp.listen(4000)

Architecture:

┌─────────────────┐    ┌──────────────────┐
│   OAuth Server  │    │    MCP Server    │
│   (Port 4001)   │    │   (Port 4000)    │
│                 │    │                  │
│ • /authorize    │    │ • MCP API        │
│ • /token        │    │ • Token Validation│
│ • Login UI      │    │ • Resources      │
└─────────────────┘    └──────────────────┘

Custom Authentication

User Authentication Callbacks

Implement your own user authentication logic:

const oauthServer = new MCPOAuthServer({
  // ... other config
  auth: {
    // Required: Validate user credentials
    authenticateUser: async (credentials, context) => {
      const { username, password } = credentials
      
      // Example with database lookup
      const user = await db.users.findByEmail(username)
      if (!user) return null
      
      // Verify password (use bcrypt in production)
      const isValid = await bcrypt.compare(password, user.hashedPassword)
      if (!isValid) return null
      
      // Return user object
      return {
        id: user.id,
        username: user.email,
        email: user.email,
        scopes: user.permissions // e.g., ['read', 'write', 'admin']
      }
    },
    
    // Optional: Check existing sessions
    getCurrentUser: async (sessionData, context) => {
      if (sessionData?.userId) {
        return await db.users.findById(sessionData.userId)
      }
      return null
    },
    
    // Optional: Custom login page
    renderLoginPage: async (context, error) => {
      return `
        <!DOCTYPE html>
        <html>
          <head><title>Login - ${context.clientId}</title></head>
          <body>
            <h2>Login to ${context.clientId}</h2>
            ${error ? `<p style="color:red">${error}</p>` : ''}
            <form method="POST" action="/authorize">
              <!-- Hidden OAuth parameters -->
              <input type="hidden" name="response_type" value="code">
              <input type="hidden" name="client_id" value="${context.clientId}">
              <input type="hidden" name="redirect_uri" value="${context.redirectUri}">
              <input type="hidden" name="scope" value="${context.scope || ''}">
              
              <!-- Login form -->
              <div>
                <label>Email:</label>
                <input type="email" name="username" required>
              </div>
              <div>
                <label>Password:</label>
                <input type="password" name="password" required>
              </div>
              <button type="submit">Login</button>
            </form>
          </body>
        </html>
      `
    }
  }
}, storage)

Integration Examples

With Supabase

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(url, key)

const oauthServer = new MCPOAuthServer({
  auth: {
    authenticateUser: async ({ username, password }) => {
      const { data, error } = await supabase.auth.signInWithPassword({
        email: username,
        password: password
      })
      
      if (error || !data.user) return null
      
      return {
        id: data.user.id,
        username: data.user.email,
        email: data.user.email,
        scopes: data.user.user_metadata?.scopes || ['read']
      }
    }
  }
})

With Firebase

import { signInWithEmailAndPassword } from 'firebase/auth'

const oauthServer = new MCPOAuthServer({
  auth: {
    authenticateUser: async ({ username, password }) => {
      try {
        const userCredential = await signInWithEmailAndPassword(auth, username, password)
        const user = userCredential.user
        
        return {
          id: user.uid,
          username: user.email,
          email: user.email,
          scopes: user.customClaims?.scopes || ['read']
        }
      } catch (error) {
        return null
      }
    }
  }
})

Configuration

Environment Variables

Configure the server using environment variables:

# Server configuration
OAUTH_ISSUER=https://auth.yourdomain.com
OAUTH_SERVER_URL=https://auth.yourdomain.com
OAUTH_JWT_SECRET=your-super-secret-jwt-key

# CORS configuration (for separate server deployment)
CORS_ORIGIN=https://yourdomain.com,https://api.yourdomain.com
TRUST_PROXY=true

# Server port
PORT=4001

Security Configuration

The server includes production-ready security features:

const oauthServer = new MCPOAuthServer({
  // ... OAuth config
  http: {
    cors: {
      origin: ['https://yourdomain.com'],
      credentials: true,
      methods: ['GET', 'POST', 'OPTIONS']
    },
    enableHelmet: true,
    enableRateLimit: true,
    rateLimitConfig: {
      windowMs: 15 * 60 * 1000, // 15 minutes
      max: 100 // requests per window
    }
  }
})

API Endpoints

OAuth 2.1 Endpoints

  • GET /authorize - Authorization endpoint (shows login page)
  • POST /authorize - Authorization endpoint (processes login)
  • POST /token - Token endpoint
  • POST /introspect - Token introspection
  • POST /revoke - Token revocation
  • GET /userinfo - User info endpoint
  • POST /register - Dynamic client registration

Discovery Endpoints

  • GET /.well-known/oauth-authorization-server - OAuth metadata (RFC 8414)
  • GET /.well-known/jwks.json - JSON Web Key Set
  • GET /.well-known/oauth-protected-resource - MCP protected resource metadata

Admin Endpoints

  • GET /health - Health check
  • GET /admin/clients - List clients
  • GET /admin/users - List users
  • GET /admin/stats - Server statistics

Storage

The package includes an in-memory storage implementation for development. For production, implement the MCPOAuthStorage interface:

import type { MCPOAuthStorage } from 'mcpresso-oauth-server'

class DatabaseStorage implements MCPOAuthStorage {
  async createClient(client: OAuthClient): Promise<void> {
    // Store client in your database
  }
  
  async getClient(clientId: string): Promise<OAuthClient | null> {
    // Fetch client from your database
  }
  
  // ... implement all required methods
}

Examples

See complete working examples:

Testing

Manual OAuth Flow Testing

  1. Get Authorization Code:
# Open in browser or use curl
curl "http://localhost:4001/authorize?response_type=code&client_id=demo-client&redirect_uri=http://localhost:4001/callback&scope=read&resource=http://localhost:4000"
  1. Exchange for Token:
curl -X POST "http://localhost:4001/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&client_id=demo-client&client_secret=demo-secret&code=YOUR_CODE&redirect_uri=http://localhost:4001/callback&resource=http://localhost:4000"
  1. Use Token with MCP API:
curl -X POST "http://localhost:4000" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami_user","arguments":{}}}'

Development

Build & Test

# Install dependencies
bun install

# Build the package
bun run build

# Run tests
bun test

# Start development server
bun run dev

Security Best Practices

For production deployments:

  1. Use Strong JWT Secrets - Generate cryptographically secure secrets
  2. Configure CORS Properly - Only allow trusted origins
  3. Enable HTTPS - Always use HTTPS in production
  4. Implement Proper Storage - Use a production database, not memory storage
  5. Monitor & Log - Set up proper logging and monitoring
  6. Keep Dependencies Updated - Regularly update packages
  7. Rate Limiting - Configure appropriate rate limits
  8. Input Validation - Validate all user inputs

Support

License

MIT - See LICENSE file for details.