{"_id":"@connexa/mcp-oauth-firebase-middleware","_rev":"4-1adde57221eba700b078b43a16ab4657","name":"@connexa/mcp-oauth-firebase-middleware","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@connexa/mcp-oauth-firebase-middleware","version":"1.0.0","keywords":["oauth","oauth2","firebase","mcp","model-context-protocol","authentication","middleware","pkce"],"author":"","license":"MIT","_id":"@connexa/mcp-oauth-firebase-middleware@1.0.0","maintainers":[{"name":"cxa-cobi","email":"choward@connexa.com"},{"name":"cxa-croper","email":"croper@connexa.com"}],"homepage":"https://github.com/yourusername/mcp-oauth-firebase-middleware#readme","bugs":{"url":"https://github.com/yourusername/mcp-oauth-firebase-middleware/issues"},"dist":{"shasum":"ad05ee1d9673384b7e5a105cb0823d491372d52f","tarball":"https://registry.npmjs.org/@connexa/mcp-oauth-firebase-middleware/-/mcp-oauth-firebase-middleware-1.0.0.tgz","fileCount":10,"integrity":"sha512-9cK2ZClfpFI/+KlDi2lPO+4r+/0HB+j2DOomyDM/SPni9Rh/xots19Jtg74hgQKumIymgY3Xe4a7UR1sCbghCg==","signatures":[{"sig":"MEUCIQDGXywIh9IKVvhM9KTHt0rKyvH6t4dHLRwKfig4seEpAwIgSiOdjXSlsMfaCvRdRn2zSe60uo1BsR1DOblAsUugAZ8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":46579},"main":"lib/index.js","engines":{"node":">=18.0.0"},"private":false,"scripts":{"dev":"node server.js","test":"echo \"Error: no test specified\" && exit 1","start":"node server.js"},"_npmUser":{"name":"cxa-cobi","email":"choward@connexa.com"},"repository":{"url":"git+https://github.com/yourusername/mcp-oauth-firebase-middleware.git","type":"git"},"_npmVersion":"10.9.3","description":"OAuth 2.0 middleware for MCP servers using Firebase Auth backend","directories":{},"_nodeVersion":"22.18.0","dependencies":{"firebase":"^10.7.1","firebase-admin":"^12.0.0"},"publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"devDependencies":{"cors":"^2.8.5","dotenv":"^16.3.1","express":"^4.18.2"},"peerDependencies":{"express":"^4.18.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-oauth-firebase-middleware_1.0.0_1767111159654_0.8419048089243395","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@connexa/mcp-oauth-firebase-middleware","version":"1.0.1","keywords":["oauth","oauth2","firebase","mcp","model-context-protocol","authentication","middleware","pkce"],"author":{"name":"Connexa"},"license":"MIT","_id":"@connexa/mcp-oauth-firebase-middleware@1.0.1","maintainers":[{"name":"cxa-cobi","email":"choward@connexa.com"},{"name":"cxa-croper","email":"croper@connexa.com"}],"homepage":"https://github.com/connexa/mcp-oauth-firebase-middleware#readme","bugs":{"url":"https://github.com/connexa/mcp-oauth-firebase-middleware/issues"},"dist":{"shasum":"e790f6176e9dcb6dec3ac2716bd280c7d3d8f219","tarball":"https://registry.npmjs.org/@connexa/mcp-oauth-firebase-middleware/-/mcp-oauth-firebase-middleware-1.0.1.tgz","fileCount":11,"integrity":"sha512-xXZ66MAN/F8HzPhTg5A1UOmYdGzZ7KzTRwEF1hBJthymGtojKYNBK+QH9KGoyGCCozBrxMzn04DdNBI4kEP9BA==","signatures":[{"sig":"MEYCIQC6AjBTYRx9RSYjMe5EotKeHhaiOl3QusBlZdQdCoswdAIhAJeX3Rj7usySX0HeoXNr7NPgAGC36z1pd8Ki5AdRhULc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":53925},"jest":{"verbose":true,"forceExit":true,"testMatch":["**/test/integration/**/*.test.js"],"testTimeout":10000,"testEnvironment":"node","detectOpenHandles":true,"setupFilesAfterEnv":["<rootDir>/test/setup/jest.setup.js"]},"main":"lib/index.js","engines":{"node":">=18.0.0"},"private":false,"scripts":{"dev":"node server.js","test":"jest --runInBand --verbose","start":"node server.js","test:watch":"jest --watch","test:server":"node test/mcp-test-server.js","test:coverage":"jest --coverage"},"_npmUser":{"name":"cxa-cobi","email":"choward@connexa.com"},"repository":{"url":"git+https://github.com/connexa/mcp-oauth-firebase-middleware.git","type":"git"},"_npmVersion":"10.9.3","description":"OAuth 2.0 middleware for MCP servers using Firebase Auth backend","directories":{},"_nodeVersion":"22.18.0","dependencies":{"firebase":"^10.7.1","firebase-admin":"^12.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"cors":"^2.8.5","jest":"^29.7.0","axios":"^1.13.2","dotenv":"^16.3.1","express":"^4.18.2","@modelcontextprotocol/sdk":"^1.0.0"},"peerDependencies":{"express":"^4.18.0"},"_npmOperationalInternal":{"tmp":"tmp/mcp-oauth-firebase-middleware_1.0.1_1767607524926_0.6453393703695185","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@connexa/mcp-oauth-firebase-middleware","version":"1.0.2","description":"OAuth 2.0 middleware for MCP servers using Firebase Auth backend","private":false,"main":"lib/index.js","scripts":{"start":"node server.js","dev":"node server.js","test":"jest --runInBand --verbose","test:watch":"jest --watch","test:coverage":"jest --coverage","test:server":"node test/mcp-test-server.js"},"keywords":["oauth","oauth2","firebase","mcp","model-context-protocol","authentication","middleware","pkce"],"author":{"name":"Connexa"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/connexa/mcp-oauth-firebase-middleware.git"},"publishConfig":{"access":"public"},"dependencies":{"firebase":"^10.7.1","firebase-admin":"^12.0.0"},"peerDependencies":{"express":"^4.18.0 || ^5.0.0"},"devDependencies":{"@modelcontextprotocol/sdk":"^1.0.0","axios":"^1.13.2","cors":"^2.8.5","dotenv":"^16.3.1","express":"^4.18.2","jest":"^29.7.0"},"jest":{"testEnvironment":"node","testTimeout":10000,"testMatch":["**/test/integration/**/*.test.js"],"setupFilesAfterEnv":["<rootDir>/test/setup/jest.setup.js"],"verbose":true,"forceExit":true,"detectOpenHandles":true},"engines":{"node":">=18.0.0"},"_id":"@connexa/mcp-oauth-firebase-middleware@1.0.2","bugs":{"url":"https://github.com/connexa/mcp-oauth-firebase-middleware/issues"},"homepage":"https://github.com/connexa/mcp-oauth-firebase-middleware#readme","_nodeVersion":"22.18.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-otUyd9RYYewT9OnLEUvYVwtcTLk9Pyr5EKQ/0d4UNgvvYHAK+hHsLUrufDVGGC0igSCqU7QStsyP67OQiJNYZQ==","shasum":"bd2271f397b60a931e7dcf53fe0ba3257c9ac728","tarball":"https://registry.npmjs.org/@connexa/mcp-oauth-firebase-middleware/-/mcp-oauth-firebase-middleware-1.0.2.tgz","fileCount":11,"unpackedSize":53943,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCg0g3o0w9flVXzdiPYaBfCXtZT3noLP7ukVBfyYt1bgwIhAK1wkP9UumSn0/jd31fQwxHq9Bx/6Aw2/aZQcs6fs3BG"}]},"_npmUser":{"name":"cxa-cobi","email":"choward@connexa.com"},"directories":{},"maintainers":[{"name":"cxa-cobi","email":"choward@connexa.com"},{"name":"cxa-croper","email":"croper@connexa.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp-oauth-firebase-middleware_1.0.2_1767617969036_0.5917008178465135"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-30T16:12:39.541Z","modified":"2026-01-05T12:59:29.411Z","1.0.0":"2025-12-30T16:12:39.794Z","1.0.1":"2026-01-05T10:05:25.085Z","1.0.2":"2026-01-05T12:59:29.177Z"},"bugs":{"url":"https://github.com/connexa/mcp-oauth-firebase-middleware/issues"},"author":{"name":"Connexa"},"license":"MIT","homepage":"https://github.com/connexa/mcp-oauth-firebase-middleware#readme","keywords":["oauth","oauth2","firebase","mcp","model-context-protocol","authentication","middleware","pkce"],"repository":{"type":"git","url":"git+https://github.com/connexa/mcp-oauth-firebase-middleware.git"},"description":"OAuth 2.0 middleware for MCP servers using Firebase Auth backend","maintainers":[{"name":"cxa-cobi","email":"choward@connexa.com"},{"name":"cxa-croper","email":"croper@connexa.com"}],"readme":"# MCP OAuth Firebase Middleware\r\n\r\nOAuth 2.0 middleware for Model Context Protocol (MCP) servers, using Firebase Authentication as the backend. This middleware translates Claude's OAuth requests into Firebase Auth operations, enabling OAuth-compliant MCP servers.\r\n\r\n## Features\r\n\r\n✅ **OAuth 2.0 Compliant** - Implements authorization code flow with PKCE  \r\n✅ **Firebase Auth Backend** - Uses Firebase for user authentication and token management  \r\n✅ **Token Validation** - Middleware for protecting MCP endpoints  \r\n✅ **Role-Based Access Control** - Built-in support for roles and permissions via Firebase custom claims  \r\n✅ **Easy Integration** - Drop-in middleware for Express.js applications  \r\n✅ **Automatic Token Refresh** - Supports refresh token grant type  \r\n\r\n## Architecture\r\n\r\n```\r\n┌─────────────┐         ┌──────────────────┐         ┌──────────────┐\r\n│   Claude    │ OAuth   │  This Middleware │ Firebase│   Firebase   │\r\n│   Desktop   │────────▶│  (OAuth Layer)   │────────▶│     Auth     │\r\n└─────────────┘         └──────────────────┘         └──────────────┘\r\n```\r\n\r\nThe middleware implements three core OAuth 2.0 endpoints:\r\n- `GET /.well-known/oauth-authorization-server` - Server metadata\r\n- `GET /oauth/authorize` - Authorization endpoint\r\n- `POST /oauth/token` - Token exchange and refresh\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @connexa/mcp-oauth-firebase-middleware\r\n```\r\n\r\n## Prerequisites\r\n\r\n- Node.js 18+\r\n- Express.js application\r\n- Firebase project with Authentication enabled\r\n- Firebase service account credentials\r\n\r\n## Quick Start\r\n\r\n### 1. Set Up Firebase\r\n\r\n1. Create a Firebase project at [console.firebase.google.com](https://console.firebase.google.com)\r\n2. Enable **Email/Password** authentication\r\n3. Create a test user in Firebase Authentication\r\n4. Download service account key:\r\n   - Go to Project Settings → Service Accounts\r\n   - Click \"Generate New Private Key\"\r\n   - Save as `serviceAccountKey.json`\r\n\r\n### 2. Basic Integration\r\n\r\n```javascript\r\nconst express = require('express');\r\nconst { createOAuthMiddleware } = require('@connexa/mcp-oauth-firebase-middleware');\r\n\r\nconst app = express();\r\n\r\n// Create OAuth middleware\r\nconst oauth = createOAuthMiddleware({\r\n  firebase: {\r\n    apiKey: process.env.FIREBASE_API_KEY,\r\n    authDomain: process.env.FIREBASE_AUTH_DOMAIN,\r\n    projectId: process.env.FIREBASE_PROJECT_ID,\r\n    serviceAccountPath: './serviceAccountKey.json'\r\n  },\r\n  baseUrl: process.env.BASE_URL || 'https://your-app.com',\r\n  clientSecret: process.env.OAUTH_CLIENT_SECRET,\r\n  clientEmail: process.env.OAUTH_CLIENT_EMAIL\r\n});\r\n\r\n// Add OAuth endpoints\r\napp.get('/.well-known/oauth-authorization-server', oauth.metadata);\r\napp.get('/oauth/authorize', oauth.authorize);\r\napp.post('/oauth/token', oauth.token);\r\n\r\n// Protect your MCP endpoints\r\napp.post('/mcp', oauth.validateToken, (req, res) => {\r\n  // Your MCP handler - req.user contains authenticated user info\r\n  res.json({ \r\n    message: 'Protected endpoint',\r\n    user: req.user \r\n  });\r\n});\r\n\r\napp.listen(8080);\r\n```\r\n\r\n### 3. Configure Environment\r\n\r\nCreate a `.env` file:\r\n\r\n```env\r\nFIREBASE_API_KEY=your_api_key\r\nFIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com\r\nFIREBASE_PROJECT_ID=your-project-id\r\nBASE_URL=https://your-app.com\r\nOAUTH_CLIENT_SECRET=user_password\r\nOAUTH_CLIENT_EMAIL=user@example.com\r\n```\r\n\r\n\r\n## API Reference\r\n\r\n### createOAuthMiddleware(config)\r\n\r\nCreates OAuth middleware instance with handlers for all OAuth endpoints.\r\n\r\n**Parameters:**\r\n\r\n```javascript\r\n{\r\n  firebase: {\r\n    apiKey: string,              // Firebase API key\r\n    authDomain: string,          // Firebase auth domain\r\n    projectId: string,           // Firebase project ID\r\n    serviceAccountPath: string,  // Path to service account JSON (optional)\r\n    serviceAccountJson: string   // Service account JSON string (optional)\r\n  },\r\n  baseUrl: string,              // Base URL for OAuth endpoints (optional, auto-detected)\r\n  clientId: string,             // OAuth client ID for validation (optional)\r\n  clientSecret: string,         // User password for Firebase auth\r\n  clientEmail: string,          // User email for Firebase auth\r\n  redirectUri: string           // Allowed redirect URI (optional)\r\n}\r\n```\r\n\r\n**Returns:**\r\n\r\n```javascript\r\n{\r\n  metadata: Function,        // GET /.well-known/oauth-authorization-server\r\n  authorize: Function,       // GET /oauth/authorize\r\n  token: Function,          // POST /oauth/token\r\n  validateToken: Function   // Middleware for protecting endpoints\r\n}\r\n```\r\n\r\n### validateToken Middleware\r\n\r\nExpress middleware that validates Firebase ID tokens from the `Authorization` header.\r\n\r\n**Usage:**\r\n\r\n```javascript\r\n// Basic usage - any authenticated user\r\napp.post('/protected', oauth.validateToken(), (req, res) => {\r\n  // req.user contains:\r\n  // - uid: User's Firebase UID\r\n  // - email: User's email\r\n  // - emailVerified: Email verification status\r\n  // - role: User's role (from custom claims)\r\n  // - permissions: Array of user permissions (from custom claims)\r\n  // - customClaims: Full decoded token with all custom claims\r\n});\r\n\r\n// Require specific role\r\napp.get('/admin', oauth.validateToken({ role: 'admin' }), handler);\r\n\r\n// Require specific permission(s)\r\napp.post('/write', oauth.validateToken({ permissions: 'write' }), handler);\r\napp.delete('/item', oauth.validateToken({ permissions: ['write', 'delete'] }), handler);\r\n\r\n// Custom validation\r\napp.get('/premium', oauth.validateToken({\r\n  customCheck: (token) => token.subscriptionTier === 'premium'\r\n}), handler);\r\n```\r\n\r\n**Convenience Methods:**\r\n\r\n```javascript\r\n// Require specific role\r\napp.get('/admin', oauth.requireRole('admin'), handler);\r\n\r\n// Require admin role\r\napp.get('/admin', oauth.requireAdmin(), handler);\r\n\r\n// Require permission(s)\r\napp.post('/write', oauth.requirePermissions('write'), handler);\r\napp.delete('/item', oauth.requirePermissions(['write', 'delete']), handler);\r\n```\r\n\r\n**Error Response:**\r\n\r\nReturns `401 Unauthorized` with `WWW-Authenticate` header if token is invalid or missing.\r\nReturns `403 Forbidden` if user lacks required role/permissions.\r\n\r\n### Custom Claims Management\r\n\r\nHelper functions for managing user roles and permissions:\r\n\r\n```javascript\r\nconst { setCustomClaims, getCustomClaims, updateCustomClaims } = require('@connexa/mcp-oauth-firebase-middleware');\r\n\r\n// Set complete custom claims\r\nawait setCustomClaims('user-uid', {\r\n  role: 'admin',\r\n  permissions: ['read', 'write', 'delete']\r\n});\r\n\r\n// Get user's custom claims\r\nconst claims = await getCustomClaims('user-uid');\r\n// Returns: { role: 'admin', permissions: [...] }\r\n\r\n// Update specific claims (merges with existing)\r\nawait updateCustomClaims('user-uid', {\r\n  permissions: ['read', 'write', 'delete', 'manage']\r\n});\r\n\r\n// Remove all custom claims\r\nawait removeCustomClaims('user-uid');\r\n```\r\n\r\n## OAuth Endpoints\r\n\r\n#### Server Metadata\r\n```http\r\nGET /.well-known/oauth-authorization-server\r\n```\r\n\r\nReturns OAuth server configuration.\r\n\r\n#### Authorization\r\n```http\r\nGET /oauth/authorize?client_id=...&code_challenge=...&code_challenge_method=S256&redirect_uri=...&response_type=code&state=...\r\n```\r\n\r\nAuthenticates user and returns authorization code.\r\n\r\n#### Token Exchange\r\n```http\r\nPOST /oauth/token\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\ngrant_type=authorization_code&code=...&client_id=...&code_verifier=...&redirect_uri=...\r\n```\r\n\r\nExchanges authorization code for access token.\r\n\r\n#### Token Refresh\r\n```http\r\nPOST /oauth/token\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\ngrant_type=refresh_token&refresh_token=...&client_id=...\r\n```\r\n\r\nRefreshes expired access token.\r\n\r\n### Protected Endpoints\r\n\r\n#### MCP Handler (Example)\r\n```http\r\nPOST /mcp\r\nAuthorization: Bearer <access_token>\r\n\r\n{\r\n  \"jsonrpc\": \"2.0\",\r\n  \"method\": \"your_method\",\r\n  \"params\": {},\r\n  \"id\": 1\r\n}\r\n```\r\n\r\n#### Resources (Example)\r\n```http\r\nGET /resources\r\nAuthorization: Bearer <access_token>\r\n```\r\n\r\n### Utility Endpoints\r\n\r\n#### Health Check\r\n```http\r\nGET /health\r\n```\r\n\r\nReturns server status.\r\n\r\n## Example Server\r\n\r\nThe package includes an example server (`server.js`) for testing:\r\n\r\n```bash\r\nnpm start\r\n```\r\n\r\nThis runs a complete OAuth server on `http://localhost:8080` with example protected endpoints.\r\n\r\n## Advanced Usage\r\n\r\n### Custom Client Validation\r\n\r\nImplement database-backed client validation:\r\n\r\n```javascript\r\nconst oauth = createOAuthMiddleware({\r\n  firebase: { /* ... */ },\r\n  validateClient: async (clientId, redirectUri) => {\r\n    const client = await db.clients.findOne({ clientId });\r\n    return client && client.redirectUris.includes(redirectUri);\r\n  }\r\n});\r\n```\r\n\r\n### Using Service Account JSON from Environment\r\n\r\nFor cloud deployments, pass service account as JSON string:\r\n\r\n```javascript\r\nconst oauth = createOAuthMiddleware({\r\n  firebase: {\r\n    apiKey: process.env.FIREBASE_API_KEY,\r\n    authDomain: process.env.FIREBASE_AUTH_DOMAIN,\r\n    projectId: process.env.FIREBASE_PROJECT_ID,\r\n    serviceAccountJson: process.env.FIREBASE_SERVICE_ACCOUNT_JSON\r\n  }\r\n});\r\n```\r\n\r\n### Auto-Detecting Base URL\r\n\r\nWhen deployed (e.g., on Cloud Run), the middleware auto-detects the base URL from request headers:\r\n\r\n```javascript\r\nconst oauth = createOAuthMiddleware({\r\n  firebase: { /* ... */ },\r\n  // baseUrl omitted - will auto-detect from x-forwarded-proto and host headers\r\n});\r\n```\r\n\r\n## Role-Based Access Control (RBAC)\r\n\r\nThis middleware supports role-based and permission-based access control using Firebase custom claims.\r\n\r\n### Setting Up Roles and Permissions\r\n\r\nCustom claims are stored directly in the Firebase user token and are automatically included when the token is validated. They must be set using the Firebase Admin SDK:\r\n\r\n```javascript\r\nconst { setCustomClaims } = require('@connexa/mcp-oauth-firebase-middleware');\r\n\r\n// Set user role and permissions\r\nawait setCustomClaims('user-firebase-uid', {\r\n  role: 'admin',\r\n  permissions: ['read', 'write', 'delete'],\r\n  subscriptionTier: 'premium' // Any custom data\r\n});\r\n```\r\n\r\n**Important:** After setting custom claims, users must refresh their tokens (or wait up to 1 hour for automatic refresh) to see the changes. Users can force a refresh by:\r\n- Logging out and back in\r\n- Calling `user.getIdToken(true)` on the client\r\n\r\n### Using Roles in Your Application\r\n\r\n```javascript\r\nconst { createOAuthMiddleware, setCustomClaims } = require('@connexa/mcp-oauth-firebase-middleware');\r\n\r\n// Method 1: Using validateToken with options\r\napp.get('/admin/dashboard', oauth.validateToken({ role: 'admin' }), (req, res) => {\r\n  res.json({ message: 'Admin dashboard', user: req.user });\r\n});\r\n\r\n// Method 2: Using convenience helpers\r\napp.get('/admin/users', oauth.requireAdmin(), handler);\r\napp.get('/editor/content', oauth.requireRole('editor'), handler);\r\n\r\n// Method 3: Using permissions\r\napp.post('/api/write', oauth.requirePermissions('write'), handler);\r\napp.delete('/api/item', oauth.requirePermissions(['write', 'delete']), handler);\r\n\r\n// Method 4: Custom validation logic\r\napp.get('/premium/feature', oauth.validateToken({\r\n  customCheck: (token) => {\r\n    return token.subscriptionTier === 'premium' && token.emailVerified;\r\n  }\r\n}), handler);\r\n\r\n// Method 5: Manual check in handler\r\napp.get('/resource', oauth.validateToken(), (req, res) => {\r\n  if (req.user.role !== 'admin' && !req.user.permissions.includes('read')) {\r\n    return res.status(403).json({ error: 'Insufficient permissions' });\r\n  }\r\n  \r\n  res.json({ data: 'sensitive data' });\r\n});\r\n```\r\n\r\n### Role Management Endpoints\r\n\r\nCreate admin endpoints to manage user roles:\r\n\r\n```javascript\r\nconst { setCustomClaims, getCustomClaims } = require('@connexa/mcp-oauth-firebase-middleware');\r\n\r\n// Set user role (admin only)\r\napp.post('/admin/users/:uid/role', oauth.requireAdmin(), async (req, res) => {\r\n  const { uid } = req.params;\r\n  const { role, permissions } = req.body;\r\n  \r\n  await setCustomClaims(uid, { role, permissions });\r\n  res.json({ message: 'Role updated', uid, role, permissions });\r\n});\r\n\r\n// Get user's current role\r\napp.get('/admin/users/:uid/role', oauth.requireAdmin(), async (req, res) => {\r\n  const claims = await getCustomClaims(req.params.uid);\r\n  res.json(claims);\r\n});\r\n```\r\n\r\n### Common Role Patterns\r\n\r\n```javascript\r\n// Basic roles\r\nconst roles = {\r\n  USER: 'user',\r\n  EDITOR: 'editor',\r\n  ADMIN: 'admin',\r\n  SUPER_ADMIN: 'super_admin'\r\n};\r\n\r\n// Permission-based (more flexible)\r\nconst permissions = {\r\n  READ: 'read',\r\n  WRITE: 'write',\r\n  DELETE: 'delete',\r\n  MANAGE_USERS: 'manage_users',\r\n  MANAGE_SETTINGS: 'manage_settings'\r\n};\r\n\r\n// Set permissions by role\r\nawait setCustomClaims(userId, {\r\n  role: 'editor',\r\n  permissions: ['read', 'write']\r\n});\r\n\r\nawait setCustomClaims(adminId, {\r\n  role: 'admin',\r\n  permissions: ['read', 'write', 'delete', 'manage_users']\r\n});\r\n```\r\n\r\n### Best Practices\r\n\r\n1. **Use permissions over roles** when possible for more granular control\r\n2. **Keep custom claims under 1000 bytes** (Firebase limit)\r\n3. **Cache role checks** at the application level if needed for performance\r\n4. **Implement role hierarchy** in your application logic if needed\r\n5. **Always validate on the server** - never trust client-side role checks\r\n6. **Log role changes** for audit trails\r\n7. **Force token refresh** after role updates for immediate effect\r\n\r\n## Project Structure\r\n\r\n```\r\nmcp-oauth-firebase-middleware/\r\n├── lib/\r\n│   ├── index.js              # Main module export\r\n│   ├── metadata.js           # OAuth metadata endpoint\r\n│   ├── authorize.js          # Authorization endpoint\r\n│   ├── token.js              # Token endpoint\r\n│   ├── firebase-client.js    # Firebase Auth operations\r\n│   ├── pkce.js              # PKCE utilities\r\n│   └── storage.js           # Authorization code storage\r\n├── middleware/\r\n│   └── validate-token.js    # Token validation middleware\r\n├── server.js                # Example Express server\r\n├── package.json\r\n├── .env.example\r\n└── README.md\r\n```\r\n\r\n## Security Considerations\r\n\r\n🔒 **PKCE Required** - All authorization requests must use PKCE with S256  \r\n🔒 **Single-Use Codes** - Authorization codes are invalidated after use  \r\n🔒 **Code Expiration** - Authorization codes expire after 10 minutes  \r\n🔒 **HTTPS Only** - All production endpoints must use HTTPS  \r\n🔒 **Token Verification** - All protected endpoints validate Firebase ID tokens  \r\n\r\n## Publishing to npm\r\n\r\n1. Update `package.json` with your details:\r\n   - Set `author` field\r\n   - Update `repository` URL\r\n   - Choose appropriate `name` (check availability on npm)\r\n\r\n2. Login to npm:\r\n   ```bash\r\n   npm login\r\n   ```\r\n\r\n3. Publish:\r\n   ```bash\r\n   npm publish\r\n   ```\r\n\r\nThe `files` field in `package.json` ensures only necessary files are included in the package.\r\n\r\n## Development\r\n\r\n### Running the Example Server\r\n\r\n```bash\r\n# Install dependencies\r\nnpm install\r\n\r\n# Copy environment template\r\ncp .env.example .env\r\n\r\n# Edit .env with your Firebase credentials\r\n\r\n# Run server\r\nnpm start\r\n```\r\n\r\n### Testing with MCP Inspector\r\n\r\n1. Install MCP Inspector:\r\n```bash\r\nnpm install -g @modelcontextprotocol/inspector\r\n```\r\n\r\n2. Test OAuth flow:\r\n```bash\r\nmcp-inspector http://localhost:8080\r\n```\r\n\r\n3. Verify:\r\n   - ✅ Metadata endpoint returns correct URLs\r\n   - ✅ Authorization redirects with code\r\n   - ✅ Token exchange returns valid tokens\r\n   - ✅ Protected endpoints accept access token\r\n   - ✅ Token refresh works\r\n\r\n## Project Structure\r\n\r\n```\r\nmcp-oauth-firebase-middleware/\r\n├── lib/\r\n│   ├── index.js              # Main module export\r\n│   ├── metadata.js           # OAuth metadata endpoint\r\n│   ├── authorize.js          # Authorization endpoint\r\n│   ├── token.js              # Token endpoint\r\n│   ├── firebase-client.js    # Firebase Auth operations\r\n│   ├── pkce.js              # PKCE utilities\r\n│   └── storage.js           # Authorization code storage\r\n├── middleware/\r\n│   └── validate-token.js    # Token validation middleware\r\n├── server.js                # Example Express server\r\n├── package.json\r\n├── .env.example\r\n└── README.md\r\n```\r\n\r\n## Security Considerations\r\n\r\n🔒 **PKCE Required** - All authorization requests must use PKCE with S256  \r\n🔒 **Single-Use Codes** - Authorization codes are invalidated after use  \r\n🔒 **Code Expiration** - Authorization codes expire after 10 minutes  \r\n🔒 **HTTPS Only** - All production endpoints must use HTTPS  \r\n🔒 **Token Verification** - All protected endpoints validate Firebase ID tokens  \r\n\r\n## Publishing to npm\r\n\r\n1. Update `package.json` with your details:\r\n   - Set `author` field\r\n   - Update `repository` URL\r\n   - Choose appropriate `name` (check availability on npm)\r\n\r\n2. Login to npm:\r\n   ```bash\r\n   npm login\r\n   ```\r\n\r\n3. Publish:\r\n   ```bash\r\n   npm publish\r\n   ```\r\n\r\nThe `files` field in `package.json` ensures only necessary files are included in the package.\r\n\r\n## Development\r\n\r\n### Running the Example Server\r\n\r\n```bash\r\n# Install dependencies\r\nnpm install\r\n\r\n# Copy environment template\r\ncp .env.example .env\r\n\r\n# Edit .env with your Firebase credentials\r\n\r\n# Run server\r\nnpm start\r\n```\r\n\r\n### Testing with MCP Inspector\r\n\r\n## Troubleshooting\r\n\r\n### Firebase Authentication Error\r\n- Verify Firebase credentials in `.env`\r\n- Ensure user exists in Firebase Authentication\r\n- Check that Email/Password provider is enabled\r\n\r\n### PKCE Validation Failed\r\n- Ensure client is using S256 method\r\n- Verify code_verifier matches code_challenge\r\n\r\n### Token Verification Failed\r\n- Check Firebase Admin SDK is initialized correctly\r\n- Verify service account has proper permissions\r\n- Ensure token hasn't expired (3600s lifetime)\r\n\r\n## License\r\n\r\nMIT\r\n\r\n## Support\r\n\r\nFor issues and questions:\r\n- Check [MCP Documentation](https://modelcontextprotocol.io)\r\n- Review Firebase Auth [documentation](https://firebase.google.com/docs/auth)\r\n- Open an issue in this repository\r\n","readmeFilename":"README.md"}