@valbuild/next 0.122.0 → 0.123.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/CHANGELOG.md +96 -0
- package/client/dist/valbuild-next-client.cjs.dev.js +3 -4
- package/client/dist/valbuild-next-client.cjs.prod.js +3 -4
- package/client/dist/valbuild-next-client.esm.js +1 -2
- package/dist/declarations/src/server/index.d.ts +1 -1
- package/dist/declarations/src/server/initValMcp.d.ts +19 -54
- package/dist/{routeFromVal-bd69625f.esm.js → routeFromVal-263eec96.esm.js} +11 -2
- package/dist/{routeFromVal-8032f5f7.cjs.prod.js → routeFromVal-8ac2f984.cjs.prod.js} +15 -5
- package/dist/{routeFromVal-7df20dfe.cjs.dev.js → routeFromVal-b48a454a.cjs.dev.js} +15 -5
- package/package.json +7 -6
- package/rsc/dist/valbuild-next-rsc.cjs.dev.js +2 -3
- package/rsc/dist/valbuild-next-rsc.cjs.prod.js +2 -3
- package/rsc/dist/valbuild-next-rsc.esm.js +1 -2
- package/server/dist/valbuild-next-server.cjs.dev.js +14 -1029
- package/server/dist/valbuild-next-server.cjs.prod.js +14 -1029
- package/server/dist/valbuild-next-server.esm.js +15 -1030
- package/dist/declarations/src/server/valAccessToken.d.ts +0 -93
- package/dist/declarations/src/server/valMcpMetadata.d.ts +0 -47
- package/dist/typeof-16428c61.cjs.prod.js +0 -13
- package/dist/typeof-a1531d8f.esm.js +0 -11
- package/dist/typeof-b568f48f.cjs.dev.js +0 -13
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { type ValToolAuth } from "@valbuild/server";
|
|
2
|
-
/**
|
|
3
|
-
* Verifying an OAuth access token, which is the whole of what makes this app a
|
|
4
|
-
* resource server rather than a relay.
|
|
5
|
-
*
|
|
6
|
-
* The token is issued by Val's authorization server and presented by an MCP
|
|
7
|
-
* client. This app holds no signing key for it and cannot mint one — it fetches
|
|
8
|
-
* the issuer's *public* keys and checks a signature. That asymmetry is the
|
|
9
|
-
* point: a verified `sub` is a fact about the token rather than a claim by
|
|
10
|
-
* whoever sent it, which is what lets the tools attribute a patch to that
|
|
11
|
-
* profile at all.
|
|
12
|
-
*
|
|
13
|
-
* ## Why this is not `jose`
|
|
14
|
-
*
|
|
15
|
-
* `jose` was the first choice and was rejected on a fact rather than a
|
|
16
|
-
* preference: version 6 is ESM-only (`"type": "module"`, no CJS export). This
|
|
17
|
-
* package is built by preconstruct and `require`d by Next.js server code, so an
|
|
18
|
-
* ESM-only dependency here is a runtime failure in consumers' apps, not a build
|
|
19
|
-
* inconvenience. Adding it would also put a dependency in every install of
|
|
20
|
-
* `@valbuild/next` for one function.
|
|
21
|
-
*
|
|
22
|
-
* The actual cryptography is still not hand-rolled — `node:crypto` does the
|
|
23
|
-
* ECDSA and the JWK import. What is written here is the JWS envelope and the
|
|
24
|
-
* claim checks, and the rules that keep that safe are worth stating because
|
|
25
|
-
* this repository has already shipped the counterexample (`decodeJwt`: `exp`
|
|
26
|
-
* never checked, a non-constant-time compare, verification skippable):
|
|
27
|
-
*
|
|
28
|
-
* - **`alg` is pinned**, not read from the token. The header is only consulted
|
|
29
|
-
* for `kid`. A verifier that honours the token's own `alg` can be handed
|
|
30
|
-
* `HS256` and will treat the *published* public key as a shared secret.
|
|
31
|
-
* - **Nothing is read from the payload before the signature verifies.** Claims
|
|
32
|
-
* from an unverified token are attacker input.
|
|
33
|
-
* - **Keys come only from the configured issuer's JWKS**, never from the token.
|
|
34
|
-
* The key set is cached, but a token naming a `kid` the cache does not hold
|
|
35
|
-
* provokes one rate-limited refetch rather than a refusal — see
|
|
36
|
-
* {@link UNKNOWN_KID_REFETCH_INTERVAL_MS}. Without that, this server's own
|
|
37
|
-
* cache turns any key rotation into an outage lasting the rest of the TTL.
|
|
38
|
-
* - **ECDSA JWS signatures are raw `r||s`** (RFC 7518), not DER, which is what
|
|
39
|
-
* `dsaEncoding: "ieee-p1363"` below is for. Omit it and every valid signature
|
|
40
|
-
* is rejected — or worse, a future change makes it accept the wrong thing.
|
|
41
|
-
*/
|
|
42
|
-
export type ValOAuthConfig = {
|
|
43
|
-
/**
|
|
44
|
-
* The authorization server, and therefore the expected `iss`.
|
|
45
|
-
*
|
|
46
|
-
* Also where the JWKS is fetched from, so it is the one value that decides
|
|
47
|
-
* which keys can produce a token this app accepts. Configuration, never
|
|
48
|
-
* request input — a request that could name its own issuer could name its own
|
|
49
|
-
* key.
|
|
50
|
-
*/
|
|
51
|
-
issuer: string;
|
|
52
|
-
/**
|
|
53
|
-
* This endpoint's absolute URL, and therefore the expected `aud` (RFC 8707).
|
|
54
|
-
*
|
|
55
|
-
* Audience binding is what stops a token minted for one Val site being
|
|
56
|
-
* replayed against another: without it, any deployment the user has access to
|
|
57
|
-
* would accept a token issued for any other.
|
|
58
|
-
*/
|
|
59
|
-
resource: string;
|
|
60
|
-
/**
|
|
61
|
-
* Clock skew allowance, in seconds.
|
|
62
|
-
*
|
|
63
|
-
* Servers disagree about the time by more than you would like, and a token
|
|
64
|
-
* refused for being a second early is indistinguishable, to the person using
|
|
65
|
-
* it, from a broken login.
|
|
66
|
-
*/
|
|
67
|
-
clockToleranceSeconds?: number;
|
|
68
|
-
/** Test seam. Defaults to the global `fetch`. */
|
|
69
|
-
fetchImpl?: typeof fetch;
|
|
70
|
-
};
|
|
71
|
-
export type ValAccessTokenResult = {
|
|
72
|
-
status: "ok";
|
|
73
|
-
auth: Extract<ValToolAuth, {
|
|
74
|
-
type: "verified-profile";
|
|
75
|
-
}>;
|
|
76
|
-
} | {
|
|
77
|
-
status: "refused";
|
|
78
|
-
/** For the `WWW-Authenticate` challenge: RFC 6750 section 3.1. */
|
|
79
|
-
error: "invalid_request" | "invalid_token" | "insufficient_scope";
|
|
80
|
-
description: string;
|
|
81
|
-
};
|
|
82
|
-
/** Test seam: a fresh process would have an empty cache anyway. */
|
|
83
|
-
export declare function clearValAccessTokenCache(): void;
|
|
84
|
-
/**
|
|
85
|
-
* Read `Authorization: Bearer …`.
|
|
86
|
-
*
|
|
87
|
-
* Exported because the refusal needs to know whether a token was presented at
|
|
88
|
-
* all: RFC 6750 distinguishes "no credential" — a bare `401`, which is an
|
|
89
|
-
* invitation to authenticate — from "a bad credential", and a client that gets
|
|
90
|
-
* the second when it deserved the first will not start the authorization flow.
|
|
91
|
-
*/
|
|
92
|
-
export declare function readBearerToken(request: Request): string | null;
|
|
93
|
-
export declare function verifyValAccessToken(request: Request, config: ValOAuthConfig): Promise<ValAccessTokenResult>;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { ValOAuthConfig } from "./valAccessToken.js";
|
|
2
|
-
/**
|
|
3
|
-
* The one document an MCP client needs before it can authorize: RFC 9728
|
|
4
|
-
* Protected Resource Metadata, served by the *resource* server.
|
|
5
|
-
*
|
|
6
|
-
* This is how a client discovers where to authorize. It asks the resource — this
|
|
7
|
-
* app — and the resource names its authorization server. Which is why this
|
|
8
|
-
* belongs here and the RFC 8414 *authorization server* metadata does not: that
|
|
9
|
-
* document lives at the issuer, describes the issuer's own endpoints, and is
|
|
10
|
-
* served by the issuer. An app serving a copy would be asserting the issuer's
|
|
11
|
-
* configuration on its behalf, and would be wrong the moment the issuer changed
|
|
12
|
-
* anything.
|
|
13
|
-
*
|
|
14
|
-
* The flow, so the split reads as a whole:
|
|
15
|
-
*
|
|
16
|
-
* 1. client → `{app}/api/mcp` with no token → `401` naming this document
|
|
17
|
-
* 2. client → `{app}/.well-known/oauth-protected-resource` → the issuer
|
|
18
|
-
* 3. client → `{issuer}/.well-known/oauth-authorization-server` → endpoints
|
|
19
|
-
* 4. client → issuer's `/authorize`, then `/token`
|
|
20
|
-
* 5. client → `{app}/api/mcp` with the token
|
|
21
|
-
*/
|
|
22
|
-
export type ValMcpMetadataHandlers = {
|
|
23
|
-
/** The metadata document. */
|
|
24
|
-
GET: (request: Request) => Response;
|
|
25
|
-
/**
|
|
26
|
-
* The CORS preflight.
|
|
27
|
-
*
|
|
28
|
-
* Required rather than defensive: the metadata document is fetched
|
|
29
|
-
* cross-origin by browser-based clients, and without a preflight answer the
|
|
30
|
-
* fetch fails before the document is read — which presents as "this connector
|
|
31
|
-
* cannot authorize" with nothing in any log to explain it.
|
|
32
|
-
*/
|
|
33
|
-
OPTIONS: (request: Request) => Response;
|
|
34
|
-
};
|
|
35
|
-
export declare function createValMcpMetadata(oauth: ValOAuthConfig, scopesSupported: string[]): ValMcpMetadataHandlers;
|
|
36
|
-
/**
|
|
37
|
-
* The `WWW-Authenticate` value for a refusal (RFC 6750 section 3, RFC 9728
|
|
38
|
-
* section 5.1).
|
|
39
|
-
*
|
|
40
|
-
* `resource_metadata` is the load-bearing parameter: it is how a client that has
|
|
41
|
-
* never seen this server learns where to authorize. A `401` without it is a dead
|
|
42
|
-
* end — the client knows it needs a token and has no way to find out from where.
|
|
43
|
-
*/
|
|
44
|
-
export declare function wwwAuthenticate(oauth: ValOAuthConfig, scopesSupported: string[], refusal?: {
|
|
45
|
-
error: string;
|
|
46
|
-
description: string;
|
|
47
|
-
}): string;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function _typeof(o) {
|
|
4
|
-
"@babel/helpers - typeof";
|
|
5
|
-
|
|
6
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
|
7
|
-
return typeof o;
|
|
8
|
-
} : function (o) {
|
|
9
|
-
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
10
|
-
}, _typeof(o);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
exports._typeof = _typeof;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
function _typeof(o) {
|
|
2
|
-
"@babel/helpers - typeof";
|
|
3
|
-
|
|
4
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
|
5
|
-
return typeof o;
|
|
6
|
-
} : function (o) {
|
|
7
|
-
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
8
|
-
}, _typeof(o);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export { _typeof as _ };
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function _typeof(o) {
|
|
4
|
-
"@babel/helpers - typeof";
|
|
5
|
-
|
|
6
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
|
7
|
-
return typeof o;
|
|
8
|
-
} : function (o) {
|
|
9
|
-
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
10
|
-
}, _typeof(o);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
exports._typeof = _typeof;
|