Skip to main content
Glama
server.ts6.06 kB
/** * Example 09: Auth0 Authentication * * This example shows how to integrate Auth0 authentication with Express-MCP, * including JWT validation, user info retrieval, and permission checks. * * Note: This is a simulation of Auth0 integration. In production, use the * actual Auth0 SDK and configure with your Auth0 tenant. */ import crypto from "node:crypto"; import express, { type Request, type Response, type NextFunction, } from "express"; import { ExpressMCP } from "../../src"; // JWT token interface interface DecodedToken { sub: string; email: string; name: string; scope: string; permissions: string[]; iat: number; exp: number; } // Extend Express Request to include user interface AuthenticatedRequest extends Request { user?: DecodedToken; } // JWT verification middleware const checkJwt = (req: Request, res: Response, next: NextFunction) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { return res.status(401).json({ error: "Unauthorized", message: "Bearer token required", }); } const token = authHeader.substring(7); try { // Simple JWT verification (in real app, use proper JWT library) // This is a mock implementation for demo purposes const decoded = JSON.parse( Buffer.from(token.split(".")[1], "base64").toString(), ) as DecodedToken; // Check if token is expired if (decoded.exp * 1000 < Date.now()) { return res.status(401).json({ error: "Unauthorized", message: "Token expired", }); } // Attach user info to request (req as AuthenticatedRequest).user = decoded; next(); } catch (error) { return res.status(401).json({ error: "Unauthorized", message: "Invalid token", }); } }; // Permission-based authorization middleware const checkPermission = (permission: string) => { return (req: Request, res: Response, next: NextFunction) => { const user = (req as AuthenticatedRequest).user; if (!user) { return res.status(401).json({ error: "Unauthorized", message: "Authentication required", }); } if (!user.permissions.includes(permission)) { return res.status(403).json({ error: "Forbidden", message: `Permission '${permission}' required`, }); } next(); }; }; // Scope-based authorization middleware const checkScope = (requiredScope: string) => { return (req: Request, res: Response, next: NextFunction) => { const user = (req as AuthenticatedRequest).user; if (!user || !user.scope) { return res.status(401).json({ error: "Unauthorized", message: "Authentication required", }); } const userScopes = user.scope.split(" "); if (!userScopes.includes(requiredScope)) { return res.status(403).json({ error: "Forbidden", message: `Scope '${requiredScope}' required`, }); } next(); }; }; const app = express(); app.use(express.json()); // Public endpoints (no auth required) app.get("/", (_req: Request, res: Response) => { res.json({ message: "Auth0-protected API", version: "1.0.0", endpoints: { public: ["GET /", "GET /health"], protected: [ "GET /api/profile", "PUT /api/profile", "GET /api/email", "GET /api/admin/users", ], }, }); }); app.get("/health", (_req: Request, res: Response) => { res.json({ status: "healthy", timestamp: new Date().toISOString() }); }); // Protected endpoints - require valid JWT app.get("/api/profile", checkJwt, (req: Request, res: Response) => { const user = (req as AuthenticatedRequest).user as DecodedToken; res.json({ profile: { id: user.sub, email: user.email, name: user.name, permissions: user.permissions, scopes: user.scope.split(" "), }, }); }); app.put( "/api/profile", checkJwt, checkPermission("write:profile"), (req: Request, res: Response) => { const user = (req as AuthenticatedRequest).user as DecodedToken; const { name, email } = req.body; // In a real app, you would update the user profile in the database res.json({ message: "Profile updated successfully", profile: { id: user.sub, email: email || user.email, name: name || user.name, updatedAt: new Date().toISOString(), }, }); }, ); app.get( "/api/email", checkJwt, checkScope("email"), (req: Request, res: Response) => { const user = (req as AuthenticatedRequest).user as DecodedToken; res.json({ email: user.email, verified: true, // Mock verification status }); }, ); app.get( "/api/admin/users", checkJwt, checkPermission("read:users"), (_req: Request, res: Response) => { // Mock user list (in real app, fetch from database) res.json({ users: [ { id: "user1", email: "user1@example.com", name: "User One", permissions: ["read:profile", "write:profile"], }, { id: "user2", email: "user2@example.com", name: "User Two", permissions: ["read:profile"], }, ], }); }, ); // Initialize ExpressMCP const mcp = new ExpressMCP(app, { mountPath: "/mcp", // Exclude admin routes from MCP tools for security exclude: (route) => { return route.path.startsWith("/api/admin/"); }, }); await mcp.init(); mcp.mount("/mcp"); const PORT = 3009; app.listen(PORT, () => { console.log(`🔐 Auth0-protected API running on http://localhost:${PORT}`); console.log(`📡 MCP tools available at http://localhost:${PORT}/mcp/tools`); console.log(`🚀 MCP invoke endpoint at http://localhost:${PORT}/mcp/invoke`); // Demo: Show available tools setTimeout(() => { const tools = mcp.listTools(); console.log(`\n🔧 Available MCP tools: ${tools.length}`); for (const tool of tools) { const toolObj = tool as { title: string; description: string }; console.log(` - ${toolObj.title}: ${toolObj.description}`); } }); }); // Helper function to find a specific tool function findTool(toolName: string) { const tools = mcp.listTools(); return tools.find((t) => { const toolObj = t as { name: string; title: string }; return toolObj.name === toolName || toolObj.title === toolName; }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bowen31337/expressjs_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server