@atlasauth/pca-mcp-rs 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +62 -2
- package/dist/index.d.ts +384 -0
- package/dist/index.js +542 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Atlas Authorization
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,63 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @atlasauth/pca-mcp-rs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP authorization resource server for the 2026-07-28 MCP authorization revision, backed by Proof-Carrying Authority (PCA). Where `@atlasauth/pca-mcp` wraps tool handlers on the client so each call carries a PCActn, this package makes the server that receives those calls a correct OAuth 2.1 resource server: it serves RFC 9728 Protected Resource Metadata, validates Client-ID Metadata Documents, enforces RFC 8707 resource binding and RFC 9207 issuer validation, accumulates scopes down the delegation chain, and issues RFC 9470 step-up challenges. The authority is the PCActn, not a bearer token: its signed `aud` is the resource binding.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm i @atlasauth/pca-mcp-rs @atlasauth/pca
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Everything is pure and runtime-neutral (no Node builtins, no framework), so it mounts on Express, Fastify, Hono, Next.js, Fetch handlers or any router.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { prmHandler, authorizeRequest } from '@atlasauth/pca-mcp-rs';
|
|
17
|
+
|
|
18
|
+
const resource = 'https://mcp.example.com';
|
|
19
|
+
const authorizationServers = ['https://as.example.com'];
|
|
20
|
+
|
|
21
|
+
// 1. Serve the Protected Resource Metadata (RFC 9728) at the returned path.
|
|
22
|
+
const prm = prmHandler({ resource, authorizationServers, scopesSupported: ['refund:write'] });
|
|
23
|
+
// GET prm.path -> JSON prm.body
|
|
24
|
+
|
|
25
|
+
// 2. Admit each tool call.
|
|
26
|
+
const result = await authorizeRequest(req /* { headers, body? } */, {
|
|
27
|
+
resource, // must equal the PCActn's signed `aud`
|
|
28
|
+
authorizationServers, // allowed grant issuers (RFC 9207)
|
|
29
|
+
resolveGrant: async (ref) => grants.get(ref) ?? null,
|
|
30
|
+
prmUrl: `${resource}${prm.path}`,
|
|
31
|
+
tool: 'refund',
|
|
32
|
+
toolScopes: { refund: 'refund:write' },
|
|
33
|
+
toolStepUp: { refund: 2 }, // optional: require a guardian (2) or principal (3) co-sign
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!result.ok) {
|
|
37
|
+
// result.status is 401 or 403; send the challenge header with the response.
|
|
38
|
+
return new Response(JSON.stringify({ error: result.error }), {
|
|
39
|
+
status: result.status,
|
|
40
|
+
headers: { 'WWW-Authenticate': result.wwwAuthenticate },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
result.pcactn; result.grantedScopes; result.checks;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`authorizeRequest` reads the PCActn from the `PCA-Action` header (base64url) or a JSON body `{ pcactn }`, and fails closed: any missing, mismatched or unverifiable element is denied with the correct status and `WWW-Authenticate` challenge, and unexpected errors deny with 403.
|
|
47
|
+
|
|
48
|
+
## API
|
|
49
|
+
|
|
50
|
+
- `protectedResourceMetadata(opts)`, `prmHandler(opts)`, `WELL_KNOWN_OAUTH_PRM` - RFC 9728 metadata.
|
|
51
|
+
- `authorizeRequest(req, opts)`, `extractPCActn(req)` - the admission check.
|
|
52
|
+
- `wwwAuthenticateChallenge(opts)` - build a `WWW-Authenticate: Bearer ...` value.
|
|
53
|
+
- `clientIdMetadataDocument(opts)`, `verifyCimd(input, opts?)` - Client-ID Metadata Documents (https `client_id`, redirect URI checks).
|
|
54
|
+
- `accumulateScopes(caveats, base?)`, `isScopeCaveat` - scope narrowing from `scope` caveats.
|
|
55
|
+
- `requiredRolesForTier`, `defaultStepUpSatisfied`, `StepUpChallenge` - step-up helpers.
|
|
56
|
+
|
|
57
|
+
## Status
|
|
58
|
+
|
|
59
|
+
Step-up satisfaction defaults to a presence check: `defaultStepUpSatisfied` only tests that the PCActn carries a threshold share from a qualifying role. It does not cryptographically verify the shares. Pass `stepUpSatisfied` to `authorizeRequest` to plug in a real threshold verifier. `authorizeRequest` calls the PCA core verifier with the grant and audience only; it takes no replay store or revocation list, so add those around it if you need them. PCA cryptography has not been independently audited.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT - see LICENSE
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP authorization RESOURCE SERVER for the 2026-07-28 revision, backed by Proof-Carrying Authority.
|
|
3
|
+
*
|
|
4
|
+
* This is the RS SIDE of the de-facto agent↔tool wire. Where `@atlasauth/pca-mcp` wraps a tool handler on
|
|
5
|
+
* the CLIENT so each call carries a PCActn, THIS package makes the server that RECEIVES those calls a
|
|
6
|
+
* correct OAuth-2.1 resource server per the MCP authorization revision of 2026-07-28 — the revision that
|
|
7
|
+
*
|
|
8
|
+
* • keeps RFC 9728 Protected Resource Metadata discovery and the `WWW-Authenticate: Bearer
|
|
9
|
+
* resource_metadata="…"` 401 challenge (unchanged since 2025-11-25),
|
|
10
|
+
* • REPLACES Dynamic Client Registration with Client-ID Metadata Documents (CIMD): a client's
|
|
11
|
+
* `client_id` IS an https URL that dereferences to its own RFC-7591-shaped metadata document, so a
|
|
12
|
+
* resource/authorization server never has to register a client out of band,
|
|
13
|
+
* • REQUIRES RFC 8707 resource-indicator enforcement (a credential is bound to exactly one resource
|
|
14
|
+
* server, so a token minted for RS-A cannot be replayed to RS-B), and
|
|
15
|
+
* • REQUIRES RFC 9207 `iss` authorization-server validation (a token whose issuer is not an expected
|
|
16
|
+
* AS is rejected — the authorization-server mix-up defense),
|
|
17
|
+
* • layers OAuth step-up (RFC 9470, `insufficient_user_authentication`) for actions that need a human.
|
|
18
|
+
*
|
|
19
|
+
* The AUTHORITY here is the PCActn, not a bearer token: a PCActn's SIGNED `aud` is the RFC 8707 resource
|
|
20
|
+
* binding (it cannot be spoofed the way a `resource` indicator can), its capability chain roots at a
|
|
21
|
+
* pinned issuer (the RFC 9207 anchor), and its signed caveats carry the scope grant that NARROWS down the
|
|
22
|
+
* delegation chain. `authorizeRequest` is the admission check that verifies all of this through the PCA
|
|
23
|
+
* core (`verifyPCActnCore`) and FAILS CLOSED: any missing / mismatched / unverifiable element denies with
|
|
24
|
+
* the correct 401/403 + `WWW-Authenticate` challenge. Step-up is wired to PCA's FROST/CIBA threshold
|
|
25
|
+
* surface: a tool that needs escalation returns a 401 the MCP client drives to collect a guardian /
|
|
26
|
+
* principal co-sign, then re-presents a PCActn carrying the threshold.
|
|
27
|
+
*
|
|
28
|
+
* Specs implemented (cited per export):
|
|
29
|
+
* - RFC 9728 — OAuth 2.0 Protected Resource Metadata (https://www.rfc-editor.org/rfc/rfc9728)
|
|
30
|
+
* - RFC 8707 — Resource Indicators for OAuth 2.0 (https://www.rfc-editor.org/rfc/rfc8707)
|
|
31
|
+
* - RFC 9207 — OAuth 2.0 Authorization Server Issuer Id (https://www.rfc-editor.org/rfc/rfc9207)
|
|
32
|
+
* - RFC 6750 — OAuth 2.0 Bearer Token Usage (the challenge shape)
|
|
33
|
+
* - RFC 7235 — HTTP/1.1 Authentication (multiple comma-separated challenges in one header)
|
|
34
|
+
* - RFC 9470 — OAuth 2.0 Step Up Authentication Challenge (https://www.rfc-editor.org/rfc/rfc9470)
|
|
35
|
+
* - Client-ID Metadata Documents (CIMD) — the DCR replacement adopted by MCP authorization 2026-07-28.
|
|
36
|
+
* - MCP Authorization, protocol revision 2026-07-28.
|
|
37
|
+
*
|
|
38
|
+
* Pure: every export is I/O-free and runtime-neutral (no Node builtins, no framework) so the same code
|
|
39
|
+
* mounts on Express / Fastify / Hono / Next / Fetch or any router. The PRM + CIMD documents are PUBLIC,
|
|
40
|
+
* non-secret configuration. This package does NOT import or modify `@atlasauth/pca-oauth` or
|
|
41
|
+
* `@atlasauth/pca-mcp`; it mirrors their conventions and verifies through the shared `@atlasauth/pca` core.
|
|
42
|
+
*/
|
|
43
|
+
import { type Capability, type CapabilityChain, type Caveat, type CheckStatus, type PcaDiscoveryDocument, type PCActn, type SignerRole } from '@atlasauth/pca';
|
|
44
|
+
/**
|
|
45
|
+
* RFC 9728 §3: the Protected Resource Metadata is published at the well-known URI formed by inserting
|
|
46
|
+
* `/.well-known/oauth-protected-resource` into the resource identifier's path. The MCP 2026-07-28 client
|
|
47
|
+
* fetches it after a 401 to discover this RS's authorization servers and scopes.
|
|
48
|
+
*/
|
|
49
|
+
export declare const WELL_KNOWN_OAUTH_PRM = "/.well-known/oauth-protected-resource";
|
|
50
|
+
/** RFC 6750 §2 token-presentation methods an RFC 9728 `bearer_methods_supported` may advertise. */
|
|
51
|
+
export type BearerMethod = 'header' | 'body' | 'query';
|
|
52
|
+
/**
|
|
53
|
+
* The PCA extension block embedded in the PRM (RFC 9728 §3.1 permits collision-resistant extra params):
|
|
54
|
+
* a generic OAuth/MCP client ignores it, while a PCA-aware client learns this RS verifies a PCActn, which
|
|
55
|
+
* header it rides, and where the sibling `.well-known/pca-configuration` lives — one-fetch auto-config.
|
|
56
|
+
*/
|
|
57
|
+
export interface PcaResourceExtension {
|
|
58
|
+
/** The HTTP header the PCActn is read from (base64url value). Mirrors the discovery doc `action_header`. */
|
|
59
|
+
pca_action_header: string;
|
|
60
|
+
/** Accepted PCActn signature suites (e.g. ["ed25519","hybrid-ed25519-ml-dsa-65"]). */
|
|
61
|
+
pca_signature_suites: string[];
|
|
62
|
+
/** PCActn wire version(s) this RS accepts. */
|
|
63
|
+
pca_versions: number[];
|
|
64
|
+
/** Absolute URL of this RS's `.well-known/pca-configuration`. */
|
|
65
|
+
pca_configuration_endpoint: string;
|
|
66
|
+
/** The embedded PCA discovery document (from `buildDiscoveryDocument`), for one-fetch auto-config. */
|
|
67
|
+
pca: PcaDiscoveryDocument;
|
|
68
|
+
}
|
|
69
|
+
/** RFC 9728 §3 Protected Resource Metadata, as required by MCP authorization 2026-07-28, + the PCA block. */
|
|
70
|
+
export interface ProtectedResourceMetadata extends PcaResourceExtension {
|
|
71
|
+
/**
|
|
72
|
+
* RFC 9728 §3.1 `resource` (REQUIRED): this protected resource's identifier. For a PCA RS this IS the
|
|
73
|
+
* value a PCActn's signed `aud` must equal (RFC 8707), so the OAuth advertisement and the cryptographic
|
|
74
|
+
* audience are one string and cannot drift.
|
|
75
|
+
*/
|
|
76
|
+
resource: string;
|
|
77
|
+
/**
|
|
78
|
+
* RFC 9728 §3.1 `authorization_servers`: the issuer identifiers of the ASs that may issue credentials
|
|
79
|
+
* for this RS. This is the SAME allowlist `authorizeRequest` enforces as the RFC 9207 mix-up defense —
|
|
80
|
+
* discovery advertises exactly what admission enforces.
|
|
81
|
+
*/
|
|
82
|
+
authorization_servers: string[];
|
|
83
|
+
/** RFC 9728 §3.1 `bearer_methods_supported`: how a bearer credential may be presented. */
|
|
84
|
+
bearer_methods_supported: BearerMethod[];
|
|
85
|
+
/** RFC 9728 §3.1 `scopes_supported`: the full scope vocabulary this RS recognizes. */
|
|
86
|
+
scopes_supported: string[];
|
|
87
|
+
/** RFC 9728 §3.1 `resource_name` (OPTIONAL). */
|
|
88
|
+
resource_name?: string;
|
|
89
|
+
/** RFC 9728 §3.1 `resource_documentation` (OPTIONAL). */
|
|
90
|
+
resource_documentation?: string;
|
|
91
|
+
/** RFC 9728 §3.1 `resource_signing_alg_values_supported` (OPTIONAL). */
|
|
92
|
+
resource_signing_alg_values_supported?: string[];
|
|
93
|
+
/**
|
|
94
|
+
* CIMD advertisement (MCP 2026-07-28): this RS/AS accepts Client-ID Metadata Documents in place of
|
|
95
|
+
* Dynamic Client Registration. Always `true` for a 2026-07-28 PCA RS.
|
|
96
|
+
*/
|
|
97
|
+
client_id_metadata_document_supported: true;
|
|
98
|
+
}
|
|
99
|
+
export interface ProtectedResourceMetadataOptions {
|
|
100
|
+
/** REQUIRED: the resource identifier — the SAME string a PCActn's signed `aud` must equal. */
|
|
101
|
+
resource: string;
|
|
102
|
+
/** REQUIRED (RFC 9207 anchor): issuer identifiers of the ASs this RS trusts. */
|
|
103
|
+
authorizationServers: string[];
|
|
104
|
+
/** REQUIRED: the full scope vocabulary this RS recognizes. */
|
|
105
|
+
scopesSupported: string[];
|
|
106
|
+
/** Token-presentation methods. Default `['header']` (a PCActn rides the `PCA-Action` header). */
|
|
107
|
+
bearerMethodsSupported?: BearerMethod[];
|
|
108
|
+
resourceName?: string;
|
|
109
|
+
resourceDocumentation?: string;
|
|
110
|
+
resourceSigningAlgValuesSupported?: string[];
|
|
111
|
+
/** Absolute URL of this RS's `.well-known/pca-configuration` (defaults to `<resource>/.well-known/pca-configuration`). */
|
|
112
|
+
pcaConfigurationEndpoint?: string;
|
|
113
|
+
signatureSuites?: string[];
|
|
114
|
+
pcaVersions?: number[];
|
|
115
|
+
actionHeader?: string;
|
|
116
|
+
requiredChecks?: string[];
|
|
117
|
+
endpoints?: PcaDiscoveryDocument['endpoints'];
|
|
118
|
+
trustedRoots?: string[];
|
|
119
|
+
metadata?: Record<string, unknown>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* RFC 9728 — build the Protected Resource Metadata served at {@link WELL_KNOWN_OAUTH_PRM}. The `resource`
|
|
123
|
+
* is this RS's identifier (= the PCActn audience), `authorization_servers` is the RFC 9207 trusted-issuer
|
|
124
|
+
* allowlist, and `scopes_supported` is the scope vocabulary. The embedded PCA discovery doc + the
|
|
125
|
+
* `.well-known/pca-configuration` pointer auto-configure a PCA-aware client. Throws on an empty `resource`
|
|
126
|
+
* or an absent/empty `authorization_servers`.
|
|
127
|
+
*/
|
|
128
|
+
export declare function protectedResourceMetadata(opts: ProtectedResourceMetadataOptions): ProtectedResourceMetadata;
|
|
129
|
+
/** Framework-agnostic glue — the body + path to serve the RFC 9728 PRM document. */
|
|
130
|
+
export declare function prmHandler(opts: ProtectedResourceMetadataOptions): {
|
|
131
|
+
path: string;
|
|
132
|
+
body: ProtectedResourceMetadata;
|
|
133
|
+
};
|
|
134
|
+
export interface WwwAuthenticateChallengeOptions {
|
|
135
|
+
/** REQUIRED: the absolute URL where {@link protectedResourceMetadata} is served (RFC 9728 pointer). */
|
|
136
|
+
resourceMetadataUrl: string;
|
|
137
|
+
/** RFC 6750 §3 `error` (e.g. "invalid_token", "insufficient_scope", "insufficient_user_authentication"). */
|
|
138
|
+
error?: string;
|
|
139
|
+
/** RFC 6750 §3 `error_description`. */
|
|
140
|
+
errorDescription?: string;
|
|
141
|
+
/** RFC 6750 §3 `scope` the client would need (used with `insufficient_scope`). */
|
|
142
|
+
scope?: string;
|
|
143
|
+
/** RFC 9470 `acr_values` — the authentication-context the client must reach (step-up). */
|
|
144
|
+
acrValues?: string;
|
|
145
|
+
/** RFC 9470 `max_age` — freshness the client must satisfy (step-up). */
|
|
146
|
+
maxAge?: number;
|
|
147
|
+
/** Bearer realm (optional). */
|
|
148
|
+
realm?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Append a PCA step-up challenge as a second RFC-7235 challenge so a PCA-aware client gets the FROST/CIBA
|
|
151
|
+
* requirement in the same header. Ignored unless set. The structured {@link StepUpChallenge} carries the
|
|
152
|
+
* same information for the MCP client to drive.
|
|
153
|
+
*/
|
|
154
|
+
pcaStepUp?: StepUpChallenge;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* MCP 2026-07-28 + RFC 6750 — build the `WWW-Authenticate` value a PCA resource server returns on a 401.
|
|
158
|
+
* The MCP spec requires `Bearer resource_metadata="<PRM URL>"` pointing at the RFC 9728 document so a
|
|
159
|
+
* generic MCP/OAuth client can discover the RS. `error`/`error_description`/`scope` follow RFC 6750;
|
|
160
|
+
* `acr_values`/`max_age` follow RFC 9470 for a step-up challenge. When `pcaStepUp` is given, a second
|
|
161
|
+
* `PCA-StepUp` challenge is appended (RFC 7235 allows multiple comma-separated challenges) carrying the
|
|
162
|
+
* tier, required co-signer roles and the FROST/CIBA step-up endpoint. Throws on an empty metadata URL or
|
|
163
|
+
* any header-injecting parameter value.
|
|
164
|
+
*/
|
|
165
|
+
export declare function wwwAuthenticateChallenge(opts: WwwAuthenticateChallengeOptions): string;
|
|
166
|
+
/**
|
|
167
|
+
* A Client-ID Metadata Document (MCP 2026-07-28). The client's `client_id` IS an https URL that
|
|
168
|
+
* dereferences to this document; its fields mirror the OAuth 2.0 Dynamic Client Registration metadata
|
|
169
|
+
* (RFC 7591). Because the document lives at the `client_id` URL, a resource/authorization server learns
|
|
170
|
+
* the client's redirect URIs and metadata WITHOUT an out-of-band registration — DCR is not needed.
|
|
171
|
+
*/
|
|
172
|
+
export interface ClientIdMetadataDocument {
|
|
173
|
+
/** MUST be the https URL this document is served from (self-referential — the CIMD integrity rule). */
|
|
174
|
+
client_id: string;
|
|
175
|
+
/** REQUIRED: the client's redirect URIs. Must be non-empty and each a valid, safe redirect URI. */
|
|
176
|
+
redirect_uris: string[];
|
|
177
|
+
client_name?: string;
|
|
178
|
+
client_uri?: string;
|
|
179
|
+
logo_uri?: string;
|
|
180
|
+
scope?: string;
|
|
181
|
+
contacts?: string[];
|
|
182
|
+
tos_uri?: string;
|
|
183
|
+
policy_uri?: string;
|
|
184
|
+
grant_types?: string[];
|
|
185
|
+
response_types?: string[];
|
|
186
|
+
token_endpoint_auth_method?: string;
|
|
187
|
+
}
|
|
188
|
+
export interface ClientIdMetadataDocumentOptions {
|
|
189
|
+
/** The https URL that is the `client_id` (and where this document is published). */
|
|
190
|
+
clientId: string;
|
|
191
|
+
/** The client's redirect URIs (non-empty). */
|
|
192
|
+
redirectUris: string[];
|
|
193
|
+
clientName?: string;
|
|
194
|
+
clientUri?: string;
|
|
195
|
+
logoUri?: string;
|
|
196
|
+
scope?: string;
|
|
197
|
+
contacts?: string[];
|
|
198
|
+
tosUri?: string;
|
|
199
|
+
policyUri?: string;
|
|
200
|
+
grantTypes?: string[];
|
|
201
|
+
responseTypes?: string[];
|
|
202
|
+
tokenEndpointAuthMethod?: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Build a well-formed Client-ID Metadata Document a client can publish at its `client_id` URL. Validates
|
|
206
|
+
* the `client_id` is https and every redirect URI is safe; throws otherwise (fail closed at construction).
|
|
207
|
+
*/
|
|
208
|
+
export declare function clientIdMetadataDocument(opts: ClientIdMetadataDocumentOptions): ClientIdMetadataDocument;
|
|
209
|
+
export type VerifyCimdResult = {
|
|
210
|
+
ok: true;
|
|
211
|
+
clientId: string;
|
|
212
|
+
doc?: ClientIdMetadataDocument;
|
|
213
|
+
} | {
|
|
214
|
+
ok: false;
|
|
215
|
+
reason: string;
|
|
216
|
+
};
|
|
217
|
+
export interface VerifyCimdOptions {
|
|
218
|
+
/**
|
|
219
|
+
* The `client_id` URL the document was dereferenced from. When set, the document's own `client_id` MUST
|
|
220
|
+
* equal it (the CIMD self-reference / integrity rule): a document that claims a different id is rejected.
|
|
221
|
+
*/
|
|
222
|
+
expectedClientId?: string;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* CIMD validation (MCP 2026-07-28), the DCR replacement. Two input forms:
|
|
226
|
+
*
|
|
227
|
+
* - a STRING: the `client_id` URL itself. Validates it is a well-formed https URL (http / malformed →
|
|
228
|
+
* rejected). The document behind it must still be fetched and validated separately.
|
|
229
|
+
* - an OBJECT: a dereferenced {@link ClientIdMetadataDocument}. Validates `client_id` is https, matches
|
|
230
|
+
* `expectedClientId` when supplied, and `redirect_uris` is a non-empty array of safe redirect URIs.
|
|
231
|
+
*
|
|
232
|
+
* Fails closed: anything missing / http / malformed → `{ ok: false, reason }`. The object form accepts an
|
|
233
|
+
* untrusted `Record<string, unknown>` (e.g. freshly parsed JSON) as well as a typed document.
|
|
234
|
+
*/
|
|
235
|
+
export declare function verifyCimd(input: string | ClientIdMetadataDocument | Record<string, unknown>, opts?: VerifyCimdOptions): VerifyCimdResult;
|
|
236
|
+
/** The scope caveat convention: a capability hop may carry `{ type: 'scope', scopes: [...] }`. */
|
|
237
|
+
export declare const SCOPE_CAVEAT = "scope";
|
|
238
|
+
export interface ScopeCaveat extends Caveat {
|
|
239
|
+
type: typeof SCOPE_CAVEAT;
|
|
240
|
+
scopes: string[];
|
|
241
|
+
}
|
|
242
|
+
export declare function isScopeCaveat(cv: unknown): cv is ScopeCaveat;
|
|
243
|
+
/**
|
|
244
|
+
* Scope accumulation: the EFFECTIVE granted scopes NARROW monotonically down the chain. Because the
|
|
245
|
+
* capability chain's caveats are append-only and conjunctive, each `scope` caveat INTERSECTS the running
|
|
246
|
+
* set — a child can grant a subset of its parent's scopes but never widen. The leaf holds the full
|
|
247
|
+
* append-only caveat list, so scanning it covers every hop.
|
|
248
|
+
*
|
|
249
|
+
* - `base` given → start from it and intersect each `scope` caveat (a delegation can only shrink it).
|
|
250
|
+
* - `base` absent → the FIRST `scope` caveat seeds the set; later ones intersect it.
|
|
251
|
+
* - no `base` and NO `scope` caveat → `[]` (fail closed: nothing is granted to a scoped tool).
|
|
252
|
+
*
|
|
253
|
+
* Returns a sorted, de-duplicated array.
|
|
254
|
+
*/
|
|
255
|
+
export declare function accumulateScopes(caveats: Caveat[], base?: string[]): string[];
|
|
256
|
+
/**
|
|
257
|
+
* The structured step-up requirement an MCP client drives. A tool that needs escalation makes the RS
|
|
258
|
+
* return a 401 carrying this: the client collects the named co-signer shares via PCA's FROST (threshold)
|
|
259
|
+
* round or a CIBA push, then re-presents a PCActn carrying the `threshold`. Tier 2 = a guardian co-sign,
|
|
260
|
+
* tier 3 = a principal (human) co-sign.
|
|
261
|
+
*/
|
|
262
|
+
export interface StepUpChallenge {
|
|
263
|
+
/** The threshold tier the re-presented PCActn must satisfy (2 guardian / 3 principal). */
|
|
264
|
+
tier: 2 | 3;
|
|
265
|
+
/** The signer roles whose FROST/CIBA shares satisfy the tier. */
|
|
266
|
+
requiredRoles: SignerRole[];
|
|
267
|
+
/** The PCA step-up endpoint the MCP client drives (FROST round / CIBA push), if advertised. */
|
|
268
|
+
stepUpEndpoint?: string;
|
|
269
|
+
/** Human-readable reason. */
|
|
270
|
+
reason: string;
|
|
271
|
+
/** The RFC-9470 `acr`-like value echoed in the Bearer challenge (e.g. "pca:tier2"). */
|
|
272
|
+
acr: string;
|
|
273
|
+
}
|
|
274
|
+
/** The roles that satisfy a tier: tier 3 needs a principal; tier 2 a guardian OR a principal. */
|
|
275
|
+
export declare function requiredRolesForTier(tier: 2 | 3): SignerRole[];
|
|
276
|
+
/**
|
|
277
|
+
* Default step-up satisfaction: the PCActn already carries a `threshold` share from a role that meets the
|
|
278
|
+
* tier. This is the ADMISSION-layer presence check (does the proof claim the co-sign?); the cryptographic
|
|
279
|
+
* verification of each share against the signer set is the threshold verifier's / adjudicator's job — the
|
|
280
|
+
* PCA core reports `threshold` as 'not-enforced' without a hook, and this mirrors that honesty. Supply
|
|
281
|
+
* `AuthorizeRequestOptions.stepUpSatisfied` to plug a real verifier in.
|
|
282
|
+
*/
|
|
283
|
+
export declare function defaultStepUpSatisfied(p: PCActn, tier: 2 | 3): boolean;
|
|
284
|
+
/** Framework-agnostic request shape: headers (record or Headers-like) + an optional parsed/raw body. */
|
|
285
|
+
export interface McpRequestLike {
|
|
286
|
+
headers: Record<string, string | string[] | undefined> | {
|
|
287
|
+
get(name: string): string | null;
|
|
288
|
+
};
|
|
289
|
+
body?: unknown;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Extract a PCActn from a request: the `PCA-Action` header (base64url-encoded PCActn JSON), else a JSON
|
|
293
|
+
* body `{ pcactn }` (string or object). Returns the decoded `PCActn`, or `null` when none is present.
|
|
294
|
+
* Throws only on an unreadable / undecodable encoding (the caller maps that to a 401).
|
|
295
|
+
*/
|
|
296
|
+
export declare function extractPCActn(req: McpRequestLike): PCActn | null;
|
|
297
|
+
export interface AuthorizeRequestOptions {
|
|
298
|
+
/** REQUIRED (RFC 8707): this RS's id. A PCActn whose signed `aud` differs is rejected (no cross-RS replay). */
|
|
299
|
+
resource: string;
|
|
300
|
+
/**
|
|
301
|
+
* REQUIRED (RFC 9207 mix-up defense): the trusted authorization-server / issuer allowlist. The resolved
|
|
302
|
+
* grant's issuer (see `resolveIssuer`, default the grant's root issuer) MUST be a member, else the
|
|
303
|
+
* credential is rejected as coming from an unexpected AS. Same value as the PRM `authorization_servers`.
|
|
304
|
+
*/
|
|
305
|
+
authorizationServers: string[];
|
|
306
|
+
/** Resolve the Root Intent Grant from the PCActn's `grant_ref`. `null` = unknown grant (→ 401). */
|
|
307
|
+
resolveGrant: (grantRef: string, pcactn: PCActn) => Capability | null | Promise<Capability | null>;
|
|
308
|
+
/** The absolute PRM URL for the `WWW-Authenticate` challenge. */
|
|
309
|
+
prmUrl: string;
|
|
310
|
+
/** The MCP tool being invoked (used to look up required scope + step-up tier). */
|
|
311
|
+
tool?: string;
|
|
312
|
+
/** Tool → required scope(s) map. The invoked tool's scopes must all be in the effective granted set. */
|
|
313
|
+
toolScopes?: Record<string, string | string[]>;
|
|
314
|
+
/** Explicit required scopes for this call (overrides `toolScopes[tool]`). */
|
|
315
|
+
requiredScopes?: string[];
|
|
316
|
+
/** Base granted scopes when the grant does not express them as `scope` caveats. Intersected with caveats. */
|
|
317
|
+
baseScopes?: string[];
|
|
318
|
+
/** Fully override how the effective granted scopes are computed (else `accumulateScopes(leaf.caveats, baseScopes)`). */
|
|
319
|
+
resolveGrantedScopes?: (grant: Capability, chain: CapabilityChain, pcactn: PCActn) => string[];
|
|
320
|
+
/** Tool → minimum threshold tier (2 guardian / 3 principal). A tool needing escalation returns a step-up 401. */
|
|
321
|
+
toolStepUp?: Record<string, 2 | 3>;
|
|
322
|
+
/** Explicit minimum tier for this call (overrides `toolStepUp[tool]`). */
|
|
323
|
+
requiredTier?: 2 | 3;
|
|
324
|
+
/**
|
|
325
|
+
* Derive the required tier from the PCActn's own risk claim via the risk policy (RFC-9470 step-up by
|
|
326
|
+
* risk). When set and no explicit tier applies, `requiredThreshold(pcactn.risk_claim.r, riskTiers).t` is
|
|
327
|
+
* used. A caller passes the grant's `risk_policy` θ thresholds here.
|
|
328
|
+
*/
|
|
329
|
+
riskTiers?: {
|
|
330
|
+
theta1: number;
|
|
331
|
+
theta2: number;
|
|
332
|
+
};
|
|
333
|
+
/** The FROST/CIBA step-up endpoint advertised in the step-up challenge. */
|
|
334
|
+
stepUpEndpoint?: string;
|
|
335
|
+
/** Resolve the credential's issuer for RFC 9207 (default: the grant's root `issuer`). */
|
|
336
|
+
resolveIssuer?: (grant: Capability, pcactn: PCActn) => string;
|
|
337
|
+
/** Plug a real step-up verifier (default {@link defaultStepUpSatisfied} — a presence check). */
|
|
338
|
+
stepUpSatisfied?: (pcactn: PCActn, tier: 2 | 3) => boolean;
|
|
339
|
+
/** Verifier clock (epoch ms). Default `Date.now()`. */
|
|
340
|
+
now?: number;
|
|
341
|
+
/** Bearer realm on the challenge (default "pca"). */
|
|
342
|
+
realm?: string;
|
|
343
|
+
}
|
|
344
|
+
export interface AuthorizeAllow {
|
|
345
|
+
ok: true;
|
|
346
|
+
status: 200;
|
|
347
|
+
pcactn: PCActn;
|
|
348
|
+
grant: Capability;
|
|
349
|
+
/** The effective (narrowed) granted scopes for this credential. */
|
|
350
|
+
grantedScopes: string[];
|
|
351
|
+
/** The PCA core verdict's per-check status. */
|
|
352
|
+
checks: Record<string, CheckStatus>;
|
|
353
|
+
}
|
|
354
|
+
export interface AuthorizeDeny {
|
|
355
|
+
ok: false;
|
|
356
|
+
status: 401 | 403;
|
|
357
|
+
error: string;
|
|
358
|
+
errorDescription: string;
|
|
359
|
+
/** The ready-to-send `WWW-Authenticate` header value. */
|
|
360
|
+
wwwAuthenticate: string;
|
|
361
|
+
/** Present when the denial is a step-up requirement the MCP client must drive. */
|
|
362
|
+
stepUp?: StepUpChallenge;
|
|
363
|
+
/** The PCA core verdict's per-check status, when verification ran. */
|
|
364
|
+
checks?: Record<string, CheckStatus>;
|
|
365
|
+
}
|
|
366
|
+
export type AuthorizeResult = AuthorizeAllow | AuthorizeDeny;
|
|
367
|
+
/**
|
|
368
|
+
* The resource-server admission check for an MCP 2026-07-28 tool call, with a PCActn as the authority.
|
|
369
|
+
* It FAILS CLOSED and runs the checks in this order, each with the correct 401/403 + `WWW-Authenticate`:
|
|
370
|
+
*
|
|
371
|
+
* 1. extract the PCActn (missing / unreadable → 401 `invalid_request`);
|
|
372
|
+
* 2. resolve the grant from `grant_ref` (unknown → 401 `invalid_token`);
|
|
373
|
+
* 3. RFC 8707 — the PCActn's signed `aud` MUST equal this RS's `resource` (mismatch → 401 `invalid_token`);
|
|
374
|
+
* 4. RFC 9207 — the credential's issuer MUST be in `authorizationServers` (mix-up → 401 `invalid_token`);
|
|
375
|
+
* 5. the PCA core verification (`verifyPCActnCore`: wire, version, audience, validity, cap_chain,
|
|
376
|
+
* plan inclusion, leaf signature, counter). A crypto/binding failure → 401; a policy failure → 403;
|
|
377
|
+
* 6. scope accumulation — the tool's required scope(s) MUST be in the effective (narrowed) granted set
|
|
378
|
+
* (insufficient → 403 `insufficient_scope`, with `scope` in the challenge);
|
|
379
|
+
* 7. step-up — if the tool needs a tier ≥ 2 co-sign the PCActn does not yet carry, return a 401
|
|
380
|
+
* `insufficient_user_authentication` (RFC 9470) carrying the {@link StepUpChallenge}.
|
|
381
|
+
*
|
|
382
|
+
* On success it returns the verified PCActn, the grant, the effective granted scopes and the core checks.
|
|
383
|
+
*/
|
|
384
|
+
export declare function authorizeRequest(req: McpRequestLike, opts: AuthorizeRequestOptions): Promise<AuthorizeResult>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP authorization RESOURCE SERVER for the 2026-07-28 revision, backed by Proof-Carrying Authority.
|
|
4
|
+
*
|
|
5
|
+
* This is the RS SIDE of the de-facto agent↔tool wire. Where `@atlasauth/pca-mcp` wraps a tool handler on
|
|
6
|
+
* the CLIENT so each call carries a PCActn, THIS package makes the server that RECEIVES those calls a
|
|
7
|
+
* correct OAuth-2.1 resource server per the MCP authorization revision of 2026-07-28 — the revision that
|
|
8
|
+
*
|
|
9
|
+
* • keeps RFC 9728 Protected Resource Metadata discovery and the `WWW-Authenticate: Bearer
|
|
10
|
+
* resource_metadata="…"` 401 challenge (unchanged since 2025-11-25),
|
|
11
|
+
* • REPLACES Dynamic Client Registration with Client-ID Metadata Documents (CIMD): a client's
|
|
12
|
+
* `client_id` IS an https URL that dereferences to its own RFC-7591-shaped metadata document, so a
|
|
13
|
+
* resource/authorization server never has to register a client out of band,
|
|
14
|
+
* • REQUIRES RFC 8707 resource-indicator enforcement (a credential is bound to exactly one resource
|
|
15
|
+
* server, so a token minted for RS-A cannot be replayed to RS-B), and
|
|
16
|
+
* • REQUIRES RFC 9207 `iss` authorization-server validation (a token whose issuer is not an expected
|
|
17
|
+
* AS is rejected — the authorization-server mix-up defense),
|
|
18
|
+
* • layers OAuth step-up (RFC 9470, `insufficient_user_authentication`) for actions that need a human.
|
|
19
|
+
*
|
|
20
|
+
* The AUTHORITY here is the PCActn, not a bearer token: a PCActn's SIGNED `aud` is the RFC 8707 resource
|
|
21
|
+
* binding (it cannot be spoofed the way a `resource` indicator can), its capability chain roots at a
|
|
22
|
+
* pinned issuer (the RFC 9207 anchor), and its signed caveats carry the scope grant that NARROWS down the
|
|
23
|
+
* delegation chain. `authorizeRequest` is the admission check that verifies all of this through the PCA
|
|
24
|
+
* core (`verifyPCActnCore`) and FAILS CLOSED: any missing / mismatched / unverifiable element denies with
|
|
25
|
+
* the correct 401/403 + `WWW-Authenticate` challenge. Step-up is wired to PCA's FROST/CIBA threshold
|
|
26
|
+
* surface: a tool that needs escalation returns a 401 the MCP client drives to collect a guardian /
|
|
27
|
+
* principal co-sign, then re-presents a PCActn carrying the threshold.
|
|
28
|
+
*
|
|
29
|
+
* Specs implemented (cited per export):
|
|
30
|
+
* - RFC 9728 — OAuth 2.0 Protected Resource Metadata (https://www.rfc-editor.org/rfc/rfc9728)
|
|
31
|
+
* - RFC 8707 — Resource Indicators for OAuth 2.0 (https://www.rfc-editor.org/rfc/rfc8707)
|
|
32
|
+
* - RFC 9207 — OAuth 2.0 Authorization Server Issuer Id (https://www.rfc-editor.org/rfc/rfc9207)
|
|
33
|
+
* - RFC 6750 — OAuth 2.0 Bearer Token Usage (the challenge shape)
|
|
34
|
+
* - RFC 7235 — HTTP/1.1 Authentication (multiple comma-separated challenges in one header)
|
|
35
|
+
* - RFC 9470 — OAuth 2.0 Step Up Authentication Challenge (https://www.rfc-editor.org/rfc/rfc9470)
|
|
36
|
+
* - Client-ID Metadata Documents (CIMD) — the DCR replacement adopted by MCP authorization 2026-07-28.
|
|
37
|
+
* - MCP Authorization, protocol revision 2026-07-28.
|
|
38
|
+
*
|
|
39
|
+
* Pure: every export is I/O-free and runtime-neutral (no Node builtins, no framework) so the same code
|
|
40
|
+
* mounts on Express / Fastify / Hono / Next / Fetch or any router. The PRM + CIMD documents are PUBLIC,
|
|
41
|
+
* non-secret configuration. This package does NOT import or modify `@atlasauth/pca-oauth` or
|
|
42
|
+
* `@atlasauth/pca-mcp`; it mirrors their conventions and verifies through the shared `@atlasauth/pca` core.
|
|
43
|
+
*/
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.SCOPE_CAVEAT = exports.WELL_KNOWN_OAUTH_PRM = void 0;
|
|
46
|
+
exports.protectedResourceMetadata = protectedResourceMetadata;
|
|
47
|
+
exports.prmHandler = prmHandler;
|
|
48
|
+
exports.wwwAuthenticateChallenge = wwwAuthenticateChallenge;
|
|
49
|
+
exports.clientIdMetadataDocument = clientIdMetadataDocument;
|
|
50
|
+
exports.verifyCimd = verifyCimd;
|
|
51
|
+
exports.isScopeCaveat = isScopeCaveat;
|
|
52
|
+
exports.accumulateScopes = accumulateScopes;
|
|
53
|
+
exports.requiredRolesForTier = requiredRolesForTier;
|
|
54
|
+
exports.defaultStepUpSatisfied = defaultStepUpSatisfied;
|
|
55
|
+
exports.extractPCActn = extractPCActn;
|
|
56
|
+
exports.authorizeRequest = authorizeRequest;
|
|
57
|
+
const pca_1 = require("@atlasauth/pca");
|
|
58
|
+
/**
|
|
59
|
+
* RFC 9728 §3: the Protected Resource Metadata is published at the well-known URI formed by inserting
|
|
60
|
+
* `/.well-known/oauth-protected-resource` into the resource identifier's path. The MCP 2026-07-28 client
|
|
61
|
+
* fetches it after a 401 to discover this RS's authorization servers and scopes.
|
|
62
|
+
*/
|
|
63
|
+
exports.WELL_KNOWN_OAUTH_PRM = '/.well-known/oauth-protected-resource';
|
|
64
|
+
// ---------------------------------------------------------------------------------------------------
|
|
65
|
+
// Header-value hygiene (RFC 7235 quoted auth-params): reject control / quote chars to stop injection.
|
|
66
|
+
// ---------------------------------------------------------------------------------------------------
|
|
67
|
+
function quotedParam(value) {
|
|
68
|
+
if (/["\r\n]/.test(value)) {
|
|
69
|
+
throw new Error('pca-mcp-rs: a WWW-Authenticate parameter value may not contain a quote or newline');
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* RFC 9728 — build the Protected Resource Metadata served at {@link WELL_KNOWN_OAUTH_PRM}. The `resource`
|
|
75
|
+
* is this RS's identifier (= the PCActn audience), `authorization_servers` is the RFC 9207 trusted-issuer
|
|
76
|
+
* allowlist, and `scopes_supported` is the scope vocabulary. The embedded PCA discovery doc + the
|
|
77
|
+
* `.well-known/pca-configuration` pointer auto-configure a PCA-aware client. Throws on an empty `resource`
|
|
78
|
+
* or an absent/empty `authorization_servers`.
|
|
79
|
+
*/
|
|
80
|
+
function protectedResourceMetadata(opts) {
|
|
81
|
+
if (typeof opts.resource !== 'string' || opts.resource.length === 0) {
|
|
82
|
+
throw new Error('protectedResourceMetadata: `resource` (the RS id = PCActn audience) is required');
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(opts.authorizationServers) || opts.authorizationServers.length === 0) {
|
|
85
|
+
throw new Error('protectedResourceMetadata: `authorizationServers` is required (RFC 9207 mix-up defense anchor)');
|
|
86
|
+
}
|
|
87
|
+
if (!Array.isArray(opts.scopesSupported)) {
|
|
88
|
+
throw new Error('protectedResourceMetadata: `scopesSupported` must be an array');
|
|
89
|
+
}
|
|
90
|
+
// One source of truth: the discovery doc's `audience` IS the OAuth `resource`.
|
|
91
|
+
const discovery = (0, pca_1.buildDiscoveryDocument)({
|
|
92
|
+
audience: opts.resource,
|
|
93
|
+
...(opts.signatureSuites ? { signatureSuites: opts.signatureSuites } : {}),
|
|
94
|
+
...(opts.pcaVersions ? { pcaVersions: opts.pcaVersions } : {}),
|
|
95
|
+
...(opts.actionHeader ? { actionHeader: opts.actionHeader } : {}),
|
|
96
|
+
...(opts.requiredChecks ? { requiredChecks: opts.requiredChecks } : {}),
|
|
97
|
+
...(opts.endpoints ? { endpoints: opts.endpoints } : {}),
|
|
98
|
+
...(opts.trustedRoots ? { trustedRoots: opts.trustedRoots } : {}),
|
|
99
|
+
...(opts.metadata ? { metadata: opts.metadata } : {}),
|
|
100
|
+
});
|
|
101
|
+
const pcaConfigurationEndpoint = opts.pcaConfigurationEndpoint ?? opts.resource.replace(/\/+$/, '') + pca_1.WELL_KNOWN_PCA_PATH;
|
|
102
|
+
const doc = {
|
|
103
|
+
resource: opts.resource,
|
|
104
|
+
authorization_servers: [...opts.authorizationServers],
|
|
105
|
+
bearer_methods_supported: opts.bearerMethodsSupported ? [...opts.bearerMethodsSupported] : ['header'],
|
|
106
|
+
scopes_supported: [...opts.scopesSupported],
|
|
107
|
+
client_id_metadata_document_supported: true,
|
|
108
|
+
pca_action_header: discovery.action_header,
|
|
109
|
+
pca_signature_suites: [...discovery.signature_suites],
|
|
110
|
+
pca_versions: [...discovery.pca_versions],
|
|
111
|
+
pca_configuration_endpoint: pcaConfigurationEndpoint,
|
|
112
|
+
pca: discovery,
|
|
113
|
+
...(opts.resourceName ? { resource_name: opts.resourceName } : {}),
|
|
114
|
+
...(opts.resourceDocumentation ? { resource_documentation: opts.resourceDocumentation } : {}),
|
|
115
|
+
...(opts.resourceSigningAlgValuesSupported
|
|
116
|
+
? { resource_signing_alg_values_supported: [...opts.resourceSigningAlgValuesSupported] }
|
|
117
|
+
: {}),
|
|
118
|
+
};
|
|
119
|
+
return doc;
|
|
120
|
+
}
|
|
121
|
+
/** Framework-agnostic glue — the body + path to serve the RFC 9728 PRM document. */
|
|
122
|
+
function prmHandler(opts) {
|
|
123
|
+
return { path: exports.WELL_KNOWN_OAUTH_PRM, body: protectedResourceMetadata(opts) };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* MCP 2026-07-28 + RFC 6750 — build the `WWW-Authenticate` value a PCA resource server returns on a 401.
|
|
127
|
+
* The MCP spec requires `Bearer resource_metadata="<PRM URL>"` pointing at the RFC 9728 document so a
|
|
128
|
+
* generic MCP/OAuth client can discover the RS. `error`/`error_description`/`scope` follow RFC 6750;
|
|
129
|
+
* `acr_values`/`max_age` follow RFC 9470 for a step-up challenge. When `pcaStepUp` is given, a second
|
|
130
|
+
* `PCA-StepUp` challenge is appended (RFC 7235 allows multiple comma-separated challenges) carrying the
|
|
131
|
+
* tier, required co-signer roles and the FROST/CIBA step-up endpoint. Throws on an empty metadata URL or
|
|
132
|
+
* any header-injecting parameter value.
|
|
133
|
+
*/
|
|
134
|
+
function wwwAuthenticateChallenge(opts) {
|
|
135
|
+
if (typeof opts.resourceMetadataUrl !== 'string' || opts.resourceMetadataUrl.length === 0) {
|
|
136
|
+
throw new Error('wwwAuthenticateChallenge: resourceMetadataUrl is required');
|
|
137
|
+
}
|
|
138
|
+
const params = [];
|
|
139
|
+
if (opts.realm)
|
|
140
|
+
params.push(`realm="${quotedParam(opts.realm)}"`);
|
|
141
|
+
params.push(`resource_metadata="${quotedParam(opts.resourceMetadataUrl)}"`);
|
|
142
|
+
if (opts.error)
|
|
143
|
+
params.push(`error="${quotedParam(opts.error)}"`);
|
|
144
|
+
if (opts.errorDescription)
|
|
145
|
+
params.push(`error_description="${quotedParam(opts.errorDescription)}"`);
|
|
146
|
+
if (opts.scope)
|
|
147
|
+
params.push(`scope="${quotedParam(opts.scope)}"`);
|
|
148
|
+
if (opts.acrValues)
|
|
149
|
+
params.push(`acr_values="${quotedParam(opts.acrValues)}"`);
|
|
150
|
+
if (typeof opts.maxAge === 'number' && Number.isFinite(opts.maxAge))
|
|
151
|
+
params.push(`max_age=${Math.max(0, Math.trunc(opts.maxAge))}`);
|
|
152
|
+
const challenges = [`Bearer ${params.join(', ')}`];
|
|
153
|
+
if (opts.pcaStepUp) {
|
|
154
|
+
const su = opts.pcaStepUp;
|
|
155
|
+
const suParams = [
|
|
156
|
+
`realm="${quotedParam(opts.realm ?? 'pca')}"`,
|
|
157
|
+
`tier="${quotedParam(String(su.tier))}"`,
|
|
158
|
+
`roles="${quotedParam(su.requiredRoles.join(' '))}"`,
|
|
159
|
+
];
|
|
160
|
+
if (su.stepUpEndpoint)
|
|
161
|
+
suParams.push(`stepup_uri="${quotedParam(su.stepUpEndpoint)}"`);
|
|
162
|
+
if (su.acr)
|
|
163
|
+
suParams.push(`acr="${quotedParam(su.acr)}"`);
|
|
164
|
+
challenges.push(`PCA-StepUp ${suParams.join(', ')}`);
|
|
165
|
+
}
|
|
166
|
+
return challenges.join(', ');
|
|
167
|
+
}
|
|
168
|
+
function isHttpsUrl(s) {
|
|
169
|
+
if (typeof s !== 'string' || s.length === 0)
|
|
170
|
+
return false;
|
|
171
|
+
try {
|
|
172
|
+
return new URL(s).protocol === 'https:';
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Is `uri` a safe OAuth redirect target? https is always allowed; plain http is allowed ONLY for loopback
|
|
180
|
+
* (localhost / 127.0.0.1 / [::1]) per the native-app BCP (RFC 8252); a non-http custom scheme (e.g.
|
|
181
|
+
* `myapp://cb`) is allowed for native apps. Anything else (a non-loopback http URL, an unparseable value)
|
|
182
|
+
* is rejected.
|
|
183
|
+
*/
|
|
184
|
+
function isAllowedRedirectUri(uri) {
|
|
185
|
+
if (typeof uri !== 'string' || uri.length === 0)
|
|
186
|
+
return false;
|
|
187
|
+
let u;
|
|
188
|
+
try {
|
|
189
|
+
u = new URL(uri);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
if (u.protocol === 'https:')
|
|
195
|
+
return true;
|
|
196
|
+
if (u.protocol === 'http:') {
|
|
197
|
+
const h = u.hostname;
|
|
198
|
+
return h === 'localhost' || h === '127.0.0.1' || h === '[::1]' || h === '::1';
|
|
199
|
+
}
|
|
200
|
+
// A non-http(s) custom scheme (native app redirect) — allowed.
|
|
201
|
+
return u.protocol !== 'http:' && u.protocol.length > 0;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Build a well-formed Client-ID Metadata Document a client can publish at its `client_id` URL. Validates
|
|
205
|
+
* the `client_id` is https and every redirect URI is safe; throws otherwise (fail closed at construction).
|
|
206
|
+
*/
|
|
207
|
+
function clientIdMetadataDocument(opts) {
|
|
208
|
+
if (!isHttpsUrl(opts.clientId)) {
|
|
209
|
+
throw new Error('clientIdMetadataDocument: `clientId` must be an https URL (CIMD requires https)');
|
|
210
|
+
}
|
|
211
|
+
if (!Array.isArray(opts.redirectUris) || opts.redirectUris.length === 0) {
|
|
212
|
+
throw new Error('clientIdMetadataDocument: at least one redirect URI is required');
|
|
213
|
+
}
|
|
214
|
+
for (const r of opts.redirectUris) {
|
|
215
|
+
if (!isAllowedRedirectUri(r))
|
|
216
|
+
throw new Error(`clientIdMetadataDocument: unsafe redirect URI ${JSON.stringify(r)}`);
|
|
217
|
+
}
|
|
218
|
+
const doc = {
|
|
219
|
+
client_id: opts.clientId,
|
|
220
|
+
redirect_uris: [...opts.redirectUris],
|
|
221
|
+
...(opts.clientName !== undefined ? { client_name: opts.clientName } : {}),
|
|
222
|
+
...(opts.clientUri !== undefined ? { client_uri: opts.clientUri } : {}),
|
|
223
|
+
...(opts.logoUri !== undefined ? { logo_uri: opts.logoUri } : {}),
|
|
224
|
+
...(opts.scope !== undefined ? { scope: opts.scope } : {}),
|
|
225
|
+
...(opts.contacts !== undefined ? { contacts: [...opts.contacts] } : {}),
|
|
226
|
+
...(opts.tosUri !== undefined ? { tos_uri: opts.tosUri } : {}),
|
|
227
|
+
...(opts.policyUri !== undefined ? { policy_uri: opts.policyUri } : {}),
|
|
228
|
+
...(opts.grantTypes !== undefined ? { grant_types: [...opts.grantTypes] } : {}),
|
|
229
|
+
...(opts.responseTypes !== undefined ? { response_types: [...opts.responseTypes] } : {}),
|
|
230
|
+
...(opts.tokenEndpointAuthMethod !== undefined ? { token_endpoint_auth_method: opts.tokenEndpointAuthMethod } : {}),
|
|
231
|
+
};
|
|
232
|
+
return doc;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* CIMD validation (MCP 2026-07-28), the DCR replacement. Two input forms:
|
|
236
|
+
*
|
|
237
|
+
* - a STRING: the `client_id` URL itself. Validates it is a well-formed https URL (http / malformed →
|
|
238
|
+
* rejected). The document behind it must still be fetched and validated separately.
|
|
239
|
+
* - an OBJECT: a dereferenced {@link ClientIdMetadataDocument}. Validates `client_id` is https, matches
|
|
240
|
+
* `expectedClientId` when supplied, and `redirect_uris` is a non-empty array of safe redirect URIs.
|
|
241
|
+
*
|
|
242
|
+
* Fails closed: anything missing / http / malformed → `{ ok: false, reason }`. The object form accepts an
|
|
243
|
+
* untrusted `Record<string, unknown>` (e.g. freshly parsed JSON) as well as a typed document.
|
|
244
|
+
*/
|
|
245
|
+
function verifyCimd(input, opts = {}) {
|
|
246
|
+
if (typeof input === 'string') {
|
|
247
|
+
if (!isHttpsUrl(input))
|
|
248
|
+
return { ok: false, reason: 'client_id must be an https URL (CIMD requires https; http/malformed rejected)' };
|
|
249
|
+
if (opts.expectedClientId !== undefined && input !== opts.expectedClientId) {
|
|
250
|
+
return { ok: false, reason: 'client_id URL does not match the expected client_id' };
|
|
251
|
+
}
|
|
252
|
+
return { ok: true, clientId: input };
|
|
253
|
+
}
|
|
254
|
+
if (input === null || typeof input !== 'object')
|
|
255
|
+
return { ok: false, reason: 'CIMD must be a string URL or a metadata object' };
|
|
256
|
+
// The declared type is a hint only — the document is UNTRUSTED (often parsed JSON), so every field is
|
|
257
|
+
// re-validated at runtime below; no cast is used to bypass that.
|
|
258
|
+
const v = { ...input };
|
|
259
|
+
if (!isHttpsUrl(v.client_id))
|
|
260
|
+
return { ok: false, reason: 'client_id must be an https URL (CIMD requires https)' };
|
|
261
|
+
if (opts.expectedClientId !== undefined && v.client_id !== opts.expectedClientId) {
|
|
262
|
+
return { ok: false, reason: 'document client_id does not match the URL it was fetched from (CIMD integrity)' };
|
|
263
|
+
}
|
|
264
|
+
const redirectUris = v.redirect_uris;
|
|
265
|
+
if (!Array.isArray(redirectUris) || redirectUris.length === 0) {
|
|
266
|
+
return { ok: false, reason: 'redirect_uris is required and must be a non-empty array' };
|
|
267
|
+
}
|
|
268
|
+
for (const r of redirectUris) {
|
|
269
|
+
if (!isAllowedRedirectUri(r))
|
|
270
|
+
return { ok: false, reason: `unsafe or malformed redirect URI ${JSON.stringify(r)}` };
|
|
271
|
+
}
|
|
272
|
+
// Narrow to the typed document (we validated client_id + redirect_uris above).
|
|
273
|
+
const doc = { client_id: v.client_id, redirect_uris: redirectUris.filter((r) => typeof r === 'string') };
|
|
274
|
+
if (typeof v.client_name === 'string')
|
|
275
|
+
doc.client_name = v.client_name;
|
|
276
|
+
if (typeof v.client_uri === 'string')
|
|
277
|
+
doc.client_uri = v.client_uri;
|
|
278
|
+
if (typeof v.logo_uri === 'string')
|
|
279
|
+
doc.logo_uri = v.logo_uri;
|
|
280
|
+
if (typeof v.scope === 'string')
|
|
281
|
+
doc.scope = v.scope;
|
|
282
|
+
if (Array.isArray(v.contacts) && v.contacts.every((c) => typeof c === 'string'))
|
|
283
|
+
doc.contacts = v.contacts;
|
|
284
|
+
if (typeof v.tos_uri === 'string')
|
|
285
|
+
doc.tos_uri = v.tos_uri;
|
|
286
|
+
if (typeof v.policy_uri === 'string')
|
|
287
|
+
doc.policy_uri = v.policy_uri;
|
|
288
|
+
if (Array.isArray(v.grant_types) && v.grant_types.every((g) => typeof g === 'string'))
|
|
289
|
+
doc.grant_types = v.grant_types;
|
|
290
|
+
if (Array.isArray(v.response_types) && v.response_types.every((g) => typeof g === 'string'))
|
|
291
|
+
doc.response_types = v.response_types;
|
|
292
|
+
if (typeof v.token_endpoint_auth_method === 'string')
|
|
293
|
+
doc.token_endpoint_auth_method = v.token_endpoint_auth_method;
|
|
294
|
+
return { ok: true, clientId: v.client_id, doc };
|
|
295
|
+
}
|
|
296
|
+
// ===================================================================================================
|
|
297
|
+
// 4. Scope accumulation — scopes NARROW down the delegation chain
|
|
298
|
+
// ===================================================================================================
|
|
299
|
+
/** The scope caveat convention: a capability hop may carry `{ type: 'scope', scopes: [...] }`. */
|
|
300
|
+
exports.SCOPE_CAVEAT = 'scope';
|
|
301
|
+
function isScopeCaveat(cv) {
|
|
302
|
+
return (cv !== null &&
|
|
303
|
+
typeof cv === 'object' &&
|
|
304
|
+
cv.type === exports.SCOPE_CAVEAT &&
|
|
305
|
+
Array.isArray(cv.scopes) &&
|
|
306
|
+
cv.scopes.every((s) => typeof s === 'string'));
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Scope accumulation: the EFFECTIVE granted scopes NARROW monotonically down the chain. Because the
|
|
310
|
+
* capability chain's caveats are append-only and conjunctive, each `scope` caveat INTERSECTS the running
|
|
311
|
+
* set — a child can grant a subset of its parent's scopes but never widen. The leaf holds the full
|
|
312
|
+
* append-only caveat list, so scanning it covers every hop.
|
|
313
|
+
*
|
|
314
|
+
* - `base` given → start from it and intersect each `scope` caveat (a delegation can only shrink it).
|
|
315
|
+
* - `base` absent → the FIRST `scope` caveat seeds the set; later ones intersect it.
|
|
316
|
+
* - no `base` and NO `scope` caveat → `[]` (fail closed: nothing is granted to a scoped tool).
|
|
317
|
+
*
|
|
318
|
+
* Returns a sorted, de-duplicated array.
|
|
319
|
+
*/
|
|
320
|
+
function accumulateScopes(caveats, base) {
|
|
321
|
+
let set = base ? new Set(base) : null;
|
|
322
|
+
if (Array.isArray(caveats)) {
|
|
323
|
+
for (const cv of caveats) {
|
|
324
|
+
if (!isScopeCaveat(cv))
|
|
325
|
+
continue;
|
|
326
|
+
const next = new Set(cv.scopes);
|
|
327
|
+
set = set === null ? next : new Set([...set].filter((s) => next.has(s)));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (set === null)
|
|
331
|
+
return base ? [...new Set(base)].sort() : [];
|
|
332
|
+
return [...set].sort();
|
|
333
|
+
}
|
|
334
|
+
/** The roles that satisfy a tier: tier 3 needs a principal; tier 2 a guardian OR a principal. */
|
|
335
|
+
function requiredRolesForTier(tier) {
|
|
336
|
+
return tier === 3 ? ['principal'] : ['guardian', 'principal'];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Default step-up satisfaction: the PCActn already carries a `threshold` share from a role that meets the
|
|
340
|
+
* tier. This is the ADMISSION-layer presence check (does the proof claim the co-sign?); the cryptographic
|
|
341
|
+
* verification of each share against the signer set is the threshold verifier's / adjudicator's job — the
|
|
342
|
+
* PCA core reports `threshold` as 'not-enforced' without a hook, and this mirrors that honesty. Supply
|
|
343
|
+
* `AuthorizeRequestOptions.stepUpSatisfied` to plug a real verifier in.
|
|
344
|
+
*/
|
|
345
|
+
function defaultStepUpSatisfied(p, tier) {
|
|
346
|
+
const shares = p.threshold?.shares;
|
|
347
|
+
if (!Array.isArray(shares))
|
|
348
|
+
return false;
|
|
349
|
+
const roles = requiredRolesForTier(tier);
|
|
350
|
+
return shares.some((s) => roles.includes(s.role));
|
|
351
|
+
}
|
|
352
|
+
function headerValue(req, name) {
|
|
353
|
+
const h = req.headers;
|
|
354
|
+
if (typeof h.get === 'function')
|
|
355
|
+
return h.get(name) ?? h.get(name.toLowerCase()) ?? undefined;
|
|
356
|
+
// Record form: HTTP header names are case-insensitive, but a plain object is not — scan case-insensitively.
|
|
357
|
+
const lower = name.toLowerCase();
|
|
358
|
+
let v = h[name] ?? h[lower];
|
|
359
|
+
if (v === undefined) {
|
|
360
|
+
for (const [k, val] of Object.entries(h)) {
|
|
361
|
+
if (k.toLowerCase() === lower && (typeof val === 'string' || Array.isArray(val))) {
|
|
362
|
+
v = val;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (Array.isArray(v))
|
|
368
|
+
return v[0];
|
|
369
|
+
return v;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Extract a PCActn from a request: the `PCA-Action` header (base64url-encoded PCActn JSON), else a JSON
|
|
373
|
+
* body `{ pcactn }` (string or object). Returns the decoded `PCActn`, or `null` when none is present.
|
|
374
|
+
* Throws only on an unreadable / undecodable encoding (the caller maps that to a 401).
|
|
375
|
+
*/
|
|
376
|
+
function extractPCActn(req) {
|
|
377
|
+
const h = headerValue(req, 'pca-action');
|
|
378
|
+
if (typeof h === 'string' && h.length > 0) {
|
|
379
|
+
const json = new TextDecoder().decode((0, pca_1.unb64u)(h.trim()));
|
|
380
|
+
return (0, pca_1.decodePCActn)(json);
|
|
381
|
+
}
|
|
382
|
+
let b = req.body;
|
|
383
|
+
if (typeof b === 'string' && b.length > 0) {
|
|
384
|
+
b = JSON.parse(b);
|
|
385
|
+
}
|
|
386
|
+
if (b !== null && typeof b === 'object') {
|
|
387
|
+
const x = b.pcactn;
|
|
388
|
+
if (typeof x === 'string')
|
|
389
|
+
return (0, pca_1.decodePCActn)(x);
|
|
390
|
+
if (x !== null && typeof x === 'object')
|
|
391
|
+
return x;
|
|
392
|
+
}
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
function requiredScopesFor(opts) {
|
|
396
|
+
if (opts.requiredScopes)
|
|
397
|
+
return opts.requiredScopes;
|
|
398
|
+
if (opts.tool !== undefined && opts.toolScopes) {
|
|
399
|
+
const req = opts.toolScopes[opts.tool];
|
|
400
|
+
if (req === undefined)
|
|
401
|
+
return [];
|
|
402
|
+
return Array.isArray(req) ? req : [req];
|
|
403
|
+
}
|
|
404
|
+
return [];
|
|
405
|
+
}
|
|
406
|
+
function requiredTierFor(opts, pcactn) {
|
|
407
|
+
if (opts.requiredTier !== undefined)
|
|
408
|
+
return opts.requiredTier;
|
|
409
|
+
if (opts.tool !== undefined && opts.toolStepUp) {
|
|
410
|
+
const t = opts.toolStepUp[opts.tool];
|
|
411
|
+
if (t !== undefined)
|
|
412
|
+
return t;
|
|
413
|
+
}
|
|
414
|
+
if (opts.riskTiers) {
|
|
415
|
+
const rt = (0, pca_1.requiredThreshold)(pcactn.risk_claim?.r ?? 1, opts.riskTiers);
|
|
416
|
+
return rt.t === 1 ? undefined : rt.t;
|
|
417
|
+
}
|
|
418
|
+
return undefined;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* The resource-server admission check for an MCP 2026-07-28 tool call, with a PCActn as the authority.
|
|
422
|
+
* It FAILS CLOSED and runs the checks in this order, each with the correct 401/403 + `WWW-Authenticate`:
|
|
423
|
+
*
|
|
424
|
+
* 1. extract the PCActn (missing / unreadable → 401 `invalid_request`);
|
|
425
|
+
* 2. resolve the grant from `grant_ref` (unknown → 401 `invalid_token`);
|
|
426
|
+
* 3. RFC 8707 — the PCActn's signed `aud` MUST equal this RS's `resource` (mismatch → 401 `invalid_token`);
|
|
427
|
+
* 4. RFC 9207 — the credential's issuer MUST be in `authorizationServers` (mix-up → 401 `invalid_token`);
|
|
428
|
+
* 5. the PCA core verification (`verifyPCActnCore`: wire, version, audience, validity, cap_chain,
|
|
429
|
+
* plan inclusion, leaf signature, counter). A crypto/binding failure → 401; a policy failure → 403;
|
|
430
|
+
* 6. scope accumulation — the tool's required scope(s) MUST be in the effective (narrowed) granted set
|
|
431
|
+
* (insufficient → 403 `insufficient_scope`, with `scope` in the challenge);
|
|
432
|
+
* 7. step-up — if the tool needs a tier ≥ 2 co-sign the PCActn does not yet carry, return a 401
|
|
433
|
+
* `insufficient_user_authentication` (RFC 9470) carrying the {@link StepUpChallenge}.
|
|
434
|
+
*
|
|
435
|
+
* On success it returns the verified PCActn, the grant, the effective granted scopes and the core checks.
|
|
436
|
+
*/
|
|
437
|
+
async function authorizeRequest(req, opts) {
|
|
438
|
+
const realm = opts.realm ?? 'pca';
|
|
439
|
+
const now = opts.now ?? Date.now();
|
|
440
|
+
const deny = (status, error, errorDescription, extra = {}) => {
|
|
441
|
+
const header = wwwAuthenticateChallenge({
|
|
442
|
+
resourceMetadataUrl: opts.prmUrl,
|
|
443
|
+
error,
|
|
444
|
+
errorDescription,
|
|
445
|
+
realm,
|
|
446
|
+
...(extra.scope !== undefined ? { scope: extra.scope } : {}),
|
|
447
|
+
...(extra.stepUp
|
|
448
|
+
? { acrValues: extra.stepUp.acr, maxAge: 0, pcaStepUp: extra.stepUp }
|
|
449
|
+
: {}),
|
|
450
|
+
});
|
|
451
|
+
return {
|
|
452
|
+
ok: false,
|
|
453
|
+
status,
|
|
454
|
+
error,
|
|
455
|
+
errorDescription,
|
|
456
|
+
wwwAuthenticate: header,
|
|
457
|
+
...(extra.stepUp ? { stepUp: extra.stepUp } : {}),
|
|
458
|
+
...(extra.checks ? { checks: extra.checks } : {}),
|
|
459
|
+
};
|
|
460
|
+
};
|
|
461
|
+
try {
|
|
462
|
+
if (typeof opts.resource !== 'string' || opts.resource.length === 0) {
|
|
463
|
+
throw new Error('authorizeRequest: `resource` is required');
|
|
464
|
+
}
|
|
465
|
+
if (!Array.isArray(opts.authorizationServers) || opts.authorizationServers.length === 0) {
|
|
466
|
+
throw new Error('authorizeRequest: `authorizationServers` is required (RFC 9207)');
|
|
467
|
+
}
|
|
468
|
+
// 1. extract
|
|
469
|
+
let pcactn;
|
|
470
|
+
try {
|
|
471
|
+
pcactn = extractPCActn(req);
|
|
472
|
+
}
|
|
473
|
+
catch (e) {
|
|
474
|
+
return deny(401, 'invalid_request', `unreadable PCActn: ${e.message}`);
|
|
475
|
+
}
|
|
476
|
+
if (pcactn === null)
|
|
477
|
+
return deny(401, 'invalid_request', 'no PCActn presented (PCA-Action header or {pcactn} body)');
|
|
478
|
+
if (typeof pcactn !== 'object' || typeof pcactn.grant_ref !== 'string') {
|
|
479
|
+
return deny(401, 'invalid_token', 'malformed PCActn');
|
|
480
|
+
}
|
|
481
|
+
// 2. resolve grant
|
|
482
|
+
const grant = await opts.resolveGrant(pcactn.grant_ref, pcactn);
|
|
483
|
+
if (!grant)
|
|
484
|
+
return deny(401, 'invalid_token', 'unknown grant_ref');
|
|
485
|
+
// 3. RFC 8707 — resource-indicator / audience binding. The signed `aud` is the real binding (a
|
|
486
|
+
// `resource` indicator can be dropped or spoofed in transit; the signature cannot).
|
|
487
|
+
if (typeof pcactn.aud !== 'string' || pcactn.aud !== opts.resource) {
|
|
488
|
+
return deny(401, 'invalid_token', `RFC 8707: PCActn audience (${String(pcactn.aud)}) is not this resource server (${opts.resource})`);
|
|
489
|
+
}
|
|
490
|
+
// 4. RFC 9207 — authorization-server issuer validation (mix-up defense).
|
|
491
|
+
const issuer = opts.resolveIssuer ? opts.resolveIssuer(grant, pcactn) : grant.issuer;
|
|
492
|
+
if (!opts.authorizationServers.includes(issuer)) {
|
|
493
|
+
return deny(401, 'invalid_token', `RFC 9207: credential issuer (${issuer}) is not an expected authorization server`);
|
|
494
|
+
}
|
|
495
|
+
// 5. PCA core verification (crypto + binding). Pass THIS RS's audience so the core re-binds too.
|
|
496
|
+
const verdict = await (0, pca_1.verifyPCActnCore)(pcactn, { grant, audience: opts.resource, nowEpoch: now });
|
|
497
|
+
if (!verdict.allow) {
|
|
498
|
+
const reason = verdict.reason ?? 'PCActn verification failed';
|
|
499
|
+
// Authentication-class failures are 401 (bad proof); everything else is a 403 (policy).
|
|
500
|
+
const authnChecks = ['wire', 'version', 'audience', 'validity', 'cap_chain', 'leaf_signature', 'malformed', 'counter'];
|
|
501
|
+
const authnFailed = authnChecks.some((c) => verdict.checks[c] === 'fail');
|
|
502
|
+
return authnFailed
|
|
503
|
+
? deny(401, 'invalid_token', reason, { checks: verdict.checks })
|
|
504
|
+
: deny(403, 'forbidden', reason, { checks: verdict.checks });
|
|
505
|
+
}
|
|
506
|
+
// 6. scope accumulation — the narrowed granted scopes must cover the tool's required scope(s).
|
|
507
|
+
const leaf = pcactn.cap_chain.at(-1);
|
|
508
|
+
const leafCaveats = leaf && Array.isArray(leaf.caveats) ? leaf.caveats : [];
|
|
509
|
+
const grantedScopes = opts.resolveGrantedScopes
|
|
510
|
+
? opts.resolveGrantedScopes(grant, pcactn.cap_chain, pcactn)
|
|
511
|
+
: accumulateScopes(leafCaveats, opts.baseScopes);
|
|
512
|
+
const need = requiredScopesFor(opts);
|
|
513
|
+
const missing = need.filter((s) => !grantedScopes.includes(s));
|
|
514
|
+
if (missing.length > 0) {
|
|
515
|
+
return deny(403, 'insufficient_scope', `missing required scope(s): ${missing.join(' ')}`, {
|
|
516
|
+
scope: need.join(' '),
|
|
517
|
+
checks: verdict.checks,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
// 7. step-up — a tool needing a tier ≥ 2 co-sign the PCActn does not yet carry.
|
|
521
|
+
const tier = requiredTierFor(opts, pcactn);
|
|
522
|
+
if (tier !== undefined) {
|
|
523
|
+
const satisfied = (opts.stepUpSatisfied ?? defaultStepUpSatisfied)(pcactn, tier);
|
|
524
|
+
if (!satisfied) {
|
|
525
|
+
const stepUp = {
|
|
526
|
+
tier,
|
|
527
|
+
requiredRoles: requiredRolesForTier(tier),
|
|
528
|
+
reason: `this action requires a tier-${tier} ${tier === 3 ? 'principal (human)' : 'guardian'} co-sign; drive the step-up and re-present a PCActn carrying the threshold`,
|
|
529
|
+
acr: `pca:tier${tier}`,
|
|
530
|
+
...(opts.stepUpEndpoint !== undefined ? { stepUpEndpoint: opts.stepUpEndpoint } : {}),
|
|
531
|
+
};
|
|
532
|
+
return deny(401, 'insufficient_user_authentication', stepUp.reason, { stepUp, checks: verdict.checks });
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return { ok: true, status: 200, pcactn, grant, grantedScopes, checks: verdict.checks };
|
|
536
|
+
}
|
|
537
|
+
catch (e) {
|
|
538
|
+
// Fail closed: any unexpected error is a 403 deny, never an accidental admit.
|
|
539
|
+
return deny(403, 'forbidden', `admission error (fail closed): ${e instanceof Error ? e.message : 'unknown'}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/pca-mcp-rs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP authorization resource-server for the 2026-07-28 spec: serve RFC 9728 PRM + Client-ID Metadata Documents, enforce RFC 8707 resource indicators + RFC 9207 issuer validation + scope accumulation, and wire PCA FROST step-up into the MCP step-up challenge.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"resource server",
|
|
8
|
+
"rfc 9728",
|
|
9
|
+
"rfc 8707",
|
|
10
|
+
"oauth",
|
|
11
|
+
"agent authorization",
|
|
12
|
+
"proof-carrying authority",
|
|
13
|
+
"pca",
|
|
14
|
+
"ai agents"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"homepage": "https://github.com/Atlas-Authorization/pca#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/Atlas-Authorization/pca/issues"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/Atlas-Authorization/pca.git",
|
|
24
|
+
"directory": "packages/pca-mcp-rs"
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@atlasauth/pca": "0.2.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"typescript": "^5.6.3",
|
|
40
|
+
"vitest": "^2.1.4"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
],
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=20"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsc -p tsconfig.build.json",
|
|
55
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
56
|
+
"test": "vitest run"
|
|
57
|
+
}
|
|
6
58
|
}
|