mcp-trellis 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -52
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +1 -0
- package/dist/app.js.map +1 -1
- package/dist/auth/signed/payload.d.ts +31 -0
- package/dist/auth/signed/payload.d.ts.map +1 -0
- package/dist/auth/signed/payload.js +85 -0
- package/dist/auth/signed/payload.js.map +1 -0
- package/dist/auth/signed/refresh.d.ts +34 -0
- package/dist/auth/signed/refresh.d.ts.map +1 -0
- package/dist/auth/signed/refresh.js +95 -0
- package/dist/auth/signed/refresh.js.map +1 -0
- package/dist/auth/signed/token.d.ts +41 -0
- package/dist/auth/signed/token.d.ts.map +1 -0
- package/dist/auth/signed/token.js +103 -0
- package/dist/auth/signed/token.js.map +1 -0
- package/dist/deprecated.d.ts +15 -15
- package/dist/deprecated.d.ts.map +1 -1
- package/dist/deprecated.js +15 -15
- package/dist/deprecated.js.map +1 -1
- package/dist/http/body.d.ts +4 -0
- package/dist/http/body.d.ts.map +1 -1
- package/dist/http/body.js +9 -2
- package/dist/http/body.js.map +1 -1
- package/dist/mcp/methods.d.ts +7 -0
- package/dist/mcp/methods.d.ts.map +1 -1
- package/dist/mcp/methods.js +13 -1
- package/dist/mcp/methods.js.map +1 -1
- package/dist/mcp/rpc-handlers.d.ts.map +1 -1
- package/dist/mcp/rpc-handlers.js +2 -2
- package/dist/mcp/rpc-handlers.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +4 -6
- package/dist/mcp/tools.js.map +1 -1
- package/dist/oauth/cimd/cimd-ssrf.d.ts +8 -4
- package/dist/oauth/cimd/cimd-ssrf.d.ts.map +1 -1
- package/dist/oauth/cimd/cimd-ssrf.js +9 -68
- package/dist/oauth/cimd/cimd-ssrf.js.map +1 -1
- package/dist/oauth/cimd/cimd.d.ts +3 -16
- package/dist/oauth/cimd/cimd.d.ts.map +1 -1
- package/dist/oauth/cimd/cimd.js +16 -6
- package/dist/oauth/cimd/cimd.js.map +1 -1
- package/dist/oauth/cimd/dns.d.ts +4 -0
- package/dist/oauth/cimd/dns.d.ts.map +1 -0
- package/dist/oauth/cimd/dns.js +45 -0
- package/dist/oauth/cimd/dns.js.map +1 -0
- package/dist/oauth/cimd/failure.d.ts.map +1 -1
- package/dist/oauth/cimd/failure.js +4 -1
- package/dist/oauth/cimd/failure.js.map +1 -1
- package/dist/oauth/cimd/types.d.ts +15 -1
- package/dist/oauth/cimd/types.d.ts.map +1 -1
- package/dist/oauth/cimd/types.js +1 -1
- package/dist/oauth/cimd/types.js.map +1 -1
- package/dist/oauth/cimd/url-guard.d.ts +5 -0
- package/dist/oauth/cimd/url-guard.d.ts.map +1 -0
- package/dist/oauth/cimd/url-guard.js +53 -0
- package/dist/oauth/cimd/url-guard.js.map +1 -0
- package/dist/oauth/config.d.ts +3 -0
- package/dist/oauth/config.d.ts.map +1 -1
- package/dist/oauth/config.js +5 -1
- package/dist/oauth/config.js.map +1 -1
- package/dist/oauth/deprecated.d.ts +22 -22
- package/dist/oauth/deprecated.d.ts.map +1 -1
- package/dist/oauth/deprecated.js +22 -22
- package/dist/oauth/deprecated.js.map +1 -1
- package/dist/oauth/endpoints/authorize.js +1 -1
- package/dist/oauth/endpoints/authorize.js.map +1 -1
- package/dist/oauth/endpoints/consent/error-page.d.ts +6 -0
- package/dist/oauth/endpoints/consent/error-page.d.ts.map +1 -0
- package/dist/oauth/endpoints/consent/error-page.js +60 -0
- package/dist/oauth/endpoints/consent/error-page.js.map +1 -0
- package/dist/oauth/endpoints/{consent.d.ts → consent/handler.d.ts} +2 -2
- package/dist/oauth/endpoints/consent/handler.d.ts.map +1 -0
- package/dist/oauth/endpoints/{consent.js → consent/handler.js} +20 -13
- package/dist/oauth/endpoints/consent/handler.js.map +1 -0
- package/dist/oauth/endpoints/{consent-page.d.ts → consent/page.d.ts} +2 -2
- package/dist/oauth/endpoints/consent/page.d.ts.map +1 -0
- package/dist/oauth/endpoints/{consent-page.js → consent/page.js} +13 -3
- package/dist/oauth/endpoints/consent/page.js.map +1 -0
- package/dist/oauth/endpoints/metadata.d.ts +0 -1
- package/dist/oauth/endpoints/metadata.d.ts.map +1 -1
- package/dist/oauth/endpoints/metadata.js.map +1 -1
- package/dist/oauth/endpoints/register.js +3 -3
- package/dist/oauth/endpoints/register.js.map +1 -1
- package/dist/oauth/endpoints/token.d.ts.map +1 -1
- package/dist/oauth/endpoints/token.js +2 -5
- package/dist/oauth/endpoints/token.js.map +1 -1
- package/dist/oauth/endpoints/wellknown.d.ts.map +1 -1
- package/dist/oauth/endpoints/wellknown.js +2 -9
- package/dist/oauth/endpoints/wellknown.js.map +1 -1
- package/dist/oauth/policy/redirect.d.ts +3 -5
- package/dist/oauth/policy/redirect.d.ts.map +1 -1
- package/dist/oauth/policy/redirect.js +13 -5
- package/dist/oauth/policy/redirect.js.map +1 -1
- package/dist/oauth/router.js +1 -1
- package/dist/oauth/router.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/docs/ROADMAP.md +17 -229
- package/docs/compatibility.md +15 -3
- package/docs/reference.md +3 -2
- package/docs/security.md +58 -4
- package/docs/troubleshooting.md +4 -1
- package/package.json +7 -4
- package/dist/auth/signed-token.d.ts +0 -26
- package/dist/auth/signed-token.d.ts.map +0 -1
- package/dist/auth/signed-token.js +0 -96
- package/dist/auth/signed-token.js.map +0 -1
- package/dist/oauth/endpoints/consent-page.d.ts.map +0 -1
- package/dist/oauth/endpoints/consent-page.js.map +0 -1
- package/dist/oauth/endpoints/consent.d.ts.map +0 -1
- package/dist/oauth/endpoints/consent.js.map +0 -1
- package/docs/diagrams/demo-tool-result.png +0 -0
- package/docs/diagrams/mcp-trellis-architecture.html +0 -305
package/README.md
CHANGED
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
# mcp-trellis
|
|
2
2
|
|
|
3
|
-
Add
|
|
4
|
-
Reuse your users, login and permissions. Trellis handles MCP + OAuth.
|
|
3
|
+
Add user-scoped MCP to your existing TypeScript SaaS. Keep your login. Own your OAuth.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
Existing SaaS
|
|
8
|
-
users + login + data
|
|
9
|
-
↓
|
|
10
|
-
mcp-trellis
|
|
11
|
-
↓
|
|
12
|
-
Claude / ChatGPT / Gemini
|
|
13
|
-
```
|
|
5
|
+
Claude and ChatGPT act as the signed-in user, not as a shared service account. The OAuth authorization server runs inside your app, reuses your existing session, and has zero runtime dependencies.
|
|
14
6
|
|
|
15
7
|
[](https://www.npmjs.com/package/mcp-trellis)
|
|
8
|
+
[](https://www.npmjs.com/package/mcp-trellis)
|
|
16
9
|
[](https://github.com/amir1824/mcp-trellis/actions/workflows/ci.yml)
|
|
17
10
|
[](LICENSE)
|
|
11
|
+

|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-

|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npm install mcp-trellis
|
|
25
|
-
```
|
|
13
|
+

