Skip to content

Feat/oauth resource server - #1

Open
martomorri wants to merge 5 commits into
mainfrom
feat/oauth-resource-server
Open

martomorri wants to merge 5 commits into
mainfrom
feat/oauth-resource-server

Conversation

@martomorri

Copy link
Copy Markdown
Collaborator

No description provided.

Exact pins, in package.json, package.runtime.json and the Dockerfile builder
stage. The builder does not install package.json — it installs a hand-curated
list — so a new runtime dependency has to be declared in all three places or
the image fails to compile while the local build passes.

tests/unit/sdk-pin-consistency.test.ts is extended to fail when those three
declarations disagree, or when the two packages drift onto different versions.

tsconfig.json gains a paths entry for @authplane/sdk/core: the package exposes
that subpath through exports only, which moduleResolution: node cannot
resolve.

Conceived by Romuald Członkowski - www.aiadvisors.pl/en
Adds the two @AuthPlane packages. The rest of the diff is not intentional:
v2.73.0 committed package.json without regenerating the lock, so `npm ci` fails
with "not in sync" on a clean clone and any `npm install` rewrites it, also
bumping @aws-sdk/* patch versions within their existing ranges.

Kept as its own commit so it can be dropped or regenerated independently.

Conceived by Romuald Członkowski - www.aiadvisors.pl/en
HTTP mode can accept access tokens issued by an external authorization server
instead of the shared AUTH_TOKEN. Tokens are verified locally — signature
against the issuer's JWKS, issuer, audience pinned to the configured resource
URI, expiry, algorithm allowlist — and the server publishes RFC 9728 Protected
Resource Metadata, extending the existing WWW-Authenticate challenge (czlonkowski#767)
with resource_metadata. A client that has never been configured with a token
discovers the authorization server from a bare 401 and runs the consent flow
itself.

AUTH_MODE=token stays the default and is unchanged; any other value refuses to
start rather than guessing. All AuthPlane-specific code is in
src/auth/authplane.ts behind the TokenVerifier interface, loaded only in oauth
mode, so token mode never imports the SDK.

Per-tool scopes (n8n:docs, n8n:workflows:read, n8n:workflows:write,
n8n:executions, n8n:credentials, with a read/write split on the multiplexed
tools) are checked in the tool handler after DISABLED_TOOLS. A denial is a tool
error carrying required_scope and granted_scopes, not an HTTP status: refusing
at the HTTP layer leaves the client's request hanging.

Also optional DPoP sender-constrained tokens with the proof URL anchored to the
configured resource origin rather than the Host header, introspection-based
revocation that fails closed, binding of each MCP session to the subject that
opened it — enforced on every route that reaches an existing session, including
the server-to-client stream and session termination — and an audit trail keyed
by subject and token id that never records the token itself.

The 401 body, the rate limiter, the multi-tenant headers and the SSRF gate are
unchanged. The access token is never used as the n8n API key.

Conceived by Romuald Członkowski - www.aiadvisors.pl/en
Hermetic suites (npm run test:auth, 125 tests) run against a fake TokenVerifier
and prove the server's own logic: that token mode is untouched, config
validation and the named errors it raises, the tool-to-scope map, session
binding and its eviction behaviour, audit redaction, challenge construction,
the gate on all five MCP routes, and that a scope denial is a tool error rather
than an HTTP 403.

A live suite drives the adapter against a real authorization server, minting
genuine tokens and signing genuine DPoP proofs, so it catches a claim read by
the wrong name or an SDK default assumed wrongly. An end-to-end suite spawns
the built server with a pinned environment and drives it with the official MCP
client over Streamable HTTP.

Both skip themselves unless AUTHPLANE_TEST_ISSUER, AUTHPLANE_TEST_ADMIN_URL and
AUTHPLANE_TEST_ADMIN_KEY are set, so CI and a fresh clone stay green without a
server.

Conceived by Romuald Członkowski - www.aiadvisors.pl/en
docs/OAUTH_AUTHENTICATION.md covers configuration, the request path, scopes,
DPoP, revocation, session binding, the audit trail, client setup, the known
limits and how to plug in a different authorization server.

THREAT_MODEL.md is updated because §8 lists a new deployment mode as a review
trigger: the new trust boundary, the mitigations for session hijack and token
replay, and what the audit trail attributes. Rows added to HTTP_DEPLOYMENT.md
and SECURITY_HARDENING.md; a pointer in the README; a CHANGELOG entry.

scripts/test-oauth-endpoint.{sh,js} check a deployed instance from outside:
the unauthenticated 401 and its challenge, the metadata document at both paths,
and a valid token opening a session.

Conceived by Romuald Członkowski - www.aiadvisors.pl/en
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant