@run402/functions 2.7.0 → 3.0.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.
Files changed (40) hide show
  1. package/dist/auth/errors.d.ts +89 -0
  2. package/dist/auth/errors.d.ts.map +1 -0
  3. package/dist/auth/errors.js +170 -0
  4. package/dist/auth/errors.js.map +1 -0
  5. package/dist/auth/index.d.ts +98 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/index.js +453 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/auth/types.d.ts +58 -0
  10. package/dist/auth/types.d.ts.map +1 -0
  11. package/dist/auth/types.js +13 -0
  12. package/dist/auth/types.js.map +1 -0
  13. package/dist/auth/url-validation.d.ts +31 -0
  14. package/dist/auth/url-validation.d.ts.map +1 -0
  15. package/dist/auth/url-validation.js +83 -0
  16. package/dist/auth/url-validation.js.map +1 -0
  17. package/dist/auth.d.ts +25 -50
  18. package/dist/auth.d.ts.map +1 -1
  19. package/dist/auth.js +34 -103
  20. package/dist/auth.js.map +1 -1
  21. package/dist/db.d.ts.map +1 -1
  22. package/dist/db.js +40 -0
  23. package/dist/db.js.map +1 -1
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +18 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/lib/actor-context-verify.d.ts +95 -0
  29. package/dist/lib/actor-context-verify.d.ts.map +1 -0
  30. package/dist/lib/actor-context-verify.js +200 -0
  31. package/dist/lib/actor-context-verify.js.map +1 -0
  32. package/dist/runtime-context.d.ts +14 -1
  33. package/dist/runtime-context.d.ts.map +1 -1
  34. package/dist/runtime-context.js +60 -0
  35. package/dist/runtime-context.js.map +1 -1
  36. package/dist/verify-webhook.d.ts +71 -0
  37. package/dist/verify-webhook.d.ts.map +1 -0
  38. package/dist/verify-webhook.js +147 -0
  39. package/dist/verify-webhook.js.map +1 -0
  40. package/package.json +2 -2
