auth
For buyers
Who it's for: (Akari to write — e.g. "Claude Code devs exposing an MCP server beyond local stdio and needing API-key auth that doesn't leak via error messages or timing.")
What it prevents / shortens: (Akari to write — e.g. "Prevents timing-attack-leakable key comparison, brute-force via missing rate limit, and internal error bleed-through.")
What's in the zip: (Akari to write — list: src/auth/, src/middleware/, tests/, README, LICENSE, package.json, .env.example.)
Known constraints: (Akari to write — e.g. "API-key auth only in v1; no OAuth/OIDC; in-memory rate limiter (single-instance).")
Shortest path to running:
unzip auth-template.zip && cd auth-template
cp .env.example .env # set NEXUS_API_KEY
npm install
npm test
npm run devNext action: Buy on Gumroad · Template source preview
MCP Server with Authentication
A production-ready Model Context Protocol server with HTTP transport, dual authentication (API key + JWT), and rate limiting.
Who this is for
- You're building: a remote / HTTP MCP server that needs to keep unauthenticated callers out — internal tools exposed over the network, multi-tenant MCP endpoints, or anything shared across a small team.
- What it saves you: the day you'd spend designing dual auth (API key + JWT), a rate limiter with correct headers, and the
formatAuthError()layer that prevents internal messages from leaking to the client. Timing-safe key comparison and role-gated admin tools are already wired in. - What's in the zip: full scaffolded project —
src/(index, auth, rate-limiter, tools),.env.examplewith every required secret, Vitest suite covering API key + JWT + rate-limit + role gating, TS config,.gitignore, and this README. - Not a fit if: you need OAuth 2.0 / OIDC / SSO integration, session cookies, or persistent rate-limit storage (Redis). The limiter is in-memory and resets on restart — swap it out before multi-instance deploys.
- Run it in 4 steps:
npm install→cp .env.example .env(setJWT_SECRETandAPI_KEYS) →npm run build→npm start. Server listens onhttp://localhost:3000by default. - Next: get it on Gumroad · scaffold via
npx @nexus-lab/create-mcp-server my-server --template auth· source on GitHub.
Quick Start
# Install dependencies
npm install
# Copy and configure environment
cp .env.example .env
# Edit .env with your settings (especially JWT_SECRET!)
# Build and start
npm run build
npm startThe server starts on http://localhost:3000 by default.
Authentication
This server supports two authentication methods. Every request to /mcp must include one.
API Key
Pass a valid key in the x-api-key header:
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "x-api-key: your-api-key" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Configure valid keys in .env:
API_KEYS=key1,key2,key3JWT Bearer Token
Pass a signed JWT in the Authorization header:
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbG..." \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Token claims:
| Claim | Description |
|---|---|
sub | User identifier |
role | "admin" or "user" |
exp | Expiry timestamp |
Use the generate-token tool (admin only) or generate tokens programmatically:
import { generateToken } from "./auth.js";
const token = generateToken("user-id", "admin", "24h");Rate Limiting (two layers)
Rate limiting is applied in two layers so unauthenticated floods cannot brute-force the auth layer at full throttle:
- Pre-auth, keyed by client IP, enforced before
authMiddleware. Invalid API keys, bad JWTs, and missing credentials all consume this bucket. Default: 30 requests / minute. - Post-auth, keyed by authenticated user / API-key prefix, enforced after
authMiddleware. Default: 100 requests / minute per user.
Configure via environment:
| Variable | Default | Description |
|---|---|---|
PREAUTH_RATE_LIMIT_MAX | 30 | Max unauthenticated attempts per IP per window |
PREAUTH_RATE_LIMIT_WINDOW_MS | 60000 | Pre-auth window in ms |
RATE_LIMIT_MAX | 100 | Max authenticated requests per user per window |
RATE_LIMIT_WINDOW_MS | 60000 | Post-auth window in ms |
Response headers on every request:
X-RateLimit-Limit— Maximum requests allowedX-RateLimit-Remaining— Requests remaining in windowX-RateLimit-Reset— Seconds until window resets
When exceeded, returns 429 Too Many Requests with a Retry-After header.
CORS (opt-in, allowlist-only)
The server authenticates via Authorization / x-api-key headers. A permissive Access-Control-Allow-Origin: * would let any origin's browser replay those credentials on behalf of a logged-in user — so the template does not enable CORS by default.
To enable CORS, set CORS_ORIGINS to a comma-separated allowlist:
CORS_ORIGINS=https://app.example.com,https://admin.example.comOnly origins in the allowlist receive CORS headers; non-matching origins are silently denied. Credentials mode is enabled for allowed origins so cookies / Authorization headers can be forwarded.
If the server is only ever called by non-browser MCP clients, leave CORS_ORIGINS empty — CORS will be disabled entirely.
Available Tools
whoami
Returns the authenticated user's identity and auth method. No parameters required.
generate-token
Generates a JWT for a specified user. Admin only.
| Parameter | Type | Default | Description |
|---|---|---|---|
userId | string | (required) | User ID for the token |
role | "admin" | "user" | "user" | Role claim |
expiresIn | string | "24h" | Expiry (e.g., "1h", "7d") |
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET | /health | No | Health check |
POST | /mcp | Yes | MCP protocol endpoint |
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
PORT | No | 3000 | Server port |
API_KEYS | Yes* | — | Comma-separated valid API keys |
JWT_SECRET | Yes* | — | Secret for signing/verifying JWTs |
PREAUTH_RATE_LIMIT_MAX | No | 30 | Pre-auth (IP) max requests |
PREAUTH_RATE_LIMIT_WINDOW_MS | No | 60000 | Pre-auth window (ms) |
RATE_LIMIT_MAX | No | 100 | Post-auth (user) max requests |
RATE_LIMIT_WINDOW_MS | No | 60000 | Post-auth window (ms) |
CORS_ORIGINS | No | — | Comma-separated allowlist; empty = CORS disabled |
BODY_LIMIT | No | 1mb | Max accepted JSON body size (e.g. 1mb, 512kb) |
*At least one auth method must be configured.
Development
# Watch mode (rebuild + restart on changes)
npm run dev
# Run tests
npm test
# Run tests in watch mode
npm run test:watchDeployment
- Set a strong, unique
JWT_SECRET(at least 32 characters) - Generate secure API keys (e.g.,
openssl rand -hex 32) - Consider placing behind a reverse proxy (nginx) for TLS termination
- For production, replace the in-memory rate limiter with Redis-backed storage
- Set
NODE_ENV=production
License
MIT