better-supabase 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/bin/better-supabase.js +4 -0
- package/dist/{base64-CQeB1eSf.js → base64-D2gsioCY.js} +2 -2
- package/dist/channel-FQW-CnGR.d.ts +70 -0
- package/dist/claims-BAf6c7is.js +48 -0
- package/dist/cli/_format-Cax5EsM9.js +101 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +48 -0
- package/dist/cli/codemod-DqV7612m.js +388 -0
- package/dist/cli/command-CK14fP91.js +502 -0
- package/dist/cli/compare-BmIWVEui.js +9 -0
- package/dist/cli/config-CPVpXP5I.js +1965 -0
- package/dist/cli/config-dtoekM0v.js +2 -0
- package/dist/cli/db-DOBPt660.js +52 -0
- package/dist/cli/diff-SjkO5rNr.js +752 -0
- package/dist/cli/dist-DBPdlPYK.js +1112 -0
- package/dist/cli/dist-DreZ24Sz.js +129 -0
- package/dist/cli/dist-xn4wlboc.js +732 -0
- package/dist/cli/doctor-L7Z8mYTx.js +2863 -0
- package/dist/cli/env-DmKv_G4A.js +108 -0
- package/dist/cli/env-jIFUADLY.js +511 -0
- package/dist/cli/errors-4v4DTtda.js +56 -0
- package/dist/cli/fastest-levenshtein-CFN-lNDv.js +120 -0
- package/dist/cli/gen-BJc9vdyu.js +2 -0
- package/dist/cli/gen-C2osRcr5.js +2204 -0
- package/dist/cli/index.d.ts +493 -0
- package/dist/cli/index.js +184 -0
- package/dist/cli/init-Saf5-R9T.js +550 -0
- package/dist/cli/introspect--H7oGmcd.js +81 -0
- package/dist/cli/io-k41_z-jT.js +24 -0
- package/dist/cli/json5-ChRycquj.js +787 -0
- package/dist/cli/jsonc-BUniUpOk.js +2 -0
- package/dist/cli/jsonc-D5Fu67jw.js +45 -0
- package/dist/cli/keys-ByKHnvP9.js +83 -0
- package/dist/cli/ohash-D7bG_EOb.js +277 -0
- package/dist/cli/openapi-Cp5Fyta-.js +66 -0
- package/dist/cli/project-B_GeXyHh.js +70 -0
- package/dist/cli/prompts-BNcNfAve.js +1142 -0
- package/dist/cli/prompts-BTYQGunc.js +2 -0
- package/dist/cli/read-sets-BfMQcKy8.js +289 -0
- package/dist/cli/rolldown-runtime-BPOCksWG.js +24 -0
- package/dist/cli/run-BKR2W_79.js +351 -0
- package/dist/cli/seed-CUlqWh-8.js +98 -0
- package/dist/cli/skills-LJJIhLFR.js +137 -0
- package/dist/cli/sql-DzPbfngx.js +348 -0
- package/dist/cli/style-C0NFdJRL.js +29 -0
- package/dist/cli/toml-DB-MNnFs.js +424 -0
- package/dist/cli/version-BDJuO0UR.js +4 -0
- package/dist/cli/yaml-CYCbJ6et.js +2252 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +15 -22
- package/dist/clone-BfgmLU90.js +21 -0
- package/dist/{compiler-2zqGzxVZ.d.ts → compiler-BXH_aZ7N.d.ts} +2 -2
- package/dist/config/index.d.ts +4 -4
- package/dist/config/index.js +5 -5
- package/dist/{config-DOsGYSiT.js → config-DreERlKo.js} +1 -1
- package/dist/{config-C1CYcm6o.d.ts → config-T_ejwyd3.d.ts} +176 -11
- package/dist/{define-DUbydmJa.d.ts → define-Bp_BZa5h.d.ts} +37 -29
- package/dist/{define-DF341GDj.js → define-cNKzqwL_.js} +356 -89
- package/dist/edge/index.d.ts +20 -13
- package/dist/edge/index.js +55 -26
- package/dist/{entitlements-DvJXoI02.d.ts → entitlements-DUxU4oHe.d.ts} +2 -2
- package/dist/env/index.d.ts +2 -2
- package/dist/env/index.js +34 -1
- package/dist/{errors-Bl3rkOzo.js → errors-DtwXa6b8.js} +2 -1
- package/dist/events/index.d.ts +3 -2
- package/dist/events/index.js +69 -16
- package/dist/events-aIO7vsIk.d.ts +230 -0
- package/dist/executor-BunPOO-k.d.ts +402 -0
- package/dist/{executor-B26lgbHD.d.ts → executor-Cghdstwu.d.ts} +1 -2
- package/dist/{executor-C57jx2pn.js → executor-XycESQPH.js} +3 -3
- package/dist/hono/index.d.ts +27 -23
- package/dist/hono/index.js +46 -11
- package/dist/impersonation-VfaGc4Q_.js +41 -0
- package/dist/{index-BNsABq7P.d.ts → index-BGdgBPdf.d.ts} +66 -25
- package/dist/{index-qriRM9cZ.d.ts → index-C7f8cOBM.d.ts} +4 -4
- package/dist/{index-Dy6kuF0P.d.ts → index-CLg87OBW.d.ts} +6 -1
- package/dist/{index-GV77NpRP.d.ts → index-DOESKG27.d.ts} +34 -9
- package/dist/{index-CriFFJ_p.d.ts → index-DgILJRXj.d.ts} +43 -17
- package/dist/{index-UJ-LUxtI.d.ts → index-DmXSxbLi.d.ts} +14 -14
- package/dist/{index-Cf-D2jk1.d.ts → index-Dxn2a5cw.d.ts} +2 -2
- package/dist/{index-CTTMS6_1.d.ts → index-SaTsEfGP.d.ts} +86 -9
- package/dist/{index-CADlNHK-.d.ts → index-fag8VJZ2.d.ts} +5 -6
- package/dist/index.d.ts +34 -17
- package/dist/index.js +15 -14
- package/dist/jobs/index.d.ts +147 -95
- package/dist/jobs/index.js +773 -250
- package/dist/{json-schema--aOXAGfU.js → json-schema-m1fiwKgN.js} +23 -2
- package/dist/kit-events-CTFDfSwb.js +60 -0
- package/dist/kit-transport-B82k1djG.d.ts +33 -0
- package/dist/kit-transport-DJlHQUBU.js +55 -0
- package/dist/{config-Be4GwsqI.js → kits-CGLLrNPd.js} +38 -10
- package/dist/list/index.d.ts +1 -1
- package/dist/list/index.js +3 -3
- package/dist/{live-D8rV1Ldw.js → live-CdJBOc4f.js} +13 -9
- package/dist/{live-QE3UPbGm.d.ts → live-D9W2M5aO.d.ts} +16 -15
- package/dist/lookup-BaP5bI27.js +18 -0
- package/dist/mcp/index.d.ts +39 -22
- package/dist/mcp/index.js +39 -25
- package/dist/{mfa-x3V3vGh-.d.ts → mfa-CusDgPaD.d.ts} +5 -1
- package/dist/next/image/index.js +6 -3
- package/dist/next/index.d.ts +60 -36
- package/dist/next/index.js +167 -50
- package/dist/notifications/index.d.ts +162 -0
- package/dist/notifications/index.js +264 -0
- package/dist/openapi/index.d.ts +1 -1
- package/dist/openapi/index.js +6 -6
- package/dist/orgs/index.d.ts +132 -0
- package/dist/orgs/index.js +245 -0
- package/dist/orpc/index.d.ts +37 -20
- package/dist/orpc/index.js +24 -11
- package/dist/otel/index.d.ts +24 -5
- package/dist/otel/index.js +124 -64
- package/dist/permdock-sql-C2Inc6Xc.d.ts +29 -0
- package/dist/permdock-sql-DgOfeixi.js +51 -0
- package/dist/plugin-DwARMuQp.js +59 -0
- package/dist/{plugin-DVRB8qNE.d.ts → plugin-o144f322.d.ts} +17 -5
- package/dist/plugins/actor/index.d.ts +5 -2
- package/dist/plugins/actor/index.js +22 -8
- package/dist/plugins/rules/index.d.ts +2 -2
- package/dist/plugins/rules/index.js +52 -17
- package/dist/plugins/soft-delete/index.d.ts +3 -3
- package/dist/plugins/soft-delete/index.js +14 -5
- package/dist/plugins/tenant/index.d.ts +3 -3
- package/dist/plugins/tenant/index.js +45 -21
- package/dist/plugins/timestamps/index.d.ts +1 -1
- package/dist/plugins/timestamps/index.js +4 -3
- package/dist/plugins/validation/index.d.ts +2 -2
- package/dist/plugins/validation/index.js +26 -6
- package/dist/{pool-B9qsLyFR.d.ts → pool-C9voM4Kw.d.ts} +24 -6
- package/dist/postgres/index.d.ts +5 -5
- package/dist/postgres/index.js +20 -10
- package/dist/{postgrest-DmBlevh-.d.ts → postgrest-CruYKAvV.d.ts} +1 -1
- package/dist/{problem-CGt1ADix.js → problem-BW1KhzSG.js} +2 -1
- package/dist/{problem-Z2RTQjZD.d.ts → problem-Blxe8S31.d.ts} +1 -1
- package/dist/query/index.d.ts +2 -2
- package/dist/query/index.js +3 -3
- package/dist/{query-BdKLTPlj.js → query-CCbo44lG.js} +18 -18
- package/dist/queue-BYUCjr59.d.ts +252 -0
- package/dist/react/index.d.ts +8 -7
- package/dist/react/index.js +4 -3
- package/dist/react/server.d.ts +16 -5
- package/dist/react/server.js +12 -4
- package/dist/react/session.d.ts +10 -4
- package/dist/react/session.js +11 -3
- package/dist/{react-DNBYNazu.js → react-C_s7kiNz.js} +138 -46
- package/dist/{read-set-D0qBcboM.js → read-set-DQFt1N_Y.js} +71 -15
- package/dist/realtime/index.d.ts +3 -3
- package/dist/realtime/index.js +14 -12
- package/dist/{index-kpgopLNv.d.ts → refresh-BTrQ_ZTp.d.ts} +6 -3
- package/dist/{resolve-BRciX2jK.d.ts → resolve-C15TJq77.d.ts} +22 -11
- package/dist/{resource-D30rp0GN.js → resource-CRxxwkQq.js} +24 -9
- package/dist/{resource-CwVgiI5q.d.ts → resource-D8iqWMr-.d.ts} +6 -6
- package/dist/respond-D2OQl62-.d.ts +371 -0
- package/dist/{result-DWXatkd6.js → result-BKiliPY7.js} +2 -2
- package/dist/server/index.d.ts +17 -15
- package/dist/server/index.js +16 -22
- package/dist/server-Csx2sRjg.js +655 -0
- package/dist/{shared-_6-uPL7E.js → shared-D1dRUrAg.js} +19 -4
- package/dist/signers-Cc6r98SW.d.ts +162 -0
- package/dist/{simplify-LakqK1vH.js → simplify-_0oUKqCd.js} +27 -95
- package/dist/sql/index.d.ts +247 -10
- package/dist/sql/index.js +8591 -780
- package/dist/{sql-BIgk12ew.js → sql-Coh6n4cC.js} +11 -3
- package/dist/ssr/index.d.ts +4 -4
- package/dist/ssr/index.js +1 -2
- package/dist/{standard-FUi3Z3-7.js → standard-D7r6dMUu.js} +6 -3
- package/dist/{standard-eudBiRk1.d.ts → standard-J4YgMOLm.d.ts} +1 -1
- package/dist/{stats-D78UQ8Yu.js → stats-Bqob3WqT.js} +34 -9
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +174 -27
- package/dist/support-DT-OhSxD.d.ts +88 -0
- package/dist/support-Dk6vdmEw.js +291 -0
- package/dist/support-view-Da5peKFg.d.ts +17 -0
- package/dist/support-view-g4YLBeqX.js +18 -0
- package/dist/{tables-Dhha3BXM.js → tables-Cb-3_VWc.js} +14 -0
- package/dist/{permdock-sql-Bta_G0v-.js → template-TxrGI5jR.js} +2 -31
- package/dist/{temporal-BwshQk0W.js → temporal-CVlIV2DZ.js} +17 -4
- package/dist/{temporal-required-CMxeSVPo.js → temporal-required-CAt4Dc9Z.js} +2 -2
- package/dist/testing/index.d.ts +113 -24
- package/dist/testing/index.js +375 -47
- package/dist/{types-DONv9SHN.d.ts → types-Dnf_wRDz.d.ts} +26 -3
- package/dist/user-change-CHFEUro1.js +49 -0
- package/dist/{webhooks-CAIFQSTM.js → verify-Ce49ETPz.js} +38 -19
- package/dist/{version-BfpG2gks.js → version-DM_Roffb.js} +1 -1
- package/dist/{view-3Po_PVlr.d.ts → view-BEf0diC5.d.ts} +10 -5
- package/dist/{mfa-BxoR1RkN.js → view-Bq64ki8-.js} +210 -64
- package/dist/webhooks/index.d.ts +136 -5
- package/dist/webhooks/index.js +588 -2
- package/dist/wire-D2dT86vi.js +35 -0
- package/package.json +50 -10
- package/schemas/config-v1.json +131 -1
- package/schemas/doctor-report-v1.json +10 -0
- package/skills/better-supabase/SKILL.md +60 -10
- package/skills/better-supabase/references/plugins.md +13 -14
- package/skills/better-supabase/references/troubleshooting.md +25 -4
- package/skills/better-supabase-api/SKILL.md +74 -19
- package/skills/better-supabase-api/references/adapters.md +48 -27
- package/skills/better-supabase-auth/SKILL.md +149 -0
- package/skills/better-supabase-auth/references/permdock.md +100 -0
- package/skills/better-supabase-testing/SKILL.md +58 -4
- package/dist/claims-CoTobNC-.js +0 -25
- package/dist/events-Do8jXh4D.d.ts +0 -84
- package/dist/executor-DynmIWhq.d.ts +0 -195
- package/dist/invalidate-DbxuL2jL.js +0 -18
- package/dist/permdock-sql-DImJULY-.d.ts +0 -17
- package/dist/plugin-BqR6wKMB.js +0 -23
- package/dist/respond-C44EZAJo.js +0 -399
- package/dist/respond-DAkPIdTH.d.ts +0 -175
- package/dist/result-c2OYgCV2.d.ts +0 -203
- package/dist/view-wZ-9qIUz.js +0 -45
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/query/invalidate.ts
|
|
2
|
+
function isString(value) {
|
|
3
|
+
return typeof value === "string";
|
|
4
|
+
}
|
|
5
|
+
function queryTables(query) {
|
|
6
|
+
const tables = query.meta?.["bsTables"];
|
|
7
|
+
if (Array.isArray(tables)) {
|
|
8
|
+
const list = tables;
|
|
9
|
+
return list.every(isString) ? list : list.filter(isString);
|
|
10
|
+
}
|
|
11
|
+
const [prefix, table] = query.queryKey;
|
|
12
|
+
return prefix === "bs" && typeof table === "string" ? [table] : void 0;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Invalidates every query that read one of `tables`: better-supabase queries
|
|
16
|
+
* by their `meta.bsTables`, and other `['bs', table, ...]` keys by prefix.
|
|
17
|
+
*/
|
|
18
|
+
function invalidateTables(client, tables) {
|
|
19
|
+
if (tables.length === 0) return Promise.resolve();
|
|
20
|
+
const changed = new Set(tables);
|
|
21
|
+
return client.invalidateQueries({ predicate: (query) => queryTables(query)?.some((table) => changed.has(table)) ?? false });
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/query/user-change.ts
|
|
25
|
+
/**
|
|
26
|
+
* Removes every `['bs', ...]` query when the signed-in user changes or signs
|
|
27
|
+
* out, so the next user never sees the previous user's rows. Returns the
|
|
28
|
+
* function that stops listening. `BetterSupabaseProvider` calls it for React;
|
|
29
|
+
* call it yourself with Vue, Solid, Svelte or Angular Query.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* const stop = clearOnUserChange(queryClient, bs.auth);
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
function clearOnUserChange(queryClient, auth) {
|
|
36
|
+
let user;
|
|
37
|
+
const check = () => {
|
|
38
|
+
const snapshot = auth.current();
|
|
39
|
+
if (snapshot.status === "loading") return;
|
|
40
|
+
const id = snapshot.user?.id ?? null;
|
|
41
|
+
if (user !== void 0 && user !== id) queryClient.removeQueries({ queryKey: ["bs"] });
|
|
42
|
+
user = id;
|
|
43
|
+
};
|
|
44
|
+
const stop = auth.subscribe(check);
|
|
45
|
+
check();
|
|
46
|
+
return stop;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { invalidateTables as n, clearOnUserChange as t };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { n as dbError } from "./errors-
|
|
2
|
-
import { n as nowInstant, r as optionalTemporal } from "./temporal-
|
|
3
|
-
import { n as temporalMissing } from "./temporal-required-
|
|
4
|
-
import { i as ok, n as err } from "./result-
|
|
5
|
-
import { i as toBase64, r as fromBase64 } from "./base64-
|
|
6
|
-
import {
|
|
1
|
+
import { n as dbError } from "./errors-DtwXa6b8.js";
|
|
2
|
+
import { n as nowInstant, r as optionalTemporal } from "./temporal-CVlIV2DZ.js";
|
|
3
|
+
import { n as temporalMissing } from "./temporal-required-CAt4Dc9Z.js";
|
|
4
|
+
import { i as ok, n as err } from "./result-BKiliPY7.js";
|
|
5
|
+
import { i as toBase64, r as fromBase64 } from "./base64-D2gsioCY.js";
|
|
6
|
+
import { c as toApp } from "./shared-D1dRUrAg.js";
|
|
7
7
|
//#region src/webhooks/verify.ts
|
|
8
|
-
const encoder = new TextEncoder();
|
|
8
|
+
const encoder = /* @__PURE__ */ new TextEncoder();
|
|
9
9
|
/** Accepts `whsec_…`, `v1,whsec_…` (Supabase Auth hooks) or raw base64. */
|
|
10
10
|
function secretBytes(secret) {
|
|
11
11
|
const raw = secret.replace(/^v1,/, "").replace(/^whsec_/, "");
|
|
@@ -15,12 +15,31 @@ function secretBytes(secret) {
|
|
|
15
15
|
throw new TypeError("Webhook secret is not valid base64");
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
/** Secrets come from configuration, so a handful of keys suffice. */
|
|
19
|
+
const keys = /* @__PURE__ */ new Map();
|
|
20
|
+
function keyFor(secret) {
|
|
21
|
+
let key = keys.get(secret);
|
|
22
|
+
if (!key) {
|
|
23
|
+
key = crypto.subtle.importKey("raw", secretBytes(secret), {
|
|
24
|
+
name: "HMAC",
|
|
25
|
+
hash: "SHA-256"
|
|
26
|
+
}, false, ["sign", "verify"]);
|
|
27
|
+
if (keys.size < 16) {
|
|
28
|
+
keys.set(secret, key);
|
|
29
|
+
key.catch(() => keys.delete(secret));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return key;
|
|
33
|
+
}
|
|
18
34
|
async function signature(secret, content) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
return toBase64(new Uint8Array(await crypto.subtle.sign("HMAC", await keyFor(secret), encoder.encode(content))));
|
|
36
|
+
}
|
|
37
|
+
function decodeSignature(value) {
|
|
38
|
+
try {
|
|
39
|
+
return fromBase64(value);
|
|
40
|
+
} catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
24
43
|
}
|
|
25
44
|
/** Constant-time string comparison. */
|
|
26
45
|
function timingSafeEqual(a, b) {
|
|
@@ -51,7 +70,6 @@ async function signWebhook(secret, message) {
|
|
|
51
70
|
*/
|
|
52
71
|
async function verifyWebhook(input, secrets, options = {}) {
|
|
53
72
|
const headers = input.headers;
|
|
54
|
-
const body = input instanceof Request ? await input.text() : input.body;
|
|
55
73
|
const id = header(headers, "webhook-id");
|
|
56
74
|
const timestamp = header(headers, "webhook-timestamp");
|
|
57
75
|
const signatures = header(headers, "webhook-signature");
|
|
@@ -64,12 +82,13 @@ async function verifyWebhook(input, secrets, options = {}) {
|
|
|
64
82
|
const tolerance = options.tolerance ?? 300;
|
|
65
83
|
if (seconds < now - tolerance) return err(unauthorized("WEBHOOK_TIMESTAMP_TOO_OLD", "Webhook timestamp is too old"));
|
|
66
84
|
if (seconds > now + tolerance) return err(unauthorized("WEBHOOK_TIMESTAMP_TOO_NEW", "Webhook timestamp is in the future"));
|
|
67
|
-
const
|
|
68
|
-
const
|
|
85
|
+
const body = input instanceof Request ? await input.text() : input.body;
|
|
86
|
+
const content = encoder.encode(`${id}.${timestamp}.${body}`);
|
|
87
|
+
const offered = signatures.split(" ").map((entry) => entry.split(",")).filter(([version, value]) => version === "v1" && value).flatMap(([, value]) => decodeSignature(value) ?? []);
|
|
69
88
|
let valid = false;
|
|
70
89
|
for (const secret of typeof secrets === "string" ? [secrets] : secrets) {
|
|
71
|
-
const
|
|
72
|
-
for (const candidate of offered) if (
|
|
90
|
+
const key = await keyFor(secret);
|
|
91
|
+
for (const candidate of offered) if (await crypto.subtle.verify("HMAC", key, candidate, content)) valid = true;
|
|
73
92
|
}
|
|
74
93
|
if (!valid) return err(unauthorized("WEBHOOK_INVALID_SIGNATURE", "Invalid webhook signature"));
|
|
75
94
|
try {
|
|
@@ -127,8 +146,8 @@ function isDatabaseWebhook(value) {
|
|
|
127
146
|
* Reads a database webhook payload for `table` as an app-cased change.
|
|
128
147
|
* Returns `null` for other tables or payloads that aren't database webhooks.
|
|
129
148
|
*/
|
|
130
|
-
function databaseChange(
|
|
131
|
-
const meta =
|
|
149
|
+
function databaseChange(betterSupabase, table, payload) {
|
|
150
|
+
const meta = betterSupabase.meta.tables[table];
|
|
132
151
|
if (!meta || !isDatabaseWebhook(payload) || payload.table !== meta.name || payload.schema !== meta.schema) return null;
|
|
133
152
|
return {
|
|
134
153
|
type: payload.type,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { _ as DbError } from "./executor-BunPOO-k.js";
|
|
2
|
+
import { i as InvalidReason, r as AuthState } from "./resolve-C15TJq77.js";
|
|
3
|
+
import { n as AmrEntry, s as Impersonator, t as Aal } from "./mfa-CusDgPaD.js";
|
|
4
4
|
import { JWTClaims, UserClaims } from "@supabase/server";
|
|
5
5
|
//#region src/auth/actor.d.ts
|
|
6
6
|
/** One level of an RFC 8693 `act` chain: who acts, and who acted before it. */
|
|
@@ -35,19 +35,24 @@ type AuthSession<C = unknown, P = unknown> = {
|
|
|
35
35
|
readonly user: UserClaims;
|
|
36
36
|
/**
|
|
37
37
|
* The verified JWT payload, including custom access token hook claims,
|
|
38
|
-
* typed and validated by `
|
|
38
|
+
* typed and validated by `betterSupabase.claims(schema)`.
|
|
39
39
|
*/
|
|
40
40
|
readonly claims: JWTClaims & C;
|
|
41
41
|
/** Seconds since epoch, from the token's `exp`. */
|
|
42
42
|
readonly expiresAt: number | null;
|
|
43
43
|
/**
|
|
44
|
-
* `user_metadata` parsed by `
|
|
44
|
+
* `user_metadata` parsed by `betterSupabase.userMetadata(schema)`, for display.
|
|
45
45
|
* Absent without a schema or when the metadata fails it. Users
|
|
46
46
|
* write this data with `auth.updateUser()`: never base access on it.
|
|
47
47
|
*/
|
|
48
48
|
readonly profile?: P;
|
|
49
49
|
/** `aal2` once the user verified a second factor in this session. */
|
|
50
50
|
readonly aal: Aal;
|
|
51
|
+
/**
|
|
52
|
+
* Signed in with `signInAnonymously()` (the `is_anonymous` claim). Guards
|
|
53
|
+
* refuse these users unless `allow` lists `'anonymous'`.
|
|
54
|
+
*/
|
|
55
|
+
readonly anonymous: boolean;
|
|
51
56
|
/** How the user signed in (`password`, `totp`, `sso/saml`, ...). */
|
|
52
57
|
readonly amr: readonly AmrEntry[];
|
|
53
58
|
/** Set when an admin acts as this user (the `act` claim), for a banner. */
|
|
@@ -1,28 +1,9 @@
|
|
|
1
|
-
import { n as dbError } from "./errors-
|
|
1
|
+
import { n as dbError } from "./errors-DtwXa6b8.js";
|
|
2
2
|
import { t as consoleLogger } from "./logger-glM28NVN.js";
|
|
3
|
-
import { n as decodeJwtPayload } from "./base64-
|
|
3
|
+
import { n as decodeJwtPayload } from "./base64-D2gsioCY.js";
|
|
4
|
+
import { n as impersonatorOf, r as userContext } from "./impersonation-VfaGc4Q_.js";
|
|
4
5
|
import { DEFAULT_COOKIE_OPTIONS, createChunks, isChunkLike, parseCookieHeader, serializeCookieHeader, stringFromBase64URL, stringToBase64URL } from "@supabase/ssr";
|
|
5
6
|
import { extractCredentials, verifyCredentials } from "@supabase/server/core";
|
|
6
|
-
//#region src/auth/impersonation.ts
|
|
7
|
-
/** The `act` claim for an impersonated session. */
|
|
8
|
-
function actClaim(options) {
|
|
9
|
-
return {
|
|
10
|
-
sub: options.actor,
|
|
11
|
-
reason: options.reason
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
/** The impersonator in `claims.act`, or `undefined` for a normal session. */
|
|
15
|
-
function impersonatorOf(claims) {
|
|
16
|
-
const act = claims["act"];
|
|
17
|
-
if (typeof act !== "object" || act === null) return void 0;
|
|
18
|
-
const { sub, reason } = act;
|
|
19
|
-
if (typeof sub !== "string" || sub === "") return void 0;
|
|
20
|
-
return typeof reason === "string" ? {
|
|
21
|
-
id: sub,
|
|
22
|
-
reason
|
|
23
|
-
} : { id: sub };
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
7
|
//#region src/auth/actor.ts
|
|
27
8
|
function isRecord(value) {
|
|
28
9
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -89,6 +70,7 @@ function delegationOf(claims) {
|
|
|
89
70
|
* refresh-token reuse interval of Supabase Auth.
|
|
90
71
|
*/
|
|
91
72
|
const REUSE_MS = 1e4;
|
|
73
|
+
const DEFAULT_TIMEOUT_MS = 5e3;
|
|
92
74
|
const inflight = /* @__PURE__ */ new Map();
|
|
93
75
|
const recent = /* @__PURE__ */ new Map();
|
|
94
76
|
function shared(outcome) {
|
|
@@ -98,10 +80,14 @@ function shared(outcome) {
|
|
|
98
80
|
} : outcome;
|
|
99
81
|
}
|
|
100
82
|
function prune(now) {
|
|
101
|
-
for (const [token, entry] of recent)
|
|
83
|
+
for (const [token, entry] of recent) {
|
|
84
|
+
if (entry.until > now) return;
|
|
85
|
+
recent.delete(token);
|
|
86
|
+
}
|
|
102
87
|
}
|
|
103
88
|
async function request(refreshToken, options, now) {
|
|
104
89
|
const doFetch = options.fetch ?? fetch;
|
|
90
|
+
const signal = AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
105
91
|
let response;
|
|
106
92
|
try {
|
|
107
93
|
response = await doFetch(`${options.url}/auth/v1/token?grant_type=refresh_token`, {
|
|
@@ -111,7 +97,8 @@ async function request(refreshToken, options, now) {
|
|
|
111
97
|
"content-type": "application/json",
|
|
112
98
|
...options.forwardedFor ? { "sb-forwarded-for": options.forwardedFor.ip } : {}
|
|
113
99
|
},
|
|
114
|
-
body: JSON.stringify({ refresh_token: refreshToken })
|
|
100
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
101
|
+
signal
|
|
115
102
|
});
|
|
116
103
|
} catch (cause) {
|
|
117
104
|
return {
|
|
@@ -121,6 +108,11 @@ async function request(refreshToken, options, now) {
|
|
|
121
108
|
};
|
|
122
109
|
}
|
|
123
110
|
const body = await response.json().catch(() => null);
|
|
111
|
+
if (signal.aborted) return {
|
|
112
|
+
ok: false,
|
|
113
|
+
reason: "network",
|
|
114
|
+
message: "refresh timed out"
|
|
115
|
+
};
|
|
124
116
|
if (!response.ok || !body || typeof body["access_token"] !== "string" || typeof body["refresh_token"] !== "string") {
|
|
125
117
|
const message = typeof body?.["msg"] === "string" ? body["msg"] : typeof body?.["error_description"] === "string" ? body["error_description"] : `Refresh failed with ${String(response.status)}`;
|
|
126
118
|
return {
|
|
@@ -154,12 +146,16 @@ function refreshSession(refreshToken, options) {
|
|
|
154
146
|
const pending = inflight.get(refreshToken);
|
|
155
147
|
if (pending) return pending.then(shared);
|
|
156
148
|
const promise = request(refreshToken, options, now).then((outcome) => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
149
|
+
if (outcome.ok || outcome.reason === "rejected") {
|
|
150
|
+
recent.delete(refreshToken);
|
|
151
|
+
recent.set(refreshToken, {
|
|
152
|
+
outcome,
|
|
153
|
+
until: now + REUSE_MS
|
|
154
|
+
});
|
|
155
|
+
}
|
|
162
156
|
return outcome;
|
|
157
|
+
}).finally(() => {
|
|
158
|
+
inflight.delete(refreshToken);
|
|
163
159
|
});
|
|
164
160
|
inflight.set(refreshToken, promise);
|
|
165
161
|
return promise;
|
|
@@ -175,8 +171,15 @@ const AUTH_CACHE_HEADERS = {
|
|
|
175
171
|
};
|
|
176
172
|
/** `sb-<first label of the hostname>-auth-token`, the supabase-js default. */
|
|
177
173
|
function sessionCookieName(url) {
|
|
178
|
-
|
|
174
|
+
let name = cookieNames.get(url);
|
|
175
|
+
if (name === void 0) {
|
|
176
|
+
name = `sb-${new URL(url).hostname.split(".")[0] ?? ""}-auth-token`;
|
|
177
|
+
if (cookieNames.size < 16) cookieNames.set(url, name);
|
|
178
|
+
}
|
|
179
|
+
return name;
|
|
179
180
|
}
|
|
181
|
+
/** Project URLs come from configuration, so a handful of entries suffice. */
|
|
182
|
+
const cookieNames = /* @__PURE__ */ new Map();
|
|
180
183
|
function parseCookies(header) {
|
|
181
184
|
return header ? parseCookieHeader(header) : [];
|
|
182
185
|
}
|
|
@@ -262,7 +265,7 @@ function serverEnv(options) {
|
|
|
262
265
|
url: env.url,
|
|
263
266
|
publishableKeys: { default: env.publishableKey },
|
|
264
267
|
secretKeys: env.secretKeys ?? (env.secretKey ? { default: env.secretKey } : {}),
|
|
265
|
-
jwks: options.jwks ?? env.jwksUrl
|
|
268
|
+
jwks: options.jwks ?? env.jwks ?? env.jwksUrl
|
|
266
269
|
};
|
|
267
270
|
}
|
|
268
271
|
const MEMO_SIZE = 256;
|
|
@@ -273,7 +276,7 @@ let memoByJwks = /* @__PURE__ */ new WeakMap();
|
|
|
273
276
|
* and island resolves the same token again; this skips the signature check.
|
|
274
277
|
*/
|
|
275
278
|
function memoFor(options) {
|
|
276
|
-
const jwks = options.jwks;
|
|
279
|
+
const jwks = options.jwks ?? options.env.jwks;
|
|
277
280
|
if (jwks && typeof jwks === "object" && !(jwks instanceof URL)) {
|
|
278
281
|
let memo = memoByJwks.get(jwks);
|
|
279
282
|
if (!memo) memoByJwks.set(jwks, memo = /* @__PURE__ */ new Map());
|
|
@@ -289,6 +292,42 @@ function memoFor(options) {
|
|
|
289
292
|
if (!memo) memoByUrl.set(key, memo = /* @__PURE__ */ new Map());
|
|
290
293
|
return memo;
|
|
291
294
|
}
|
|
295
|
+
function sameJson(a, b) {
|
|
296
|
+
if (a === b) return true;
|
|
297
|
+
if (typeof a !== "object" || typeof b !== "object" || !a || !b) return false;
|
|
298
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
299
|
+
const left = Object.entries(a);
|
|
300
|
+
const right = new Map(Object.entries(b));
|
|
301
|
+
return left.length === right.size && left.every(([key, value]) => right.has(key) && sameJson(value, right.get(key)));
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Records a token that `@supabase/server` verified for this request, so the
|
|
305
|
+
* next `resolveAuth` with these options doesn't verify it again. Only when
|
|
306
|
+
* `claims` is the token's own payload, it hasn't expired, and its `iss` and
|
|
307
|
+
* `aud` pass the checks these options would apply; otherwise returns false
|
|
308
|
+
* and the token is verified as usual.
|
|
309
|
+
*/
|
|
310
|
+
function rememberVerified(options, verified) {
|
|
311
|
+
const { token, claims, user } = verified;
|
|
312
|
+
const now = Math.floor((options.now ?? Date.now)() / 1e3);
|
|
313
|
+
const expiresAt = typeof claims.exp === "number" ? claims.exp : null;
|
|
314
|
+
if (expiresAt === null || expiresAt <= now) return false;
|
|
315
|
+
if (!sameJson(decodeJwtPayload(token), claims)) return false;
|
|
316
|
+
const issuers = [options.issuer ?? `${options.env.url}/auth/v1`].flat();
|
|
317
|
+
if (typeof claims.iss !== "string" || !issuers.includes(claims.iss)) return false;
|
|
318
|
+
const audiences = [options.audience ?? "authenticated"].flat();
|
|
319
|
+
const aud = claims.aud;
|
|
320
|
+
const given = Array.isArray(aud) ? aud : [aud];
|
|
321
|
+
if (!audiences.some((audience) => given.includes(audience))) return false;
|
|
322
|
+
remember(memoFor(options), token, {
|
|
323
|
+
kind: "user",
|
|
324
|
+
token,
|
|
325
|
+
claims,
|
|
326
|
+
user,
|
|
327
|
+
expiresAt
|
|
328
|
+
}, expiresAt);
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
292
331
|
function remembered(memo, token, now) {
|
|
293
332
|
const hit = memo.get(token);
|
|
294
333
|
if (!hit) return void 0;
|
|
@@ -297,8 +336,31 @@ function remembered(memo, token, now) {
|
|
|
297
336
|
memo.set(token, hit);
|
|
298
337
|
return hit;
|
|
299
338
|
}
|
|
300
|
-
|
|
301
|
-
|
|
339
|
+
const NO_SCHEMA = {};
|
|
340
|
+
const checkedByMemo = /* @__PURE__ */ new WeakMap();
|
|
341
|
+
/**
|
|
342
|
+
* Tokens that already passed (or failed) the claims and `userMetadata`
|
|
343
|
+
* schemas, per verification memo and schema pair, so a memo hit skips
|
|
344
|
+
* validation too.
|
|
345
|
+
*/
|
|
346
|
+
function checkedFor(memo, options) {
|
|
347
|
+
const claims = options.claims ?? NO_SCHEMA;
|
|
348
|
+
const metadata = options.userMetadata ?? NO_SCHEMA;
|
|
349
|
+
let byClaims = checkedByMemo.get(memo);
|
|
350
|
+
if (!byClaims) checkedByMemo.set(memo, byClaims = /* @__PURE__ */ new WeakMap());
|
|
351
|
+
let byMetadata = byClaims.get(claims);
|
|
352
|
+
if (!byMetadata) byClaims.set(claims, byMetadata = /* @__PURE__ */ new WeakMap());
|
|
353
|
+
let checked = byMetadata.get(metadata);
|
|
354
|
+
if (!checked) byMetadata.set(metadata, checked = /* @__PURE__ */ new Map());
|
|
355
|
+
return checked;
|
|
356
|
+
}
|
|
357
|
+
function withoutSource(state) {
|
|
358
|
+
if (state.kind !== "user") return state;
|
|
359
|
+
const { source: _source, ...rest } = state;
|
|
360
|
+
return rest;
|
|
361
|
+
}
|
|
362
|
+
function remember(memo, token, user, expiresAt) {
|
|
363
|
+
if (expiresAt === null) return;
|
|
302
364
|
memo.set(token, user);
|
|
303
365
|
if (memo.size > MEMO_SIZE) memo.delete(memo.keys().next().value);
|
|
304
366
|
}
|
|
@@ -307,10 +369,20 @@ async function verify(credentials, modes, options, source) {
|
|
|
307
369
|
const memo = token ? memoFor(options) : void 0;
|
|
308
370
|
if (token && memo) {
|
|
309
371
|
const hit = remembered(memo, token, Math.floor((options.now ?? Date.now)() / 1e3));
|
|
310
|
-
if (hit)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
372
|
+
if (hit) {
|
|
373
|
+
const checked = checkedFor(memo, options);
|
|
374
|
+
const known = checked.get(token);
|
|
375
|
+
if (known) return known.kind === "user" ? {
|
|
376
|
+
...known,
|
|
377
|
+
source
|
|
378
|
+
} : known;
|
|
379
|
+
const state = await checkUser({
|
|
380
|
+
...hit,
|
|
381
|
+
source
|
|
382
|
+
}, options);
|
|
383
|
+
remember(checked, token, withoutSource(state), hit.expiresAt);
|
|
384
|
+
return state;
|
|
385
|
+
}
|
|
314
386
|
}
|
|
315
387
|
const state = await verifyOnce(credentials, modes, options, source);
|
|
316
388
|
if (token && memo && state.kind === "user") remember(memo, token, {
|
|
@@ -319,8 +391,10 @@ async function verify(credentials, modes, options, source) {
|
|
|
319
391
|
claims: state.claims,
|
|
320
392
|
user: state.user,
|
|
321
393
|
expiresAt: state.expiresAt
|
|
322
|
-
});
|
|
323
|
-
|
|
394
|
+
}, state.expiresAt);
|
|
395
|
+
const checked = await checkUser(state, options);
|
|
396
|
+
if (token && memo && state.kind === "user") remember(checkedFor(memo, options), token, withoutSource(checked), state.expiresAt);
|
|
397
|
+
return checked;
|
|
324
398
|
}
|
|
325
399
|
/** Validates a user's claims against `options.claims`, keeping every JWT claim. */
|
|
326
400
|
async function checkClaims(state, options) {
|
|
@@ -368,10 +442,34 @@ async function checkUser(state, options) {
|
|
|
368
442
|
};
|
|
369
443
|
if (!warnedMetadata.has(schema)) {
|
|
370
444
|
warnedMetadata.add(schema);
|
|
371
|
-
(options.logger ?? consoleLogger).warn("user_metadata does not match
|
|
445
|
+
(options.logger ?? consoleLogger).warn("user_metadata does not match betterSupabase.userMetadata(schema); session.profile is undefined", { paths: outcome.issues.map((issue) => issue.path?.map((segment) => typeof segment === "object" ? String(segment.key) : String(segment)).join(".") ?? "") });
|
|
372
446
|
}
|
|
373
447
|
return user;
|
|
374
448
|
}
|
|
449
|
+
/** A token whose `kid` matches no key: verifying it loads the JWKS and fails. */
|
|
450
|
+
const PREFETCH_TOKEN = `${base64url(JSON.stringify({
|
|
451
|
+
alg: "ES256",
|
|
452
|
+
kid: "better-supabase-prefetch",
|
|
453
|
+
typ: "JWT"
|
|
454
|
+
}))}.e30.AA`;
|
|
455
|
+
function base64url(text) {
|
|
456
|
+
return btoa(text).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Loads the remote JWKS into the verifier's cache so the first request
|
|
460
|
+
* doesn't wait for it. Never throws; a failure leaves the first request to
|
|
461
|
+
* fetch it as before.
|
|
462
|
+
*/
|
|
463
|
+
function prefetchJwks(options) {
|
|
464
|
+
if (options.jwks !== void 0 || options.env.jwks !== void 0) return Promise.resolve();
|
|
465
|
+
return verifyCredentials({
|
|
466
|
+
token: PREFETCH_TOKEN,
|
|
467
|
+
apikey: null
|
|
468
|
+
}, {
|
|
469
|
+
auth: ["user"],
|
|
470
|
+
env: serverEnv(options)
|
|
471
|
+
}).then(() => {}, () => {});
|
|
472
|
+
}
|
|
375
473
|
async function verifyOnce(credentials, modes, options, source) {
|
|
376
474
|
const { data, error } = await verifyCredentials(credentials, {
|
|
377
475
|
auth: modes,
|
|
@@ -406,11 +504,16 @@ async function verifyOnce(credentials, modes, options, source) {
|
|
|
406
504
|
}
|
|
407
505
|
function resolution(auth, cookies, writes = []) {
|
|
408
506
|
const headers = writes.length > 0 ? AUTH_CACHE_HEADERS : {};
|
|
507
|
+
let requestCookies;
|
|
409
508
|
return {
|
|
410
509
|
auth,
|
|
411
510
|
cookies: writes,
|
|
412
511
|
headers,
|
|
413
|
-
|
|
512
|
+
/** Parsed on first read: bearer requests rarely need their cookies. */
|
|
513
|
+
get requestCookies() {
|
|
514
|
+
requestCookies ??= writes.length > 0 ? applyCookieWrites(cookies(), writes) : cookies();
|
|
515
|
+
return requestCookies;
|
|
516
|
+
},
|
|
414
517
|
apply(response) {
|
|
415
518
|
if (writes.length === 0) return response;
|
|
416
519
|
let target = response;
|
|
@@ -440,7 +543,8 @@ function clientIp(request) {
|
|
|
440
543
|
* set, through a single-flight request, and only then are cookies written.
|
|
441
544
|
*/
|
|
442
545
|
async function resolveAuth(request, options) {
|
|
443
|
-
|
|
546
|
+
let parsed;
|
|
547
|
+
const cookies = () => parsed ??= parseCookies(request.headers.get("cookie"));
|
|
444
548
|
for (const resolver of options.resolvers ?? []) {
|
|
445
549
|
const state = await resolver.resolve(request);
|
|
446
550
|
if (!state) continue;
|
|
@@ -459,11 +563,15 @@ async function resolveAuth(request, options) {
|
|
|
459
563
|
token: null,
|
|
460
564
|
apikey: credentials.apikey
|
|
461
565
|
}, options.secret === true ? ["secret"] : options.secret.map((name) => `secret:${name}`), options, "bearer"), cookies);
|
|
566
|
+
if (options.cookies === false) return resolution({
|
|
567
|
+
kind: "anon",
|
|
568
|
+
reason: "none"
|
|
569
|
+
}, cookies);
|
|
462
570
|
const name = options.cookie?.name ?? sessionCookieName(options.env.url);
|
|
463
|
-
const session = readSession(cookies, name);
|
|
571
|
+
const session = readSession(cookies(), name);
|
|
464
572
|
if (!session) {
|
|
465
|
-
const stale = cookies.some((cookie) => cookie.name === name || cookie.name.startsWith(`${name}.`));
|
|
466
|
-
const writes = stale && options.refresh ? writeSession(cookies, name, null, options.cookie?.options) : [];
|
|
573
|
+
const stale = cookies().some((cookie) => cookie.name === name || cookie.name.startsWith(`${name}.`));
|
|
574
|
+
const writes = stale && options.refresh ? writeSession(cookies(), name, null, options.cookie?.options) : [];
|
|
467
575
|
return resolution({
|
|
468
576
|
kind: "anon",
|
|
469
577
|
reason: stale ? "signed_out" : "none"
|
|
@@ -471,7 +579,8 @@ async function resolveAuth(request, options) {
|
|
|
471
579
|
}
|
|
472
580
|
const now = Math.floor((options.now ?? Date.now)() / 1e3);
|
|
473
581
|
const expiry = expiryOf(session);
|
|
474
|
-
|
|
582
|
+
const leeway = options.refresh ? options.leeway ?? 60 : 0;
|
|
583
|
+
if (expiry !== void 0 && expiry - leeway > now) {
|
|
475
584
|
const state = await verify({
|
|
476
585
|
token: session.access_token,
|
|
477
586
|
apikey: null
|
|
@@ -496,7 +605,8 @@ async function resolveAuth(request, options) {
|
|
|
496
605
|
secretKey
|
|
497
606
|
} } : {},
|
|
498
607
|
...options.fetch ? { fetch: options.fetch } : {},
|
|
499
|
-
...options.now ? { now: options.now } : {}
|
|
608
|
+
...options.now ? { now: options.now } : {},
|
|
609
|
+
...options.refreshTimeoutMs === void 0 ? {} : { timeoutMs: options.refreshTimeoutMs }
|
|
500
610
|
});
|
|
501
611
|
options.onRefresh?.({
|
|
502
612
|
ok: outcome.ok,
|
|
@@ -511,9 +621,9 @@ async function resolveAuth(request, options) {
|
|
|
511
621
|
return resolution({
|
|
512
622
|
kind: "anon",
|
|
513
623
|
reason: "signed_out"
|
|
514
|
-
}, cookies, writeSession(cookies, name, null, options.cookie?.options));
|
|
624
|
+
}, cookies, writeSession(cookies(), name, null, options.cookie?.options));
|
|
515
625
|
}
|
|
516
|
-
const writes = writeSession(cookies, name, outcome.session, options.cookie?.options);
|
|
626
|
+
const writes = writeSession(cookies(), name, outcome.session, options.cookie?.options);
|
|
517
627
|
return resolution(await verify({
|
|
518
628
|
token: outcome.session.access_token,
|
|
519
629
|
apikey: null
|
|
@@ -522,19 +632,7 @@ async function resolveAuth(request, options) {
|
|
|
522
632
|
/** The repository context (`actor`, `claims`) for an auth state. */
|
|
523
633
|
function authContext(auth) {
|
|
524
634
|
switch (auth.kind) {
|
|
525
|
-
case "user":
|
|
526
|
-
const impersonator = impersonatorOf(auth.claims);
|
|
527
|
-
return {
|
|
528
|
-
actor: {
|
|
529
|
-
id: auth.user.id,
|
|
530
|
-
kind: "user",
|
|
531
|
-
...auth.user.role === void 0 ? {} : { role: auth.user.role },
|
|
532
|
-
...auth.user.email === void 0 ? {} : { email: auth.user.email },
|
|
533
|
-
...impersonator ? { impersonator: impersonator.id } : {}
|
|
534
|
-
},
|
|
535
|
-
claims: auth.claims
|
|
536
|
-
};
|
|
537
|
-
}
|
|
635
|
+
case "user": return userContext(auth.user, auth.claims);
|
|
538
636
|
case "service": return {
|
|
539
637
|
actor: {
|
|
540
638
|
id: `service:${auth.keyName}`,
|
|
@@ -593,4 +691,52 @@ function checkAal(auth, required) {
|
|
|
593
691
|
});
|
|
594
692
|
}
|
|
595
693
|
//#endregion
|
|
596
|
-
|
|
694
|
+
//#region src/auth/view.ts
|
|
695
|
+
/** Whether the token belongs to an anonymous user (`signInAnonymously()`). */
|
|
696
|
+
function isAnonymousUser(claims) {
|
|
697
|
+
return "is_anonymous" in claims && claims.is_anonymous === true;
|
|
698
|
+
}
|
|
699
|
+
/** Drops the token from an `AuthState`, leaving only serializable fields. */
|
|
700
|
+
function toSession(auth) {
|
|
701
|
+
switch (auth.kind) {
|
|
702
|
+
case "user": {
|
|
703
|
+
const impersonator = impersonatorOf(auth.claims);
|
|
704
|
+
const outcome = actorOf(auth.claims);
|
|
705
|
+
const actor = outcome.ok ? outcome.actor : void 0;
|
|
706
|
+
const scopes = actor ? delegationOf(auth.claims)?.scopes : void 0;
|
|
707
|
+
const delegation = actor && (scopes || actor.chain) ? {
|
|
708
|
+
scopes: scopes ?? [],
|
|
709
|
+
...actor.chain ? { chain: actor.chain } : {}
|
|
710
|
+
} : void 0;
|
|
711
|
+
return {
|
|
712
|
+
kind: "user",
|
|
713
|
+
user: auth.user,
|
|
714
|
+
claims: auth.claims,
|
|
715
|
+
expiresAt: auth.expiresAt,
|
|
716
|
+
...auth.profile === void 0 ? {} : { profile: auth.profile },
|
|
717
|
+
aal: aalOf(auth.claims),
|
|
718
|
+
anonymous: isAnonymousUser(auth.claims),
|
|
719
|
+
amr: amrOf(auth.claims),
|
|
720
|
+
...impersonator ? { impersonator } : {},
|
|
721
|
+
...actor ? { actor } : {},
|
|
722
|
+
...delegation ? { delegation } : {}
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
case "service": return {
|
|
726
|
+
kind: "service",
|
|
727
|
+
keyName: auth.keyName
|
|
728
|
+
};
|
|
729
|
+
case "anon": return {
|
|
730
|
+
kind: "anon",
|
|
731
|
+
reason: auth.reason
|
|
732
|
+
};
|
|
733
|
+
case "invalid": return {
|
|
734
|
+
kind: "invalid",
|
|
735
|
+
reason: auth.reason,
|
|
736
|
+
error: auth.error
|
|
737
|
+
};
|
|
738
|
+
default: return auth;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
//#endregion
|
|
742
|
+
export { writeSession as _, checkAal as a, delegationOf as b, prefetchJwks as c, AUTH_CACHE_HEADERS as d, applyCookieWrites as f, sessionCookieName as g, serializeCookie as h, amrOf as i, rememberVerified as l, readSession as m, toSession as n, authContext as o, parseCookies as p, aalOf as r, clientIp as s, isAnonymousUser as t, resolveAuth as u, refreshSession as v, actorOf as y };
|