@@ -0,0 +1,200 @@
1
+ /**
2
+ * SDK runtime verifier for the gateway-signed actor-context envelope.
3
+ *
4
+ * Why this lives in `packages/functions/src/lib/` (vendored shape):
5
+ * - `@run402/functions` is published independently; it must NOT depend
6
+ * on `@run402/gateway` types or runtime.
7
+ * - The verifier is small, deterministic, and security-critical — easier
8
+ * to audit when it lives next to the consumer.
9
+ * - Matches the convention used for `jwt.ts` (vendored upstream copy of
10
+ * the verifier we trust).
11
+ *
12
+ * Contract — the encoded envelope is `base64url(canonical_json) + "." +
13
+ * base64url(hmac_sha256)`. Canonical JSON has a fixed key order; the
14
+ * verifier doesn't re-canonicalise, it just rehmac's the body bytes that
15
+ * were base64-decoded. (Trying to re-derive canonical JSON from the parsed
16
+ * object would re-introduce key-order brittleness; verify the bytes you
17
+ * received, parse only to read fields.)
18
+ *
19
+ * Lookup of `actor_context_signing_key[kid]`:
20
+ * 1. `ACTOR_CONTEXT_SIGNING_KEY_MAP_JSON` env (`{kid: base64}` JSON).
21
+ * 2. `ACTOR_CONTEXT_SIGNING_KEY_<KID_UPPER>` env (one var per kid).
22
+ * 3. Test injection via `_setActorContextKeyMapForTest` on this module.
23
+ *
24
+ * Verification failure modes (mirrors the gateway side):
25
+ * - "malformed" — encoded shape rejected before parse
26
+ * - "unknown_kid" — envelope's kid not in the verifier map
27
+ * - "bad_signature" — HMAC mismatch
28
+ * - "iss_mismatch" — envelope.iss !== "run402-gateway"
29
+ * - "aud_mismatch" — envelope.aud !== "run402-functions-runtime"
30
+ * - "expired" — envelope.exp <= now
31
+ * - "lifetime_too_long" — envelope.exp - envelope.iat > 60s
32
+ * - "project_id_mismatch" — envelope.project_id !== request's
33
+ * - "request_id_mismatch" — envelope.request_id !== request's
34
+ * - "method_mismatch" — different HTTP method
35
+ * - "host_mismatch" — different host (after default-port normalise)
36
+ * - "path_mismatch" — different path (compared by sha256)
37
+ * - "version_mismatch" — schema version not what we compiled
38
+ *
39
+ * On ANY failure the SDK runtime treats the request as anonymous AND
40
+ * logs `R402_AUTH_ACTOR_HEADER_SPOOF` so spoofs / replays are visible
41
+ * in observability.
42
+ *
43
+ * @see openspec/changes/auth-aware-ssr/specs/routed-http-functions/spec.md
44
+ */
45
+ import crypto from "node:crypto";
46
+ export const ACTOR_CONTEXT_ENVELOPE_VERSION = 1;
47
+ export const ACTOR_CONTEXT_ENVELOPE_ISS = "run402-gateway";
48
+ export const ACTOR_CONTEXT_ENVELOPE_AUD = "run402-functions-runtime";
49
+ export const ACTOR_CONTEXT_MAX_LIFETIME_SEC = 60;
50
+ /** Inbound header carrying the encoded envelope. The runtime reads from
51
+ * the request headers in `RunRequestContext.request.headers`. */
52
+ export const ACTOR_CONTEXT_HEADER = "x-run402-actor-context";
53
+ // ---------------------------------------------------------------------------
54
+ // Key store (env-loaded, test-injectable)
55
+ // ---------------------------------------------------------------------------
56
+ const KEY_MIN_BYTES = 32;
57
+ let keyMap = null;
58
+ function loadKeyMap() {
59
+ if (keyMap)
60
+ return keyMap;
61
+ const map = new Map();
62
+ const mapJson = process.env.ACTOR_CONTEXT_SIGNING_KEY_MAP_JSON;
63
+ if (mapJson) {
64
+ let parsed;
65
+ try {
66
+ parsed = JSON.parse(mapJson);
67
+ }
68
+ catch (err) {
69
+ throw new Error(`ACTOR_CONTEXT_SIGNING_KEY_MAP_JSON is not valid JSON: ${err.message}`);
70
+ }
71
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
72
+ throw new Error("ACTOR_CONTEXT_SIGNING_KEY_MAP_JSON must be an object {kid: base64}");
73
+ }
74
+ for (const [kid, b64] of Object.entries(parsed)) {
75
+ if (typeof b64 !== "string")
76
+ continue;
77
+ const buf = Buffer.from(b64, "base64");
78
+ if (buf.length >= KEY_MIN_BYTES)
79
+ map.set(kid, buf);
80
+ }
81
+ }
82
+ for (const [envName, envValue] of Object.entries(process.env)) {
83
+ const match = envName.match(/^ACTOR_CONTEXT_SIGNING_KEY_([A-Z0-9_-]+)$/);
84
+ if (!match)
85
+ continue;
86
+ if (envName === "ACTOR_CONTEXT_SIGNING_KEY_CURRENT_KID" ||
87
+ envName === "ACTOR_CONTEXT_SIGNING_KEY_MAP_JSON") {
88
+ continue;
89
+ }
90
+ if (typeof envValue !== "string" || envValue.length === 0)
91
+ continue;
92
+ const kid = match[1].toLowerCase().replace(/_/g, "-");
93
+ if (map.has(kid))
94
+ continue;
95
+ const buf = Buffer.from(envValue, "base64");
96
+ if (buf.length >= KEY_MIN_BYTES)
97
+ map.set(kid, buf);
98
+ }
99
+ keyMap = map;
100
+ return map;
101
+ }
102
+ /** Test injection. NEVER call from production code. */
103
+ export function _setActorContextKeyMapForTest(map) {
104
+ if (map === null) {
105
+ keyMap = null;
106
+ return;
107
+ }
108
+ const m = new Map();
109
+ for (const [kid, value] of Object.entries(map)) {
110
+ const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, "base64");
111
+ if (buf.length < KEY_MIN_BYTES) {
112
+ throw new Error(`Actor-context key for kid "${kid}" is too short`);
113
+ }
114
+ m.set(kid, buf);
115
+ }
116
+ keyMap = m;
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Verifier
120
+ // ---------------------------------------------------------------------------
121
+ export function verifyActorContextEnvelope(encoded, ctx) {
122
+ if (typeof encoded !== "string" || encoded.length === 0) {
123
+ return { ok: false, reason: "malformed" };
124
+ }
125
+ const dot = encoded.indexOf(".");
126
+ if (dot <= 0 || dot === encoded.length - 1) {
127
+ return { ok: false, reason: "malformed" };
128
+ }
129
+ const bodyB64 = encoded.slice(0, dot);
130
+ const sigB64 = encoded.slice(dot + 1);
131
+ if (!/^[A-Za-z0-9_-]+$/.test(bodyB64) || !/^[A-Za-z0-9_-]+$/.test(sigB64)) {
132
+ return { ok: false, reason: "malformed" };
133
+ }
134
+ let bodyBuf;
135
+ let envelope;
136
+ try {
137
+ bodyBuf = Buffer.from(bodyB64, "base64url");
138
+ envelope = JSON.parse(bodyBuf.toString("utf8"));
139
+ }
140
+ catch {
141
+ return { ok: false, reason: "malformed" };
142
+ }
143
+ if (!envelope || typeof envelope !== "object") {
144
+ return { ok: false, reason: "malformed" };
145
+ }
146
+ if (envelope.v !== ACTOR_CONTEXT_ENVELOPE_VERSION) {
147
+ return { ok: false, reason: "version_mismatch" };
148
+ }
149
+ if (typeof envelope.kid !== "string")
150
+ return { ok: false, reason: "malformed" };
151
+ const key = loadKeyMap().get(envelope.kid);
152
+ if (!key)
153
+ return { ok: false, reason: "unknown_kid" };
154
+ const sigBuf = Buffer.from(sigB64, "base64url");
155
+ const expectedSig = crypto.createHmac("sha256", key).update(bodyBuf).digest();
156
+ if (sigBuf.length !== expectedSig.length) {
157
+ return { ok: false, reason: "bad_signature" };
158
+ }
159
+ if (!crypto.timingSafeEqual(sigBuf, expectedSig)) {
160
+ return { ok: false, reason: "bad_signature" };
161
+ }
162
+ if (envelope.iss !== ACTOR_CONTEXT_ENVELOPE_ISS) {
163
+ return { ok: false, reason: "iss_mismatch" };
164
+ }
165
+ if (envelope.aud !== ACTOR_CONTEXT_ENVELOPE_AUD) {
166
+ return { ok: false, reason: "aud_mismatch" };
167
+ }
168
+ if (typeof envelope.iat !== "number" || typeof envelope.exp !== "number") {
169
+ return { ok: false, reason: "malformed" };
170
+ }
171
+ if (envelope.exp - envelope.iat > ACTOR_CONTEXT_MAX_LIFETIME_SEC) {
172
+ return { ok: false, reason: "lifetime_too_long" };
173
+ }
174
+ const nowSec = Math.floor((ctx.now ?? new Date()).getTime() / 1000);
175
+ if (envelope.exp <= nowSec)
176
+ return { ok: false, reason: "expired" };
177
+ if (envelope.project_id !== ctx.projectId) {
178
+ return { ok: false, reason: "project_id_mismatch" };
179
+ }
180
+ if (envelope.request_id !== ctx.requestId) {
181
+ return { ok: false, reason: "request_id_mismatch" };
182
+ }
183
+ if (envelope.method !== ctx.method.toUpperCase()) {
184
+ return { ok: false, reason: "method_mismatch" };
185
+ }
186
+ if (envelope.host !== normaliseHost(ctx.host)) {
187
+ return { ok: false, reason: "host_mismatch" };
188
+ }
189
+ if (envelope.path_hash !== sha256Hex(ctx.path)) {
190
+ return { ok: false, reason: "path_mismatch" };
191
+ }
192
+ return { ok: true, envelope };
193
+ }
194
+ function normaliseHost(host) {
195
+ return host.toLowerCase().replace(/:80$/, "").replace(/:443$/, "");
196
+ }
197
+ function sha256Hex(s) {
198
+ return crypto.createHash("sha256").update(s, "utf8").digest("hex");
199
+ }
200
+ //# sourceMappingURL=actor-context-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actor-context-verify.js","sourceRoot":"","sources":["../../src/lib/actor-context-verify.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,15 @@
27
27
  * @see openspec/changes/astro-ssr-runtime/specs/routed-http-functions/spec.md
