Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,53 @@ HOST=0.0.0.0
# BASE_URL=https://your-domain.com:8443
# PUBLIC_URL=https://n8n-mcp.mydomain.com (alternative to BASE_URL)

# Authentication token for HTTP mode (REQUIRED)
# Authentication mode for HTTP mode
# - token (default): shared secret in AUTH_TOKEN / AUTH_TOKEN_FILE
# - oauth: OAuth 2.1 resource server; see docs/OAUTH_AUTHENTICATION.md
# Any other value refuses to start.
# AUTH_MODE=token

# Authentication token for HTTP mode (REQUIRED when AUTH_MODE=token)
# Generate with: openssl rand -base64 32
AUTH_TOKEN=your-secure-token-here

# OAuth 2.1 settings (only read when AUTH_MODE=oauth)
#
# Developers: do not put AUTH_MODE=oauth in the repository's own .env. The test
# setup loads that file into every vitest run (tests/setup/test-env.ts), which
# would put the whole suite in oauth mode and fail the token-mode
# backward-compatibility tests. Keep a local oauth profile in a separate,
# gitignored file such as .env.oauth.local and pass it to the server when you
# start it. See docs/OAUTH_AUTHENTICATION.md, "Running it locally".
#
# Authorization server issuer URL (required)
# AUTHPLANE_ISSUER=https://auth.example.com
# Canonical resource URI = token audience. Defaults to <BASE_URL or PUBLIC_URL>/mcp
# AUTHPLANE_RESOURCE=https://n8n-mcp.example.com/mcp
# Scopes advertised to clients. Default: all five tool-group scopes
# AUTHPLANE_SCOPES=n8n:docs,n8n:workflows:read,n8n:workflows:write,n8n:executions,n8n:credentials
# JWT signing algorithms accepted (default ES256,RS256; "none" and HS* are refused)
# AUTHPLANE_ALLOWED_ALGORITHMS=ES256,RS256
# AUTHPLANE_CLOCK_SKEW_SECONDS=30
# Realm in WWW-Authenticate challenges
# AUTHPLANE_REALM=n8n-mcp
# Allow http:// issuer/resource for local development only
# AUTHPLANE_DEV_MODE=false
# Client credentials of this server at the authorization server (required for revocation checks)
# AUTHPLANE_CLIENT_ID=
# AUTHPLANE_CLIENT_SECRET=
# Per-tool scope checks (true) or authentication only (false)
# MCP_AUTH_ENFORCE_SCOPES=true
# DPoP sender-constrained tokens: off | optional | required
# MCP_AUTH_DPOP=off
# Introspect every token at the authorization server; an unreachable server rejects the request
# MCP_AUTH_REVOCATION_CHECK=false
# Refuse a session to any subject other than the one that opened it
# MCP_AUTH_BIND_SESSION=true
# Audit trail (structured log records; optional line-delimited JSON file)
# MCP_AUTH_AUDIT=true
# MCP_AUTH_AUDIT_FILE=/var/log/n8n-mcp/auth-audit.log