|
|
26
14
|
|
|
27
15
|
## The 30-second example
|
|
28
16
|
|
|
@@ -45,60 +33,79 @@ export const mcp = createMcpApp<{ userId: string }>({
|
|
|
45
33
|
});
|
|
46
34
|
```
|
|
47
35
|
|
|
48
|
-
|
|
36
|
+
That is 15 lines of code. You bring two things: your own session lookup, and a shared store where one-time login codes can be used once (an atomic set-if-absent: Redis, Postgres, D1 or a Durable Object; Workers KV has no atomic NX). Node 20+.
|
|
49
37
|
|
|
50
|
-
|
|
38
|
+
Client ID Metadata Documents (CIMD) are supported and off by default: set `cimd: true`. Pass `cimdCache` too (strongly recommended: without it every refresh and revoke re-fetches the client document). Dynamic Client Registration works too.
|
|
51
39
|
|
|
52
|
-
|
|
40
|
+
Protocol: MCP 2024-11-05, 2025-03-26 and 2025-06-18 (default 2025-06-18). The 2026-07-28 revision, which deprecates Dynamic Client Registration in favor of CIMD, is planned and not supported yet. See the [roadmap](docs/ROADMAP.md).
|
|
53
41
|
|
|
54
|
-
##
|
|
42
|
+
## Quick start
|
|
55
43
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
1. `npm install mcp-trellis`
|
|
45
|
+
2. Wire `resolveUser` and `loginUrl` to your existing session.
|
|
46
|
+
3. Wire `codeStore` to a shared single-use store (Redis, Postgres, D1 or a Durable Object). trellis does not ship one; copy the adapter in [examples/stores.ts](examples/stores.ts), which is typechecked example code. `allowInMemoryCodeStore: true` is for single-process demos.
|
|
47
|
+
4. Deploy over HTTPS, then add your `/mcp` URL as a custom connector in Claude. The [client guide](docs/guide.md#clients) lists the client profiles.
|
|
59
48
|
|
|
60
|
-
|
|
49
|
+
`npm run demo` runs the local Alice and Bob demo: login, consent, then `list_my_projects`. See [examples/saas-demo](examples/saas-demo/README.md).
|
|
61
50
|
|
|
62
|
-
|
|
63
|
-
- You already have an MCP server and authorization server that meet your needs.
|
|
64
|
-
- You need protocol features or client combinations absent from the [verified support table](docs/compatibility.md).
|
|
65
|
-
- You only need a local stdio tool with no remote authorization flow.
|
|
51
|
+
## How it fits together
|
|
66
52
|
|
|
67
|
-
|
|
53
|
+

|
|
68
54
|
|
|
69
|
-
The
|
|
55
|
+
The blue boxes ship in the package. The purple and green boxes are yours to plug in: your session, a code store, token handling and your data layer. `signedTokenAuth` gives you a default for the token ports. Editable source: [architecture.drawio](docs/media/architecture.drawio).
|
|
70
56
|
|
|
71
|
-
|
|
57
|
+
## When to use it, and when not
|
|
72
58
|
|
|
73
|
-
|
|
59
|
+
Use it when:
|
|
74
60
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
| ChatGPT / Codex (`codex`) | Public client, PKCE; configure exact hosted callback as needed | Pending for each product |
|
|
79
|
-
| Gemini Enterprise (`gemini`) | Pre-registered client; secret basic/post; requires `clientStore` | Pending; does not imply every Gemini product |
|
|
61
|
+
- Your TypeScript app already has login and user data, and Claude or ChatGPT should read and act on a signed-in user's own data (projects, tickets, invoices) with no shared service account.
|
|
62
|
+
- You want the MCP handler and the OAuth endpoints in one package, on Next.js, Express or Cloudflare Workers.
|
|
63
|
+
- Your tools should follow the permissions and tenant boundaries the app already enforces ([multi-tenant](docs/guide.md#multi-tenant-saas-connectors)).
|
|
80
64
|
|
|
81
|
-
|
|
65
|
+
Skip it when:
|
|
82
66
|
|
|
83
|
-
|
|
67
|
+
- You need a managed identity provider, a user database, or a login system built for you.
|
|
68
|
+
- You need MCP spec 2026-07-28 today, or a client combination that is not in the [compatibility table](docs/compatibility.md).
|
|
69
|
+
- You only need a local stdio tool, or you already run an MCP server and authorization server that meet your needs.
|
|
70
|
+
|
|
71
|
+
## Compared with the alternatives
|
|
72
|
+
|
|
73
|
+
| Option | Who hosts the authorization server | You need | Choose it when |
|
|
74
|
+
|---|---|---|---|
|
|
75
|
+
| **mcp-trellis** | Your app | Your session lookup and a shared single-use store | You want no new vendor, database or hosted service |
|
|
76
|
+
| [Better Auth MCP plugin](https://better-auth.com/docs/plugins/mcp) | Your app | Better Auth, its database schema and the JWT plugin | You already use Better Auth, or you need spec 2026-07-28 today (it targets that revision) |
|
|
77
|
+
| [Cloudflare workers-oauth-provider](https://github.com/cloudflare/workers-oauth-provider) | Your Worker | A Workers-only app and a KV namespace | You run only on Workers and want Cloudflare's own library |
|
|
78
|
+
| [Scalekit](https://docs.scalekit.com/authenticate/mcp/quickstart/) | Scalekit | A Scalekit dashboard and SDK | You want a hosted identity provider instead of running the authorization server |
|
|
79
|
+
| [WorkOS AuthKit (Standalone Connect)](https://workos.com/docs/authkit/mcp) | WorkOS | A Login URI set in WorkOS, and a call to AuthKit's completion API from your login | You want the authorization server hosted for you and still keep your own login |
|
|
80
|
+
| [Official TypeScript SDK v2](https://ts.sdk.modelcontextprotocol.io/v2/migration/upgrade-to-v2) | Nobody. v2 ships resource-server helpers only; the old authorization server helpers live in a frozen, deprecated `@modelcontextprotocol/server-legacy/auth`. | An authorization server from somewhere else | You already have one and only need to verify its tokens |
|
|
84
81
|
|
|
85
|
-
|
|
82
|
+
Checked 2026-11-07. These projects move fast, so re-verify before you decide.
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
## Integrate with your app
|
|
88
85
|
|
|
89
|
-
|
|
86
|
+
Choose your token mode:
|
|
90
87
|
|
|
91
|
-
|
|
88
|
+
- Start with `signedTokenAuth`: one secret. Access tokens are stateless, and the only stored state is single-use marks in `codeStore`. Access tokens cannot be revoked before they expire.
|
|
89
|
+
- Need revocable tokens, refresh-token families or your own JWT/JWKS? Implement the `mintAccessToken`, `verifyToken`, `refreshAccessToken` and `revokeToken` ports.
|
|
90
|
+
- Mint tokens for the requested MCP resource and return the verified `audience`. The library rejects a mismatch before a tool runs.
|
|
91
|
+
- A token outlives the login session that produced it. Pass `isUserActive` so a removed or disabled user stops working on the next request. Pass `refreshTtlSeconds` (needs `codeStore` and `isUserActive`) so a connector renews without sending the user back to the browser. What ends what: [Sessions and expiry](docs/security.md#sessions-and-expiry).
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
Before you ship:
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
- Your data layer must enforce ownership and tenancy. The library does not.
|
|
96
|
+
- Users see an OAuth consent screen even when already signed in to your app.
|
|
97
|
+
- Browser callers that send an `Origin` header need `allowedRequestOrigins`. It is fail-closed by default.
|
|
98
|
+
- CIMD fetches client metadata over HTTPS, for public clients only. The connection is not pinned to the looked-up IP, so DNS rebinding is possible. Do not rely on it as your only SSRF defense ([security.md](docs/security.md)).
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
## Clients and compatibility
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
| Profile | Implemented server flow | Live client evidence |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| Claude (`claude`) | Dynamic registration, public client, PKCE; CIMD with `cimd: true` | Used in the maintainer's own production servers (reported); logged live pass pending |
|
|
105
|
+
| ChatGPT / Codex (`codex`) | Public client, PKCE; ChatGPT's stable and per-connector callbacks allowed | Pending for each product |
|
|
106
|
+
| Gemini Enterprise (`gemini`) | Pre-registered client; secret basic/post; requires `clientStore` | Pending; does not imply every Gemini product |
|
|
100
107
|
|
|
101
|
-
|
|
108
|
+
The OAuth flows above run in automated tests and in the local demo. [docs/compatibility.md](docs/compatibility.md) has the checklist for testing each vendor app, plus [client configuration](docs/guide.md#clients) and [troubleshooting](docs/troubleshooting.md).
|
|
102
109
|
|
|
103
110
|
## Docs
|
|
104
111
|
|
|
@@ -106,14 +113,16 @@ Host recipes, ports, tools, and multi-tenant: [docs/guide.md](docs/guide.md).
|
|
|
106
113
|
|-----|----------|
|
|
107
114
|
| [docs/guide.md](docs/guide.md) | Architecture, clients, recipes, ports, tools, multi-tenant |
|
|
108
115
|
| [docs/reference.md](docs/reference.md) | Routes, methods, status codes, options, exports |
|
|
109
|
-
| [docs/security.md](docs/security.md) | Protocol promises, threat model, not in scope |
|
|
116
|
+
| [docs/security.md](docs/security.md) | Protocol promises, threat model, sessions, not in scope |
|
|
117
|
+
| [docs/compatibility.md](docs/compatibility.md) | Protocol versions, client profiles, test evidence |
|
|
118
|
+
| [docs/troubleshooting.md](docs/troubleshooting.md) | Common connection and token errors |
|
|
110
119
|
| [docs/ROADMAP.md](docs/ROADMAP.md) | What's next |
|
|
111
120
|
|
|
112
|
-
|
|
121
|
+
Examples: [saas-demo](examples/saas-demo/README.md), [nextjs-saas](examples/nextjs-saas/README.md), and [project-desk](examples/project-desk/README.md), a reference remote server example. More recipes in [`examples/`](examples/).
|
|
113
122
|
|
|
114
123
|
## Contributing
|
|
115
124
|
|
|
116
|
-
|
|
125
|
+
Found a bug or a gap? Open an [issue](https://github.com/amir1824/mcp-trellis/issues). Pull requests are welcome, see [CONTRIBUTING.md](CONTRIBUTING.md). Everyone taking part follows the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
117
126
|
|
|
118
127
|
```bash
|
|
119
128
|
npm test
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA0BtD,YAAY,EACV,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA0BtD,YAAY,EACV,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD,CAAC;AAyLF,eAAO,MAAM,YAAY,GAAI,IAAI,EAAE,SAAS,aAAa,CAAC,IAAI,CAAC,KAAG,MA8CjE,CAAC"}
|
package/dist/app.js
CHANGED
|
@@ -111,6 +111,7 @@ const buildOAuthOptions = (options, input) => {
|
|
|
111
111
|
// Client profiles are the source of truth for callbacks.
|
|
112
112
|
extra: [...redirectUrisFor(clients), ...(options.extraRedirectUris ?? [])],
|
|
113
113
|
allowClaude: false,
|
|
114
|
+
allowChatgptConnector: clients.includes("codex"),
|
|
114
115
|
allowLoopback: options.allowLoopback ?? true,
|
|
115
116
|
},
|
|
116
117
|
ports: {
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMAC-signed JSON payloads for `signedTokenAuth`. Internal: not exported
|
|
3
|
+
* from the package.
|
|
4
|
+
*
|
|
5
|
+
* The HKDF `info` string is the only thing that stops one token type being
|
|
6
|
+
* presented as another (they share the `body.signature` shape), so the kind
|
|
7
|
+
* is a closed set here and never a caller-chosen label.
|
|
8
|
+
*/
|
|
9
|
+
declare const TOKEN_LABELS: {
|
|
10
|
+
readonly access: "mcp-trellis:signed-access-token:v1";
|
|
11
|
+
readonly refresh: "mcp-trellis:signed-refresh-token:v1";
|
|
12
|
+
};
|
|
13
|
+
type SignedTokenKind = keyof typeof TOKEN_LABELS;
|
|
14
|
+
export declare const signPayload: (secret: string, kind: SignedTokenKind, payload: object) => Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* The decoded payload of a token of `kind` signed by one of `secrets`, or
|
|
17
|
+
* null for anything else. Callers still shape-check what comes back: a valid
|
|
18
|
+
* signature proves who signed it, not what it contains.
|
|
19
|
+
*/
|
|
20
|
+
export declare const verifyPayload: (secrets: readonly string[], kind: SignedTokenKind, token: string) => Promise<unknown>;
|
|
21
|
+
/** The claims both token kinds carry. */
|
|
22
|
+
export type GrantClaims = {
|
|
23
|
+
userId: string;
|
|
24
|
+
scopes: string[];
|
|
25
|
+
audience: string;
|
|
26
|
+
exp: number;
|
|
27
|
+
};
|
|
28
|
+
/** The shared claims of a verified payload, or null if one is missing, malformed or expired. */
|
|
29
|
+
export declare const parseGrantClaims: (payload: unknown) => GrantClaims | null;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=payload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/auth/signed/payload.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,QAAA,MAAM,YAAY;;;CAGR,CAAC;AAEX,KAAK,eAAe,GAAG,MAAM,OAAO,YAAY,CAAC;AAiBjD,eAAO,MAAM,WAAW,GACtB,QAAQ,MAAM,EACd,MAAM,eAAe,EACrB,SAAS,MAAM,KACd,OAAO,CAAC,MAAM,CAYhB,CAAC;AAqBF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,SAAS,MAAM,EAAE,EAC1B,MAAM,eAAe,EACrB,OAAO,MAAM,KACZ,OAAO,CAAC,OAAO,CAWjB,CAAC;AAEF,yCAAyC;AACzC,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAK9F,gGAAgG;AAChG,eAAO,MAAM,gBAAgB,GAAI,SAAS,OAAO,KAAG,WAAW,GAAG,IAQjE,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMAC-signed JSON payloads for `signedTokenAuth`. Internal: not exported
|
|
3
|
+
* from the package.
|
|
4
|
+
*
|
|
5
|
+
* The HKDF `info` string is the only thing that stops one token type being
|
|
6
|
+
* presented as another (they share the `body.signature` shape), so the kind
|
|
7
|
+
* is a closed set here and never a caller-chosen label.
|
|
8
|
+
*/
|
|
9
|
+
import { bytesToBase64Url, fromBase64Url } from "../../oauth/crypto/base64url.js";
|
|
10
|
+
import { hkdfKey } from "../../oauth/crypto/hkdf.js";
|
|
11
|
+
import { isJsonObject } from "../../util/json.js";
|
|
12
|
+
const TOKEN_LABELS = {
|
|
13
|
+
access: "mcp-trellis:signed-access-token:v1",
|
|
14
|
+
refresh: "mcp-trellis:signed-refresh-token:v1",
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Refresh tokens are capped, and `signPayload` enforces it so an oversized one
|
|
18
|
+
* fails when issued, not on every later use. Access tokens are bounded only by
|
|
19
|
+
* the server's header limit, so a long user id still verifies.
|
|
20
|
+
*/
|
|
21
|
+
const MAX_REFRESH_TOKEN_LENGTH = 4096;
|
|
22
|
+
const exceedsCap = (kind, token) => kind === "refresh" && token.length > MAX_REFRESH_TOKEN_LENGTH;
|
|
23
|
+
const HMAC_SHA256 = { name: "HMAC", hash: "SHA-256", length: 256 };
|
|
24
|
+
const tokenKey = (secret, kind, usage) => hkdfKey(secret, { info: TOKEN_LABELS[kind], algorithm: HMAC_SHA256, usage });
|
|
25
|
+
export const signPayload = async (secret, kind, payload) => {
|
|
26
|
+
const key = await tokenKey(secret, kind, "sign");
|
|
27
|
+
const body = bytesToBase64Url(new TextEncoder().encode(JSON.stringify(payload)));
|
|
28
|
+
const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(body));
|
|
29
|
+
const token = `${body}.${bytesToBase64Url(signature)}`;
|
|
30
|
+
if (exceedsCap(kind, token)) {
|
|
31
|
+
throw new Error(`signedTokenAuth: a refresh token would be ${token.length} characters, over the ` +
|
|
32
|
+
`${MAX_REFRESH_TOKEN_LENGTH} limit — shorten the user id or the scope list`);
|
|
33
|
+
}
|
|
34
|
+
return token;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* True when `signature` verifies `body` under any of `secrets`, tried in order.
|
|
38
|
+
* ponytail: one HMAC per rotation entry for a forged token; `signedTokenAuth`
|
|
39
|
+
* caps the list at 8. Upgrade: carry a key id in the token and look the key up.
|
|
40
|
+
*/
|
|
41
|
+
const signedByAny = async (secrets, kind, token) => {
|
|
42
|
+
const signature = fromBase64Url(token.signature);
|
|
43
|
+
const message = new TextEncoder().encode(token.body);
|
|
44
|
+
for (const secret of secrets) {
|
|
45
|
+
const key = await tokenKey(secret, kind, "verify");
|
|
46
|
+
if (await crypto.subtle.verify("HMAC", key, signature, message))
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The decoded payload of a token of `kind` signed by one of `secrets`, or
|
|
53
|
+
* null for anything else. Callers still shape-check what comes back: a valid
|
|
54
|
+
* signature proves who signed it, not what it contains.
|
|
55
|
+
*/
|
|
56
|
+
export const verifyPayload = async (secrets, kind, token) => {
|
|
57
|
+
if (exceedsCap(kind, token))
|
|
58
|
+
return null;
|
|
59
|
+
const parts = token.split(".");
|
|
60
|
+
const [body, signature] = parts;
|
|
61
|
+
if (parts.length !== 2 || !body || !signature)
|
|
62
|
+
return null;
|
|
63
|
+
try {
|
|
64
|
+
if (!(await signedByAny(secrets, kind, { body, signature })))
|
|
65
|
+
return null;
|
|
66
|
+
return JSON.parse(new TextDecoder().decode(fromBase64Url(body)));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
73
|
+
/** The shared claims of a verified payload, or null if one is missing, malformed or expired. */
|
|
74
|
+
export const parseGrantClaims = (payload) => {
|
|
75
|
+
if (!isJsonObject(payload))
|
|
76
|
+
return null;
|
|
77
|
+
const { userId, scopes, audience, exp } = payload;
|
|
78
|
+
if (typeof userId !== "string" || typeof audience !== "string" || !isStringArray(scopes)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
if (typeof exp !== "number" || !Number.isFinite(exp) || exp < Date.now())
|
|
82
|
+
return null;
|
|
83
|
+
return { userId, scopes, audience, exp };
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.js","sourceRoot":"","sources":["../../../src/auth/signed/payload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateless refresh tokens for `signedTokenAuth`. Internal: not exported
|
|
3
|
+
* from the package.
|
|
4
|
+
*
|
|
5
|
+
* Signed, not stored. The only state is the single-use `jti` mark in the
|
|
6
|
+
* shared `codeStore`, which is what makes rotation work: a refresh token is
|
|
7
|
+
* accepted once and its successor is a different token. The mark lives until
|
|
8
|
+
* the grant's `exp`, one per refresh.
|
|
9
|
+
*
|
|
10
|
+
* ponytail: a replayed, already-rotated token is refused, but there is no
|
|
11
|
+
* token family to revoke when that happens, so the thief's successor stays
|
|
12
|
+
* valid until `exp` or until `isUserActive` turns false. Upgrade: an opaque
|
|
13
|
+
* server-side refresh store (see `examples/project-desk/src/tokens.ts`).
|
|
14
|
+
*/
|
|
15
|
+
import type { CodeStore } from "../../oauth/crypto/codes.js";
|
|
16
|
+
import type { MintAccessTokenInput, MintedToken, RefreshAccessTokenInput, RevokeTokenInput } from "../../oauth/types.js";
|
|
17
|
+
export type SecretList = readonly [string, ...string[]];
|
|
18
|
+
export type UserCheck = (userId: string) => boolean | Promise<boolean>;
|
|
19
|
+
export type AccessMinter = (input: MintAccessTokenInput) => Promise<MintedToken>;
|
|
20
|
+
type RefreshTokenConfig = {
|
|
21
|
+
secrets: SecretList;
|
|
22
|
+
lifetimeMs: number;
|
|
23
|
+
codeStore: CodeStore;
|
|
24
|
+
isUserActive: UserCheck;
|
|
25
|
+
mintAccess: AccessMinter;
|
|
26
|
+
};
|
|
27
|
+
export type RefreshTokens = {
|
|
28
|
+
issue: (grant: MintAccessTokenInput) => Promise<string>;
|
|
29
|
+
refresh: (input: RefreshAccessTokenInput) => Promise<MintedToken | null>;
|
|
30
|
+
revoke: (input: RevokeTokenInput) => Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
export declare const createRefreshTokens: (config: RefreshTokenConfig) => RefreshTokens;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../../src/auth/signed/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAI7D,OAAO,KAAK,EACV,oBAAoB,EACpB,WAAW,EACX,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;AAoB9B,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;AACxD,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AACvE,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAEjF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,SAAS,CAAC;IACxB,UAAU,EAAE,YAAY,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,OAAO,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACzE,MAAM,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD,CAAC;AAqFF,eAAO,MAAM,mBAAmB,GAAI,QAAQ,kBAAkB,KAAG,aAI/D,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateless refresh tokens for `signedTokenAuth`. Internal: not exported
|
|
3
|
+
* from the package.
|
|
4
|
+
*
|
|
5
|
+
* Signed, not stored. The only state is the single-use `jti` mark in the
|
|
6
|
+
* shared `codeStore`, which is what makes rotation work: a refresh token is
|
|
7
|
+
* accepted once and its successor is a different token. The mark lives until
|
|
8
|
+
* the grant's `exp`, one per refresh.
|
|
9
|
+
*
|
|
10
|
+
* ponytail: a replayed, already-rotated token is refused, but there is no
|
|
11
|
+
* token family to revoke when that happens, so the thief's successor stays
|
|
12
|
+
* valid until `exp` or until `isUserActive` turns false. Upgrade: an opaque
|
|
13
|
+
* server-side refresh store (see `examples/project-desk/src/tokens.ts`).
|
|
14
|
+
*/
|
|
15
|
+
import { randomBase64Url, sha256Base64Url } from "../../oauth/crypto/pkce.js";
|
|
16
|
+
import { resourcesEqual } from "../../oauth/policy/resource.js";
|
|
17
|
+
import { formatScope, parseScope } from "../../oauth/policy/scope.js";
|
|
18
|
+
import { isJsonObject } from "../../util/json.js";
|
|
19
|
+
import { parseGrantClaims, signPayload, verifyPayload } from "./payload.js";
|
|
20
|
+
const JTI_BYTES = 16;
|
|
21
|
+
const MAX_JTI_LENGTH = 64;
|
|
22
|
+
/** Keeps a refresh `jti` apart from auth-code jtis (bare) and consent tickets (`ct:`). */
|
|
23
|
+
const JTI_KEY_PREFIX = "rt:";
|
|
24
|
+
const hasText = (value) => typeof value === "string" && value !== "";
|
|
25
|
+
const parseRefreshPayload = (payload) => {
|
|
26
|
+
if (!isJsonObject(payload))
|
|
27
|
+
return null;
|
|
28
|
+
const claims = parseGrantClaims(payload);
|
|
29
|
+
if (!claims || claims.userId === "" || claims.audience === "")
|
|
30
|
+
return null;
|
|
31
|
+
const { clientHash, jti } = payload;
|
|
32
|
+
if (!hasText(clientHash) || !hasText(jti) || jti.length > MAX_JTI_LENGTH)
|
|
33
|
+
return null;
|
|
34
|
+
return { ...claims, clientHash, jti };
|
|
35
|
+
};
|
|
36
|
+
/** The requested scope narrowed to what the grant holds, or null if it asks for more. */
|
|
37
|
+
const narrowedScopes = (granted, requested) => {
|
|
38
|
+
if (requested === undefined)
|
|
39
|
+
return granted;
|
|
40
|
+
const wanted = parseScope(requested);
|
|
41
|
+
return wanted.length > 0 && wanted.every((scope) => granted.includes(scope)) ? wanted : null;
|
|
42
|
+
};
|
|
43
|
+
const signRefresh = (config, payload) => signPayload(config.secrets[0], "refresh", payload);
|
|
44
|
+
const readRefresh = async (config, token) => parseRefreshPayload(await verifyPayload(config.secrets, "refresh", token));
|
|
45
|
+
// `=== true`: only a store that says so burns the token; a throw propagates
|
|
46
|
+
// as a 500 instead of letting a replay through.
|
|
47
|
+
const burnRefresh = async (config, payload) => (await config.codeStore.consume(`${JTI_KEY_PREFIX}${payload.jti}`, payload.exp)) === true;
|
|
48
|
+
const issueRefresh = async (config, grant) => signRefresh(config, {
|
|
49
|
+
userId: grant.userId,
|
|
50
|
+
scopes: parseScope(grant.scope),
|
|
51
|
+
audience: grant.resource,
|
|
52
|
+
clientHash: await sha256Base64Url(grant.clientId),
|
|
53
|
+
exp: Date.now() + config.lifetimeMs,
|
|
54
|
+
jti: randomBase64Url(JTI_BYTES),
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* Order matters: every check that can fail on the caller's input runs before
|
|
58
|
+
* the burn, so a wrong-client or wrong-resource attempt cannot spend the
|
|
59
|
+
* rightful holder's token.
|
|
60
|
+
*/
|
|
61
|
+
const rotateRefresh = async (config, input) => {
|
|
62
|
+
const payload = await readRefresh(config, input.refreshToken);
|
|
63
|
+
if (!payload || payload.clientHash !== (await sha256Base64Url(input.clientId)))
|
|
64
|
+
return null;
|
|
65
|
+
if (!resourcesEqual(payload.audience, input.resource))
|
|
66
|
+
return null;
|
|
67
|
+
const scopes = narrowedScopes(payload.scopes, input.scope);
|
|
68
|
+
if (!scopes)
|
|
69
|
+
return null;
|
|
70
|
+
if ((await config.isUserActive(payload.userId)) !== true)
|
|
71
|
+
return null;
|
|
72
|
+
if (!(await burnRefresh(config, payload)))
|
|
73
|
+
return null;
|
|
74
|
+
const access = await config.mintAccess({
|
|
75
|
+
userId: payload.userId,
|
|
76
|
+
clientId: input.clientId,
|
|
77
|
+
scope: formatScope(scopes),
|
|
78
|
+
resource: payload.audience,
|
|
79
|
+
});
|
|
80
|
+
const successor = await signRefresh(config, { ...payload, jti: randomBase64Url(JTI_BYTES) });
|
|
81
|
+
return { ...access, refreshToken: successor };
|
|
82
|
+
};
|
|
83
|
+
/** RFC 7009: an unusable or foreign token is a silent no-op; a store failure is not. */
|
|
84
|
+
const revokeRefresh = async (config, input) => {
|
|
85
|
+
const payload = await readRefresh(config, input.token);
|
|
86
|
+
if (payload && payload.clientHash === (await sha256Base64Url(input.clientId))) {
|
|
87
|
+
await burnRefresh(config, payload);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
export const createRefreshTokens = (config) => ({
|
|
91
|
+
issue: (grant) => issueRefresh(config, grant),
|
|
92
|
+
refresh: (input) => rotateRefresh(config, input),
|
|
93
|
+
revoke: (input) => revokeRefresh(config, input),
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../../src/auth/signed/refresh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convenience auth: one secret seals OAuth codes (via `codeSecret`) and
|
|
3
|
+
* signs short-lived access tokens. Domain separation is HKDF, not luck —
|
|
4
|
+
* access tokens, refresh tokens and sealed codes each use their own label.
|
|
5
|
+
*
|
|
6
|
+
* Tokens are stateless, so an issued access token stays valid until `exp`
|
|
7
|
+
* whatever happens to the login session. Pass `isUserActive` to close that:
|
|
8
|
+
* it runs on every verified token. Refresh is opt-in (`refreshTtlSeconds`);
|
|
9
|
+
* see `refresh.ts` for its limits.
|
|
10
|
+
*
|
|
11
|
+
* ponytail: access tokens cannot be revoked before `exp`; `/revoke` burns
|
|
12
|
+
* refresh tokens only. Upgrade: supply your own `mintAccessToken` /
|
|
13
|
+
* `verifyToken` over a store (see `examples/project-desk/src/tokens.ts`).
|
|
14
|
+
*/
|
|
15
|
+
import type { McpAppAuth } from "../../app-options.js";
|
|
16
|
+
import type { CodeStore } from "../../oauth/crypto/codes.js";
|
|
17
|
+
import type { OAuthUser } from "../../oauth/types.js";
|
|
18
|
+
import { type UserCheck } from "./refresh.js";
|
|
19
|
+
export type SignedTokenAuthOptions = {
|
|
20
|
+
/** One secret, or a rotation list: the first signs, every entry verifies (and seals codes). */
|
|
21
|
+
secret: string | string[];
|
|
22
|
+
resolveUser: (request: Request) => Promise<OAuthUser | null>;
|
|
23
|
+
loginUrl: (request: Request, nextPath: string) => string;
|
|
24
|
+
/** Access-token lifetime in seconds. Default 3600. Must be finite and > 0. */
|
|
25
|
+
ttlSeconds?: number;
|
|
26
|
+
/** Passthrough — production still needs a shared store across instances. */
|
|
27
|
+
codeStore?: CodeStore;
|
|
28
|
+
/**
|
|
29
|
+
* Checked on every verified token and every refresh; only `true` passes and a
|
|
30
|
+
* throw is a 500. See docs/security.md#sessions-and-expiry.
|
|
31
|
+
*/
|
|
32
|
+
isUserActive?: UserCheck;
|
|
33
|
+
/**
|
|
34
|
+
* Enables rotating refresh tokens; also the absolute lifetime of one grant, counted from the code exchange.
|
|
35
|
+
* Needs `codeStore` and `isUserActive`. See docs/security.md#sessions-and-expiry.
|
|
36
|
+
*/
|
|
37
|
+
refreshTtlSeconds?: number;
|
|
38
|
+
};
|
|
39
|
+
/** Builds a complete `McpAppAuth` from one secret plus your session resolver. */
|
|
40
|
+
export declare const signedTokenAuth: (options: SignedTokenAuthOptions) => McpAppAuth;
|
|
41
|
+
//# sourceMappingURL=token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../../src/auth/signed/token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,sBAAsB,CAAC;AAEtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAE7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAMtB,MAAM,MAAM,sBAAsB,GAAG;IACnC,+FAA+F;IAC/F,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC7D,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AA8EF,iFAAiF;AACjF,eAAO,MAAM,eAAe,GAAI,SAAS,sBAAsB,KAAG,UAoBjE,CAAC"}
|