28
28
  */
29
29
  import { AsyncLocalStorage } from "node:async_hooks";
30
+ import { type VerifiedActorPayload } from "./lib/actor-context-verify.js";
31
+ /** Capability `auth-aware-ssr`: the verified actor payload the SDK
32
+ * exposes via `auth.user()` etc. Populated by `runWithContext` after
33
+ * it verifies the inbound actor-context envelope. `null` when the
34
+ * request is anonymous OR when verification failed (defense in depth:
35
+ * forged envelopes never surface as a non-null actor). */
36
+ export type ActorContext = VerifiedActorPayload & {
37
+ sessionId: string;
38
+ };
30
39
  /** The shape stored in AsyncLocalStorage. See the spec referenced above
31
40
  * for the canonical definition. */
32
41
  export interface RunRequestContext {
@@ -52,6 +61,10 @@ export interface RunRequestContext {
52
61
  url: string;
53
62
  headers: Record<string, string | string[] | undefined>;
54
63
  };
64
+ /** Capability `auth-aware-ssr`: actor populated from the verified
65
+ * gateway-signed envelope. The SDK's `auth.user()` returns this
66
+ * verbatim; consumer code never sees the raw envelope. */
67
+ actor: ActorContext | null;
55
68
  /** Mutable ref: SDK functions that read request-scoped auth or invoke
56
69
  * payment primitives set `value = true`. The SSR Lambda runtime
57
70
  * returns the final value to the gateway in the response metadata
@@ -85,7 +98,7 @@ export declare function getCurrentContext(): RunRequestContext | undefined;
85
98
  * runtime) flips it to `false` after the response body is materialized.
86
99
  * Don't call this from user code — it's the runtime's primitive.
87
100
  */
88
- export declare function runWithContext<T>(context: Omit<RunRequestContext, "cacheBypassTainted" | "active"> & Partial<Pick<RunRequestContext, "cacheBypassTainted" | "active">>, callback: () => Promise<T> | T): Promise<T> | T;
101
+ export declare function runWithContext<T>(context: Omit<RunRequestContext, "cacheBypassTainted" | "active" | "actor"> & Partial<Pick<RunRequestContext, "cacheBypassTainted" | "active" | "actor">>, callback: () => Promise<T> | T): Promise<T> | T;
89
102
  /**
90
103
  * Throw a structured `R402_SDK_OUTSIDE_REQUEST_CONTEXT` error. Used by
91
104
  * SDK functions when they're invoked with no ALS store OR while the
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-context.d.ts","sourceRoot":"","sources":["../src/runtime-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD;oCACoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB;yBACqB;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;qDACiD;IACjD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;yDACqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb;;;uBAGmB;IACnB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;KACxD,CAAC;IACF;;;oBAGgB;IAChB,kBAAkB,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IACvC;;;8CAG0C;IAC1C,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CAC5B;AAED;oEACoE;AACpE,eAAO,MAAM,GAAG,sCAA6C,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,GAAG,SAAS,CAEjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,GAAG,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,GAAG,QAAQ,CAAC,CAAC,EACrI,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC7B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAOhB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,IAAI,sCAAsC;IACnD,QAAQ,CAAC,IAAI,iEAAiE;IAC9E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,kBAAkB,CAAC;gBAEtC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,kBAAkB;CAc1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,CAS3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAUjD,CAAC;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EAC/D,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAChC,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAe7B"}
1
+ {"version":3,"file":"runtime-context.d.ts","sourceRoot":"","sources":["../src/runtime-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,+BAA+B,CAAC;AAEvC;;;;2DAI2D;AAC3D,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE;oCACoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB;yBACqB;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;qDACiD;IACjD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;yDACqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb;;;uBAGmB;IACnB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;KACxD,CAAC;IACF;;+DAE2D;IAC3D,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B;;;oBAGgB;IAChB,kBAAkB,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IACvC;;;8CAG0C;IAC1C,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CAC5B;AAED;oEACoE;AACpE,eAAO,MAAM,GAAG,sCAA6C,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,GAAG,SAAS,CAEjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,GAAG,QAAQ,GAAG,OAAO,CAAC,GACzE,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,GAAG,QAAQ,GAAG,OAAO,CAAC,CAAC,EAC7E,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC7B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAgBhB;AA6DD;;;;;;;;;;;GAWG;AACH,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,IAAI,sCAAsC;IACnD,QAAQ,CAAC,IAAI,iEAAiE;IAC9E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,kBAAkB,CAAC;gBAEtC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,kBAAkB;CAc1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,CAS3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAUjD,CAAC;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EAC/D,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAChC,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAe7B"}
@@ -27,6 +27,7 @@
27
27
  * @see openspec/changes/astro-ssr-runtime/specs/routed-http-functions/spec.md
28
28
  */
29
29
  import { AsyncLocalStorage } from "node:async_hooks";
30
+ import { verifyActorContextEnvelope, ACTOR_CONTEXT_HEADER, } from "./lib/actor-context-verify.js";
30
31
  /** The shared ALS instance. Modules in @run402/functions read from
31
32
  * this; the SSR Lambda runtime (in @run402/astro) writes to it. */
32
33
  export const als = new AsyncLocalStorage();
@@ -48,13 +49,72 @@ export function getCurrentContext() {
48
49
  * Don't call this from user code — it's the runtime's primitive.
49
50
  */
50
51
  export function runWithContext(context, callback) {
52
+ // Resolve the actor either from the caller-provided override (tests, the
53
+ // SSR runtime when it has pre-verified the envelope) or by verifying the
54
+ // inbound actor-context header against the request bound to this context.
55
+ // Failure is non-fatal: the request becomes anonymous AND we log
56
+ // `R402_AUTH_ACTOR_HEADER_SPOOF` so spoof attempts surface in metrics.
57
+ const actor = context.actor !== undefined
58
+ ? context.actor
59
+ : verifyEnvelopeFromRequest(context);
51
60
  const full = {
52
61
  ...context,
62
+ actor,
53
63
  cacheBypassTainted: context.cacheBypassTainted ?? { value: false },
54
64
  active: context.active ?? { value: true },
55
65
  };
56
66
  return als.run(full, callback);
57
67
  }
68
+ function verifyEnvelopeFromRequest(ctx) {
69
+ const headers = ctx.request.headers;
70
+ const headerValue = headers[ACTOR_CONTEXT_HEADER];
71
+ const encoded = Array.isArray(headerValue) ? headerValue[0] : headerValue;
72
+ if (!encoded || typeof encoded !== "string")
73
+ return null;
74
+ // The runtime sees `request.url` as a full path-only string from the
75
+ // SSR Lambda envelope (e.g. `/forum/topic-1?q=astro`). The signing side
76
+ // hashed the path-only URL, so we hash the same here. The query string
77
+ // is part of the binding so `/forum?topic=1` and `/forum?topic=2` can't
78
+ // accept the same envelope.
79
+ const path = ctx.request.url;
80
+ const outcome = verifyActorContextEnvelope(encoded, {
81
+ projectId: ctx.projectId,
82
+ requestId: ctx.requestId,
83
+ method: ctx.request.method,
84
+ host: ctx.host,
85
+ path,
86
+ });
87
+ if (!outcome.ok) {
88
+ logActorContextSpoof(outcome.reason, ctx.requestId);
89
+ return null;
90
+ }
91
+ return {
92
+ id: outcome.envelope.actor.id,
93
+ email: outcome.envelope.actor.email,
94
+ emailVerified: outcome.envelope.actor.emailVerified,
95
+ authTime: outcome.envelope.actor.authTime,
96
+ amr: outcome.envelope.actor.amr,
97
+ amrTimes: outcome.envelope.actor.amrTimes,
98
+ authzVersion: outcome.envelope.actor.authzVersion,
99
+ // session_id is NOT in the envelope — it's gateway-internal state.
100
+ // For now we mint a deterministic-by-request id so downstream code
101
+ // has a string to hang `db()` set_config on. Once the gateway adds
102
+ // session_id to the envelope (planned in section 4 of the spec),
103
+ // populate it here from outcome.envelope.actor.sessionId.
104
+ sessionId: outcome.envelope.request_id,
105
+ };
106
+ }
107
+ function logActorContextSpoof(reason, requestId) {
108
+ // Structured one-line log so observability can alert on rate. The
109
+ // SDK runs in Lambda; CloudWatch picks this up.
110
+ // eslint-disable-next-line no-console
111
+ console.warn(JSON.stringify({
112
+ level: "warn",
113
+ event: "R402_AUTH_ACTOR_HEADER_SPOOF",
114
+ reason,
115
+ request_id: requestId,
116
+ }));
117
+ }
58
118
  /**
59
119
  * Throw a structured `R402_SDK_OUTSIDE_REQUEST_CONTEXT` error. Used by
60
120
  * SDK functions when they're invoked with no ALS store OR while the
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-context.js","sourceRoot":"","sources":["../src/runtime-context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime-context.js","sourceRoot":"","sources":["../src/runtime-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Verify a Run402 operator-notifications webhook request.
3
+ *
4
+ * Run402 signs every webhook delivery with HMAC-SHA256 in Stripe shape:
5
+ * Run402-Signature: t=<unix_seconds>,v1=<hex_digest>
6
+ *
7
+ * where `<hex_digest>` is `HMAC_SHA256(secret, "${t}.${rawBody}")`.
8
+ *
9
+ * Receivers should call `verifyWebhook(headers, rawBody, secret)` BEFORE
10
+ * parsing or trusting the body. Pass the EXACT raw request body — re-
11
+ * serializing JSON will change byte order and break the HMAC.
12
+ *
13
+ * Dual-secret rotation: pass `previousSecret` (returned from
14
+ * `POST /agent/v1/webhook-secret/rotate` before rotation) to accept
15
+ * signatures created with either secret during the 24h grace window.
16
+ *
17
+ * Replay protection: the function rejects signatures whose `t` is more
18
+ * than `toleranceSeconds` (default 300 = 5 minutes) away from the
19
+ * current time.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { verifyWebhook } from "@run402/functions";
24
+ *
25
+ * export default async (req: Request) => {
26
+ * const rawBody = await req.text();
27
+ * const result = verifyWebhook(req.headers, rawBody, process.env.RUN402_WEBHOOK_SECRET!);
28
+ * if (!result.valid) {
29
+ * return new Response(`bad signature: ${result.reason}`, { status: 401 });
30
+ * }
31
+ * const event = JSON.parse(rawBody);
32
+ * // …handle event…
33
+ * return new Response("ok");
34
+ * };
35
+ * ```
36
+ */
37
+ export interface VerifyWebhookOptions {
38
+ /** Maximum age of the signed timestamp, in seconds. Default 300 (5 min). */
39
+ toleranceSeconds?: number;
40
+ /** Optional previous secret accepted during a 24h rotation grace window. */
41
+ previousSecret?: string | null;
42
+ /** Override the current Unix timestamp (for tests). */
43
+ nowSeconds?: () => number;
44
+ }
45
+ export type VerifyWebhookResult = {
46
+ valid: true;
47
+ timestamp: number;
48
+ secret_used: "current" | "previous";
49
+ } | {
50
+ valid: false;
51
+ reason: "missing_signature_header" | "malformed_signature_header" | "missing_timestamp" | "missing_v1" | "timestamp_out_of_tolerance" | "signature_mismatch";
52
+ };
53
+ /**
54
+ * Verify a Run402-signed webhook.
55
+ *
56
+ * @param headers Either an instance of `Headers` (Web API), a Node
57
+ * `IncomingHttpHeaders`-shaped record, or any object with
58
+ * `get(name)` and case-insensitive header access.
59
+ * @param rawBody The exact raw request body bytes received. Must NOT be
60
+ * re-serialized from a parsed JSON object — byte-level
61
+ * equality with the signed payload is required.
62
+ * @param secret The current webhook signing secret returned by
63
+ * `POST /agent/v1/webhook-secret/rotate`.
64
+ * @param options Optional behavior tweaks.
65
+ */
66
+ export declare function verifyWebhook(headers: HeadersLike, rawBody: string, secret: string, options?: VerifyWebhookOptions): VerifyWebhookResult;
67
+ /** Anything we can read a header from. */
68
+ export type HeadersLike = Headers | Record<string, string | string[] | undefined> | {
69
+ get(name: string): string | null;
70
+ };
71
+ //# sourceMappingURL=verify-webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-webhook.d.ts","sourceRoot":"","sources":["../src/verify-webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAIH,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,SAAS,GAAG,UAAU,CAAA;CAAE,GACvE;IACE,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EACF,0BAA0B,GAC1B,4BAA4B,GAC5B,mBAAmB,GACnB,YAAY,GACZ,4BAA4B,GAC5B,oBAAoB,CAAC;CAC1B,CAAC;AAEN;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,oBAAyB,GACjC,mBAAmB,CAoCrB;AAOD,0CAA0C;AAC1C,MAAM,MAAM,WAAW,GACnB,OAAO,GACP,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7C;IAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Verify a Run402 operator-notifications webhook request.
3
+ *
4
+ * Run402 signs every webhook delivery with HMAC-SHA256 in Stripe shape:
5
+ * Run402-Signature: t=<unix_seconds>,v1=<hex_digest>
6
+ *
7
+ * where `<hex_digest>` is `HMAC_SHA256(secret, "${t}.${rawBody}")`.
8
+ *
9
+ * Receivers should call `verifyWebhook(headers, rawBody, secret)` BEFORE
10
+ * parsing or trusting the body. Pass the EXACT raw request body — re-
11
+ * serializing JSON will change byte order and break the HMAC.
12
+ *
13
+ * Dual-secret rotation: pass `previousSecret` (returned from
14
+ * `POST /agent/v1/webhook-secret/rotate` before rotation) to accept
15
+ * signatures created with either secret during the 24h grace window.
16
+ *
17
+ * Replay protection: the function rejects signatures whose `t` is more
18
+ * than `toleranceSeconds` (default 300 = 5 minutes) away from the
19
+ * current time.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { verifyWebhook } from "@run402/functions";
24
+ *
25
+ * export default async (req: Request) => {
26
+ * const rawBody = await req.text();
27
+ * const result = verifyWebhook(req.headers, rawBody, process.env.RUN402_WEBHOOK_SECRET!);
28
+ * if (!result.valid) {
29
+ * return new Response(`bad signature: ${result.reason}`, { status: 401 });
30
+ * }
31
+ * const event = JSON.parse(rawBody);
32
+ * // …handle event…
33
+ * return new Response("ok");
34
+ * };
35
+ * ```
36
+ */
37
+ import { createHmac, timingSafeEqual } from "node:crypto";
38
+ /**
39
+ * Verify a Run402-signed webhook.
40
+ *
41
+ * @param headers Either an instance of `Headers` (Web API), a Node
42
+ * `IncomingHttpHeaders`-shaped record, or any object with
43
+ * `get(name)` and case-insensitive header access.
44
+ * @param rawBody The exact raw request body bytes received. Must NOT be
45
+ * re-serialized from a parsed JSON object — byte-level
46
+ * equality with the signed payload is required.
47
+ * @param secret The current webhook signing secret returned by
48
+ * `POST /agent/v1/webhook-secret/rotate`.
49
+ * @param options Optional behavior tweaks.
50
+ */
51
+ export function verifyWebhook(headers, rawBody, secret, options = {}) {
52
+ const tolerance = options.toleranceSeconds ?? 300;
53
+ const nowSeconds = options.nowSeconds ?? (() => Math.floor(Date.now() / 1000));
54
+ const headerValue = getHeader(headers, "run402-signature");
55
+ if (headerValue === null) {
56
+ // Header not present at all.
57
+ return { valid: false, reason: "missing_signature_header" };
58
+ }
59
+ const parsed = parseSignatureHeader(headerValue);
60
+ if (parsed === null) {
61
+ return { valid: false, reason: "malformed_signature_header" };
62
+ }
63
+ if (parsed.t === null) {
64
+ return { valid: false, reason: "missing_timestamp" };
65
+ }
66
+ if (parsed.v1Hex === null) {
67
+ return { valid: false, reason: "missing_v1" };
68
+ }
69
+ const now = nowSeconds();
70
+ if (Math.abs(now - parsed.t) > tolerance) {
71
+ return { valid: false, reason: "timestamp_out_of_tolerance" };
72
+ }
73
+ const signedPayload = `${parsed.t}.${rawBody}`;
74
+ if (constantTimeEqualHex(parsed.v1Hex, computeHmacHex(secret, signedPayload))) {
75
+ return { valid: true, timestamp: parsed.t, secret_used: "current" };
76
+ }
77
+ if (options.previousSecret) {
78
+ if (constantTimeEqualHex(parsed.v1Hex, computeHmacHex(options.previousSecret, signedPayload))) {
79
+ return { valid: true, timestamp: parsed.t, secret_used: "previous" };
80
+ }
81
+ }
82
+ return { valid: false, reason: "signature_mismatch" };
83
+ }
84
+ function getHeader(headers, name) {
85
+ // Web Headers: has .get and case-insensitive.
86
+ if (typeof headers.get === "function" && headers.get.length === 1) {
87
+ return headers.get(name);
88
+ }
89
+ // Plain object: case-insensitive lookup.
90
+ const lower = name.toLowerCase();
91
+ const rec = headers;
92
+ for (const key of Object.keys(rec)) {
93
+ if (key.toLowerCase() === lower) {
94
+ const v = rec[key];
95
+ if (Array.isArray(v))
96
+ return v[0] ?? null;
97
+ return v ?? null;
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+ function parseSignatureHeader(value) {
103
+ // Format: t=<unix>,v1=<hex>[,v2=...]
104
+ // Use comma + equals splitting; reject anything else.
105
+ const trimmed = value.trim();
106
+ if (trimmed === "")
107
+ return null;
108
+ const parts = trimmed.split(",");
109
+ let t = null;
110
+ let v1Hex = null;
111
+ for (const raw of parts) {
112
+ const segment = raw.trim();
113
+ const eq = segment.indexOf("=");
114
+ if (eq <= 0)
115
+ return null;
116
+ const key = segment.slice(0, eq).trim();
117
+ const val = segment.slice(eq + 1).trim();
118
+ if (!key || !val)
119
+ return null;
120
+ if (key === "t") {
121
+ const n = Number(val);
122
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0)
123
+ return null;
124
+ t = n;
125
+ }
126
+ else if (key === "v1") {
127
+ if (!/^[0-9a-f]{64}$/i.test(val))
128
+ return null;
129
+ v1Hex = val.toLowerCase();
130
+ }
131
+ // Unknown keys ignored — forward-compat for future v2 schemes.
132
+ }
133
+ return { t, v1Hex };
134
+ }
135
+ // ---------------------------------------------------------------------------
136
+ // Crypto.
137
+ // ---------------------------------------------------------------------------
138
+ function computeHmacHex(secret, payload) {
139
+ return createHmac("sha256", secret).update(payload).digest("hex");
140
+ }
141
+ function constantTimeEqualHex(a, b) {
142
+ if (a.length !== b.length)
143
+ return false;
144
+ // Convert both to Buffer of equal length, then timingSafeEqual.
145
+ return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
146
+ }
147
+ //# sourceMappingURL=verify-webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-webhook.js","sourceRoot":"","sources":["../src/verify-webhook.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@run402/functions",
3
- "version": "2.7.0",
4
- "description": "In-function helper library for Run402 serverless functions — db, adminDb, getUser, email, ai, assets. Auto-bundled into deployed functions; also installable for local TypeScript autocomplete.",
3
+ "version": "3.0.0",
4
+ "description": "In-function helper library for Run402 serverless functions — db, adminDb, getUser, email, ai, assets, verifyWebhook. Auto-bundled into deployed functions; also installable for local TypeScript autocomplete.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",