# CORS origin for HTTP mode (optional)
# Default: * (allow all origins)
# For production, set to your specific domain
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **`AUTH_MODE=oauth`: HTTP mode as an OAuth 2.1 resource server.** The shared `AUTH_TOKEN` can be replaced by access tokens issued by an AuthPlane authorization server and verified locally with `@authplane/sdk` (signature against the issuer's JWKS, issuer, audience pinned to the configured resource URI, expiry, algorithm allowlist). The server publishes RFC 9728 Protected Resource Metadata at `/.well-known/oauth-protected-resource/mcp` (and the root well-known path) and extends the existing `WWW-Authenticate` challenge with `resource_metadata`, so MCP clients that implement the authorization specification — Claude Code, Claude Desktop, Claude.ai remote servers, MCP Inspector — discover the authorization server from a bare 401 and run the consent flow without a token in their configuration. The 401 body, the rate limiter, CORS handling, the multi-tenant headers and the SSRF gate are unchanged; the access token is never used as an n8n API key. Per-tool scopes (`n8n:docs`, `n8n:workflows:read`, `n8n:workflows:write`, `n8n:executions`, `n8n:credentials`, with read/write split on the multiplexed tools) are enforced in the tool handler after `DISABLED_TOOLS`, and a denial is a tool error rather than an HTTP status so the client's request completes. Optional DPoP sender-constrained tokens (`MCP_AUTH_DPOP=optional|required`, proof URL anchored to the configured resource origin rather than `Host`), introspection-based revocation that fails closed (`MCP_AUTH_REVOCATION_CHECK`), binding of each MCP session to the subject that opened it (`MCP_AUTH_BIND_SESSION`), and an audit trail of authentications, denials and tool authorizations keyed by subject and token id (`MCP_AUTH_AUDIT`, `MCP_AUTH_AUDIT_FILE`). `AUTH_MODE=token` remains the default and is behaviourally unchanged; any other value refuses to start. All AuthPlane-specific code is in `src/auth/authplane.ts` behind a `TokenVerifier` interface and is loaded only in `oauth` mode. Documented in `docs/OAUTH_AUTHENTICATION.md`; threat model updated.

## [2.73.0] - 2026-08-19

### Changed
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ RUN --mount=type=cache,target=/root/.npm \
npm install --no-save typescript@^5.8.3 @types/node@^22.15.30 @types/express@^5.0.3 \
@modelcontextprotocol/sdk@1.30.0 dotenv@^16.5.0 express@^5.1.0 axios@^1.18.1 \
n8n-workflow@2.35.2 uuid@^11.1.1 @types/uuid@^10.0.0 \
openai@^4.77.0 zod@3.25.76 lru-cache@^11.2.1 @supabase/supabase-js@^2.57.4
openai@^4.77.0 zod@3.25.76 lru-cache@^11.2.1 @supabase/supabase-js@^2.57.4 \
@authplane/mcp@0.3.0 @authplane/sdk@0.3.0

# Copy source and build
COPY src ./src
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ If your n8n instance sits behind Cloudflare Access (Zero Trust), provide your se

When set, these are sent as `CF-Access-Client-Id` / `CF-Access-Client-Secret` headers on n8n API requests, version/health probes, and webhook executions. The token is confined to the `N8N_API_URL` origin — webhook calls to a different host (e.g. a split `WEBHOOK_URL` origin) do not receive it, to avoid leaking the token.

### OAuth 2.1 for HTTP deployments

HTTP mode can authenticate clients with OAuth 2.1 access tokens from an [AuthPlane](https://authplane.com) authorization server instead of a shared `AUTH_TOKEN` (`AUTH_MODE=oauth`). Clients such as Claude Code, Claude Desktop and MCP Inspector discover the authorization server and run the consent flow themselves; each request is attributed to a subject, tools are gated by scope, and an audit trail records who called what. See [OAUTH_AUTHENTICATION.md](./docs/OAUTH_AUTHENTICATION.md).

## Connect your IDE

n8n-MCP works with multiple AI-powered IDEs and tools:
Expand Down
16 changes: 15 additions & 1 deletion docs/HTTP_DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ Skip HTTP entirely and use stdio mode directly:
| Variable | Description | Example |
|----------|-------------|------|
| `MCP_MODE` | Must be set to `http` | `http` |
| `AUTH_TOKEN` or `AUTH_TOKEN_FILE` | Authentication method | See security section |
| `AUTH_TOKEN` or `AUTH_TOKEN_FILE` | Authentication method (`AUTH_MODE=token`, the default) | See security section |
| `AUTH_MODE` | `token` (default) or `oauth` — OAuth 2.1 resource server, see [OAUTH_AUTHENTICATION.md](./OAUTH_AUTHENTICATION.md) | `oauth` |

### Optional Settings

Expand Down Expand Up @@ -301,6 +302,19 @@ curl -H "Authorization: Bearer $AUTH_TOKEN" \
https://your-server.com/health
```

### OAuth 2.1 (AuthPlane)

Instead of a shared `AUTH_TOKEN`, the server can verify OAuth 2.1 access tokens issued by an [AuthPlane](https://authplane.com) authorization server. Clients discover the authorization server from the `WWW-Authenticate` challenge and the `/.well-known/oauth-protected-resource` document and run the consent flow themselves, so no token is copied into client configuration. Each request is attributed to a subject, tools are gated by scope, and an audit trail records who called what.

```bash
MCP_MODE=http AUTH_MODE=oauth \
AUTHPLANE_ISSUER=https://auth.example.com \
BASE_URL=https://your-server.com \
npm run start:http
```

Register `https://your-server.com/mcp` as a resource at the authorization server first. Clients that implement MCP authorization — Claude Code (`claude mcp add --transport http n8n https://your-server.com/mcp`), Claude Desktop and Claude.ai remote servers, MCP Inspector — then connect without `mcp-remote --header`. The full reference, including scopes, DPoP, revocation and session binding, is in [OAUTH_AUTHENTICATION.md](./OAUTH_AUTHENTICATION.md).

### SSL/HTTPS (Strongly Recommended)

Use a reverse proxy for SSL termination:
Expand Down
Loading