Skip to content

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:

bash
unzip auth-template.zip && cd auth-template
cp .env.example .env  # set NEXUS_API_KEY
npm install
npm test
npm run dev

Next 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.example with 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 installcp .env.example .env (set JWT_SECRET and API_KEYS) → npm run buildnpm start. Server listens on http://localhost:3000 by default.
  • Next: get it on Gumroad · scaffold via npx @nexus-lab/create-mcp-server my-server --template auth · source on GitHub.

Quick Start

bash
# 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 start

The 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:

bash
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,key3

JWT Bearer Token

Pass a signed JWT in the Authorization header:

bash
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:

ClaimDescription
subUser identifier
role"admin" or "user"
expExpiry timestamp

Use the generate-token tool (admin only) or generate tokens programmatically:

typescript
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:

  1. 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.
  2. Post-auth, keyed by authenticated user / API-key prefix, enforced after authMiddleware. Default: 100 requests / minute per user.

Configure via environment:

VariableDefaultDescription
PREAUTH_RATE_LIMIT_MAX30Max unauthenticated attempts per IP per window
PREAUTH_RATE_LIMIT_WINDOW_MS60000Pre-auth window in ms
RATE_LIMIT_MAX100Max authenticated requests per user per window
RATE_LIMIT_WINDOW_MS60000Post-auth window in ms

Response headers on every request:

  • X-RateLimit-Limit — Maximum requests allowed
  • X-RateLimit-Remaining — Requests remaining in window
  • X-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.com

Only 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.

ParameterTypeDefaultDescription
userIdstring(required)User ID for the token
role"admin" | "user""user"Role claim
expiresInstring"24h"Expiry (e.g., "1h", "7d")

Endpoints

MethodPathAuthDescription
GET/healthNoHealth check
POST/mcpYesMCP protocol endpoint

Environment Variables

VariableRequiredDefaultDescription
PORTNo3000Server port
API_KEYSYes*Comma-separated valid API keys
JWT_SECRETYes*Secret for signing/verifying JWTs
PREAUTH_RATE_LIMIT_MAXNo30Pre-auth (IP) max requests
PREAUTH_RATE_LIMIT_WINDOW_MSNo60000Pre-auth window (ms)
RATE_LIMIT_MAXNo100Post-auth (user) max requests
RATE_LIMIT_WINDOW_MSNo60000Post-auth window (ms)
CORS_ORIGINSNoComma-separated allowlist; empty = CORS disabled
BODY_LIMITNo1mbMax accepted JSON body size (e.g. 1mb, 512kb)

*At least one auth method must be configured.

Development

bash
# Watch mode (rebuild + restart on changes)
npm run dev

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

Deployment

  1. Set a strong, unique JWT_SECRET (at least 32 characters)
  2. Generate secure API keys (e.g., openssl rand -hex 32)
  3. Consider placing behind a reverse proxy (nginx) for TLS termination
  4. For production, replace the in-memory rate limiter with Redis-backed storage
  5. Set NODE_ENV=production

License

MIT

Built by Nexus Lab (nokaze). MIT Licensed.