@augmenting-integrations/auth 4.2.0 → 5.0.1
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/dist/client/AppUserProvider.cjs +73 -0
- package/dist/client/AppUserProvider.cjs.map +1 -0
- package/dist/client/AppUserProvider.d.ts +49 -0
- package/dist/client/AppUserProvider.d.ts.map +1 -0
- package/dist/client/AppUserProvider.js +36 -0
- package/dist/client/AppUserProvider.js.map +1 -0
- package/dist/client/ImpersonationBanner.cjs +103 -0
- package/dist/client/ImpersonationBanner.cjs.map +1 -0
- package/dist/client/ImpersonationBanner.d.ts +9 -0
- package/dist/client/ImpersonationBanner.d.ts.map +1 -0
- package/dist/client/ImpersonationBanner.js +69 -0
- package/dist/client/ImpersonationBanner.js.map +1 -0
- package/dist/client/SignOutButton.cjs +59 -0
- package/dist/client/SignOutButton.cjs.map +1 -0
- package/dist/client/SignOutButton.d.ts +10 -0
- package/dist/client/SignOutButton.d.ts.map +1 -0
- package/dist/client/SignOutButton.js +35 -0
- package/dist/client/SignOutButton.js.map +1 -0
- package/dist/client/UserMenu.cjs +54 -0
- package/dist/client/UserMenu.cjs.map +1 -0
- package/dist/client/UserMenu.d.ts +6 -0
- package/dist/client/UserMenu.d.ts.map +1 -0
- package/dist/client/UserMenu.js +30 -0
- package/dist/client/UserMenu.js.map +1 -0
- package/dist/client/use-impersonation.cjs +77 -0
- package/dist/client/use-impersonation.cjs.map +1 -0
- package/dist/client/use-impersonation.d.ts +39 -0
- package/dist/client/use-impersonation.d.ts.map +1 -0
- package/dist/client/use-impersonation.js +43 -0
- package/dist/client/use-impersonation.js.map +1 -0
- package/dist/client.cjs +47 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +23 -0
- package/dist/client.js.map +1 -0
- package/dist/{index.d.ts → server/createAuth.d.ts} +1 -1
- package/dist/server/createAuth.d.ts.map +1 -0
- package/dist/server/impersonation.d.ts +23 -0
- package/dist/server/impersonation.d.ts.map +1 -0
- package/dist/server/jit.d.ts +97 -0
- package/dist/server/jit.d.ts.map +1 -0
- package/dist/{index.cjs → server.cjs} +155 -7
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/{index.js → server.js} +144 -3
- package/dist/server.js.map +1 -0
- package/package.json +22 -11
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var use_impersonation_exports = {};
|
|
31
|
+
__export(use_impersonation_exports, {
|
|
32
|
+
useImpersonation: () => useImpersonation
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(use_impersonation_exports);
|
|
35
|
+
var React = __toESM(require("react"));
|
|
36
|
+
function useImpersonation() {
|
|
37
|
+
const [state, setState] = React.useState({
|
|
38
|
+
status: "loading",
|
|
39
|
+
data: null,
|
|
40
|
+
error: null
|
|
41
|
+
});
|
|
42
|
+
const refresh = React.useCallback(async () => {
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch("/api/auth/me", {
|
|
45
|
+
credentials: "same-origin",
|
|
46
|
+
cache: "no-store"
|
|
47
|
+
});
|
|
48
|
+
if (res.status === 401) {
|
|
49
|
+
setState({ status: "anonymous", data: null, error: null });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const txt = await res.text().catch(() => "");
|
|
54
|
+
setState({
|
|
55
|
+
status: "error",
|
|
56
|
+
data: null,
|
|
57
|
+
error: txt || `HTTP ${res.status}`
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const data = await res.json();
|
|
62
|
+
setState({ status: "ready", data, error: null });
|
|
63
|
+
} catch (err) {
|
|
64
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
65
|
+
setState({ status: "error", data: null, error: message });
|
|
66
|
+
}
|
|
67
|
+
}, []);
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
void refresh();
|
|
70
|
+
}, [refresh]);
|
|
71
|
+
return { ...state, refresh };
|
|
72
|
+
}
|
|
73
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
74
|
+
0 && (module.exports = {
|
|
75
|
+
useImpersonation
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=use-impersonation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/use-impersonation.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n// =============================================================================\n// useImpersonation -- surfaces the *effective* user via /api/auth/me.\n//\n// Used by ImpersonationBanner + admin surfaces to know whether the current\n// session is impersonated. Hits the spoke's /api/auth/me which returns\n// `{user, impersonatedBy}`.\n//\n// Deliberately doesn't lean on TanStack Query so this can be imported from\n// any client component without forcing a QueryClient provider. Refetch is\n// manual via `refresh()`, which the impersonate API mutators call after a\n// successful start / stop.\n// =============================================================================\n\nexport type EffectiveUser = {\n id: string;\n email: string;\n name: string;\n role: string;\n is_active: boolean;\n credit_balance: number;\n};\n\nexport type ImpersonatedBy = {\n id: string;\n name: string;\n email: string;\n};\n\nexport type MeResponse = {\n user: EffectiveUser;\n impersonatedBy: ImpersonatedBy | null;\n};\n\ntype State =\n | { status: \"loading\"; data: null; error: null }\n | { status: \"anonymous\"; data: null; error: null }\n | { status: \"ready\"; data: MeResponse; error: null }\n | { status: \"error\"; data: null; error: string };\n\nexport function useImpersonation(): State & { refresh: () => Promise<void> } {\n const [state, setState] = React.useState<State>({\n status: \"loading\",\n data: null,\n error: null,\n });\n\n const refresh = React.useCallback(async () => {\n try {\n const res = await fetch(\"/api/auth/me\", {\n credentials: \"same-origin\",\n cache: \"no-store\",\n });\n if (res.status === 401) {\n setState({ status: \"anonymous\", data: null, error: null });\n return;\n }\n if (!res.ok) {\n const txt = await res.text().catch(() => \"\");\n setState({\n status: \"error\",\n data: null,\n error: txt || `HTTP ${res.status}`,\n });\n return;\n }\n const data = (await res.json()) as MeResponse;\n setState({ status: \"ready\", data, error: null });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n setState({ status: \"error\", data: null, error: message });\n }\n }, []);\n\n React.useEffect(() => {\n void refresh();\n }, [refresh]);\n\n return { ...state, refresh };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,YAAuB;AAyChB,SAAS,mBAA6D;AAC3E,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAgB;AAAA,IAC9C,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,EACT,CAAC;AAED,QAAM,UAAU,MAAM,YAAY,YAAY;AAC5C,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,gBAAgB;AAAA,QACtC,aAAa;AAAA,QACb,OAAO;AAAA,MACT,CAAC;AACD,UAAI,IAAI,WAAW,KAAK;AACtB,iBAAS,EAAE,QAAQ,aAAa,MAAM,MAAM,OAAO,KAAK,CAAC;AACzD;AAAA,MACF;AACA,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC3C,iBAAS;AAAA,UACP,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO,OAAO,QAAQ,IAAI,MAAM;AAAA,QAClC,CAAC;AACD;AAAA,MACF;AACA,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,eAAS,EAAE,QAAQ,SAAS,MAAM,OAAO,KAAK,CAAC;AAAA,IACjD,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,eAAS,EAAE,QAAQ,SAAS,MAAM,MAAM,OAAO,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM;AACpB,SAAK,QAAQ;AAAA,EACf,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,GAAG,OAAO,QAAQ;AAC7B;","names":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type EffectiveUser = {
|
|
2
|
+
id: string;
|
|
3
|
+
email: string;
|
|
4
|
+
name: string;
|
|
5
|
+
role: string;
|
|
6
|
+
is_active: boolean;
|
|
7
|
+
credit_balance: number;
|
|
8
|
+
};
|
|
9
|
+
export type ImpersonatedBy = {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
email: string;
|
|
13
|
+
};
|
|
14
|
+
export type MeResponse = {
|
|
15
|
+
user: EffectiveUser;
|
|
16
|
+
impersonatedBy: ImpersonatedBy | null;
|
|
17
|
+
};
|
|
18
|
+
type State = {
|
|
19
|
+
status: "loading";
|
|
20
|
+
data: null;
|
|
21
|
+
error: null;
|
|
22
|
+
} | {
|
|
23
|
+
status: "anonymous";
|
|
24
|
+
data: null;
|
|
25
|
+
error: null;
|
|
26
|
+
} | {
|
|
27
|
+
status: "ready";
|
|
28
|
+
data: MeResponse;
|
|
29
|
+
error: null;
|
|
30
|
+
} | {
|
|
31
|
+
status: "error";
|
|
32
|
+
data: null;
|
|
33
|
+
error: string;
|
|
34
|
+
};
|
|
35
|
+
export declare function useImpersonation(): State & {
|
|
36
|
+
refresh: () => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=use-impersonation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-impersonation.d.ts","sourceRoot":"","sources":["../../src/client/use-impersonation.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,aAAa,CAAC;IACpB,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,KAAK,KAAK,GACN;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAC9C;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAChD;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAClD;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,gBAAgB,IAAI,KAAK,GAAG;IAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAuC3E"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function useImpersonation() {
|
|
4
|
+
const [state, setState] = React.useState({
|
|
5
|
+
status: "loading",
|
|
6
|
+
data: null,
|
|
7
|
+
error: null
|
|
8
|
+
});
|
|
9
|
+
const refresh = React.useCallback(async () => {
|
|
10
|
+
try {
|
|
11
|
+
const res = await fetch("/api/auth/me", {
|
|
12
|
+
credentials: "same-origin",
|
|
13
|
+
cache: "no-store"
|
|
14
|
+
});
|
|
15
|
+
if (res.status === 401) {
|
|
16
|
+
setState({ status: "anonymous", data: null, error: null });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
const txt = await res.text().catch(() => "");
|
|
21
|
+
setState({
|
|
22
|
+
status: "error",
|
|
23
|
+
data: null,
|
|
24
|
+
error: txt || `HTTP ${res.status}`
|
|
25
|
+
});
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const data = await res.json();
|
|
29
|
+
setState({ status: "ready", data, error: null });
|
|
30
|
+
} catch (err) {
|
|
31
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32
|
+
setState({ status: "error", data: null, error: message });
|
|
33
|
+
}
|
|
34
|
+
}, []);
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
void refresh();
|
|
37
|
+
}, [refresh]);
|
|
38
|
+
return { ...state, refresh };
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
useImpersonation
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=use-impersonation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/use-impersonation.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n// =============================================================================\n// useImpersonation -- surfaces the *effective* user via /api/auth/me.\n//\n// Used by ImpersonationBanner + admin surfaces to know whether the current\n// session is impersonated. Hits the spoke's /api/auth/me which returns\n// `{user, impersonatedBy}`.\n//\n// Deliberately doesn't lean on TanStack Query so this can be imported from\n// any client component without forcing a QueryClient provider. Refetch is\n// manual via `refresh()`, which the impersonate API mutators call after a\n// successful start / stop.\n// =============================================================================\n\nexport type EffectiveUser = {\n id: string;\n email: string;\n name: string;\n role: string;\n is_active: boolean;\n credit_balance: number;\n};\n\nexport type ImpersonatedBy = {\n id: string;\n name: string;\n email: string;\n};\n\nexport type MeResponse = {\n user: EffectiveUser;\n impersonatedBy: ImpersonatedBy | null;\n};\n\ntype State =\n | { status: \"loading\"; data: null; error: null }\n | { status: \"anonymous\"; data: null; error: null }\n | { status: \"ready\"; data: MeResponse; error: null }\n | { status: \"error\"; data: null; error: string };\n\nexport function useImpersonation(): State & { refresh: () => Promise<void> } {\n const [state, setState] = React.useState<State>({\n status: \"loading\",\n data: null,\n error: null,\n });\n\n const refresh = React.useCallback(async () => {\n try {\n const res = await fetch(\"/api/auth/me\", {\n credentials: \"same-origin\",\n cache: \"no-store\",\n });\n if (res.status === 401) {\n setState({ status: \"anonymous\", data: null, error: null });\n return;\n }\n if (!res.ok) {\n const txt = await res.text().catch(() => \"\");\n setState({\n status: \"error\",\n data: null,\n error: txt || `HTTP ${res.status}`,\n });\n return;\n }\n const data = (await res.json()) as MeResponse;\n setState({ status: \"ready\", data, error: null });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n setState({ status: \"error\", data: null, error: message });\n }\n }, []);\n\n React.useEffect(() => {\n void refresh();\n }, [refresh]);\n\n return { ...state, refresh };\n}\n"],"mappings":";AAEA,YAAY,WAAW;AAyChB,SAAS,mBAA6D;AAC3E,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAgB;AAAA,IAC9C,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,EACT,CAAC;AAED,QAAM,UAAU,MAAM,YAAY,YAAY;AAC5C,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,gBAAgB;AAAA,QACtC,aAAa;AAAA,QACb,OAAO;AAAA,MACT,CAAC;AACD,UAAI,IAAI,WAAW,KAAK;AACtB,iBAAS,EAAE,QAAQ,aAAa,MAAM,MAAM,OAAO,KAAK,CAAC;AACzD;AAAA,MACF;AACA,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC3C,iBAAS;AAAA,UACP,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,OAAO,OAAO,QAAQ,IAAI,MAAM;AAAA,QAClC,CAAC;AACD;AAAA,MACF;AACA,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,eAAS,EAAE,QAAQ,SAAS,MAAM,OAAO,KAAK,CAAC;AAAA,IACjD,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,eAAS,EAAE,QAAQ,SAAS,MAAM,MAAM,OAAO,QAAQ,CAAC;AAAA,IAC1D;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,MAAM;AACpB,SAAK,QAAQ;AAAA,EACf,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,GAAG,OAAO,QAAQ;AAC7B;","names":[]}
|
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var client_exports = {};
|
|
20
|
+
__export(client_exports, {
|
|
21
|
+
AppUserProvider: () => import_AppUserProvider.AppUserProvider,
|
|
22
|
+
ImpersonationBanner: () => import_ImpersonationBanner.ImpersonationBanner,
|
|
23
|
+
SignOutButton: () => import_SignOutButton.SignOutButton,
|
|
24
|
+
UserMenu: () => import_UserMenu.UserMenu,
|
|
25
|
+
useAppUser: () => import_AppUserProvider.useAppUser,
|
|
26
|
+
useDbAppUser: () => import_AppUserProvider.useDbAppUser,
|
|
27
|
+
useImpersonation: () => import_use_impersonation.useImpersonation,
|
|
28
|
+
useRole: () => import_AppUserProvider.useRole
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(client_exports);
|
|
31
|
+
var import_AppUserProvider = require("./client/AppUserProvider.js");
|
|
32
|
+
var import_UserMenu = require("./client/UserMenu.js");
|
|
33
|
+
var import_SignOutButton = require("./client/SignOutButton.js");
|
|
34
|
+
var import_ImpersonationBanner = require("./client/ImpersonationBanner.js");
|
|
35
|
+
var import_use_impersonation = require("./client/use-impersonation.js");
|
|
36
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
37
|
+
0 && (module.exports = {
|
|
38
|
+
AppUserProvider,
|
|
39
|
+
ImpersonationBanner,
|
|
40
|
+
SignOutButton,
|
|
41
|
+
UserMenu,
|
|
42
|
+
useAppUser,
|
|
43
|
+
useDbAppUser,
|
|
44
|
+
useImpersonation,
|
|
45
|
+
useRole
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["export {\n AppUserProvider,\n useAppUser,\n useDbAppUser,\n useRole,\n type AppUserState,\n type DbAppUser,\n type SessionAppUser,\n} from \"./client/AppUserProvider.js\";\nexport { UserMenu } from \"./client/UserMenu.js\";\nexport { SignOutButton } from \"./client/SignOutButton.js\";\nexport { ImpersonationBanner } from \"./client/ImpersonationBanner.js\";\nexport {\n useImpersonation,\n type EffectiveUser,\n type ImpersonatedBy,\n type MeResponse,\n} from \"./client/use-impersonation.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAQO;AACP,sBAAyB;AACzB,2BAA8B;AAC9B,iCAAoC;AACpC,+BAKO;","names":[]}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AppUserProvider, useAppUser, useDbAppUser, useRole, type AppUserState, type DbAppUser, type SessionAppUser, } from "./client/AppUserProvider.js";
|
|
2
|
+
export { UserMenu } from "./client/UserMenu.js";
|
|
3
|
+
export { SignOutButton } from "./client/SignOutButton.js";
|
|
4
|
+
export { ImpersonationBanner } from "./client/ImpersonationBanner.js";
|
|
5
|
+
export { useImpersonation, type EffectiveUser, type ImpersonatedBy, type MeResponse, } from "./client/use-impersonation.js";
|
|
6
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,cAAc,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EACL,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,+BAA+B,CAAC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppUserProvider,
|
|
3
|
+
useAppUser,
|
|
4
|
+
useDbAppUser,
|
|
5
|
+
useRole
|
|
6
|
+
} from "./client/AppUserProvider.js";
|
|
7
|
+
import { UserMenu } from "./client/UserMenu.js";
|
|
8
|
+
import { SignOutButton } from "./client/SignOutButton.js";
|
|
9
|
+
import { ImpersonationBanner } from "./client/ImpersonationBanner.js";
|
|
10
|
+
import {
|
|
11
|
+
useImpersonation
|
|
12
|
+
} from "./client/use-impersonation.js";
|
|
13
|
+
export {
|
|
14
|
+
AppUserProvider,
|
|
15
|
+
ImpersonationBanner,
|
|
16
|
+
SignOutButton,
|
|
17
|
+
UserMenu,
|
|
18
|
+
useAppUser,
|
|
19
|
+
useDbAppUser,
|
|
20
|
+
useImpersonation,
|
|
21
|
+
useRole
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["export {\n AppUserProvider,\n useAppUser,\n useDbAppUser,\n useRole,\n type AppUserState,\n type DbAppUser,\n type SessionAppUser,\n} from \"./client/AppUserProvider.js\";\nexport { UserMenu } from \"./client/UserMenu.js\";\nexport { SignOutButton } from \"./client/SignOutButton.js\";\nexport { ImpersonationBanner } from \"./client/ImpersonationBanner.js\";\nexport {\n useImpersonation,\n type EffectiveUser,\n type ImpersonatedBy,\n type MeResponse,\n} from \"./client/use-impersonation.js\";\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP,SAAS,gBAAgB;AACzB,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,OAIK;","names":[]}
|
|
@@ -70,4 +70,4 @@ export declare function hasGroup(session: Session | null | undefined, name: stri
|
|
|
70
70
|
export declare function requireGroup(session: Session | null | undefined, ...names: string[]): void;
|
|
71
71
|
export declare function createAuth(opts: CreateAuthOptions): import("next-auth").NextAuthResult;
|
|
72
72
|
export type { NextAuthConfig } from "next-auth";
|
|
73
|
-
//# sourceMappingURL=
|
|
73
|
+
//# sourceMappingURL=createAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAuth.d.ts","sourceRoot":"","sources":["../../src/server/createAuth.ts"],"names":[],"mappings":"AAkBA,OAAiB,EACf,KAAK,cAAc,EAEnB,KAAK,OAAO,EACb,MAAM,WAAW,CAAC;AAInB,OAAO,QAAQ,WAAW,CAAC;IACzB,UAAU,OAAO;QACf,IAAI,EAAE;YACJ,MAAM,EAAE,MAAM,EAAE,CAAC;YACjB,IAAI,EAAE,MAAM,CAAC;SACd,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;KAC5B;IACD,UAAU,IAAI;QACZ,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;KACnB;CACF;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2DAA2D;IAC3D,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAIF,qBAAa,SAAU,SAAQ,KAAK;IACf,IAAI,EAAE,iBAAiB,GAAG,WAAW;gBAArC,IAAI,EAAE,iBAAiB,GAAG,WAAW;CAIzD;AAID,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAE3E;AAED,+CAA+C;AAC/C,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAInF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,EACnC,GAAG,KAAK,EAAE,MAAM,EAAE,GACjB,IAAI,CAKN;AAyFD,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,sCAgJjD;AAED,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
export declare const IMPERSONATE_COOKIE_NAME = "__impersonate";
|
|
3
|
+
export declare const IMPERSONATE_TTL_SECONDS = 3600;
|
|
4
|
+
export type ImpersonationClaims = {
|
|
5
|
+
/** Admin user id who started the impersonation (stringified BigInt). */
|
|
6
|
+
impersonatedBy: string;
|
|
7
|
+
/** Target user id being impersonated (stringified BigInt). */
|
|
8
|
+
sub: string;
|
|
9
|
+
/** Issued-at (seconds since epoch). */
|
|
10
|
+
iat: number;
|
|
11
|
+
/** Expiry (seconds since epoch). */
|
|
12
|
+
exp: number;
|
|
13
|
+
};
|
|
14
|
+
export declare function mintImpersonationToken(args: {
|
|
15
|
+
adminId: bigint | string;
|
|
16
|
+
targetId: bigint | string;
|
|
17
|
+
now?: Date;
|
|
18
|
+
}): Promise<{
|
|
19
|
+
token: string;
|
|
20
|
+
expiresAt: Date;
|
|
21
|
+
}>;
|
|
22
|
+
export declare function verifyImpersonationToken(token: string): Promise<ImpersonationClaims | null>;
|
|
23
|
+
//# sourceMappingURL=impersonation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impersonation.d.ts","sourceRoot":"","sources":["../../src/server/impersonation.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAqBrB,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AACvD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAG5C,MAAM,MAAM,mBAAmB,GAAG;IAChC,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAkBF,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAgB9C;AAED,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CA0BrC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import type { Session } from "next-auth";
|
|
3
|
+
/**
|
|
4
|
+
* Minimum contract every spoke User row must satisfy. Spokes can widen this
|
|
5
|
+
* with additional fields (credit_balance, must_change_password, etc.) and the
|
|
6
|
+
* factory will preserve them through the returned `Promise<TUser>`.
|
|
7
|
+
*/
|
|
8
|
+
export type BaseAppUser = {
|
|
9
|
+
id: bigint | string | number;
|
|
10
|
+
email: string;
|
|
11
|
+
name: string;
|
|
12
|
+
role: string;
|
|
13
|
+
parent_id: bigint | string | number | null;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Loose typing for the Prisma delegates the factory touches. Each spoke has
|
|
17
|
+
* its own generated client whose actual types are concrete; we use loose
|
|
18
|
+
* shapes here so the factory works with any spoke's schema.
|
|
19
|
+
*/
|
|
20
|
+
export type PrismaLikeUserDelegate<TUser> = {
|
|
21
|
+
findUnique: (args: {
|
|
22
|
+
where: {
|
|
23
|
+
id?: unknown;
|
|
24
|
+
email?: string;
|
|
25
|
+
};
|
|
26
|
+
}) => Promise<TUser | null>;
|
|
27
|
+
create: (args: {
|
|
28
|
+
data: unknown;
|
|
29
|
+
}) => Promise<TUser>;
|
|
30
|
+
};
|
|
31
|
+
export type PrismaLikeInvitationDelegate = {
|
|
32
|
+
findFirst: (args: {
|
|
33
|
+
where: {
|
|
34
|
+
email: string;
|
|
35
|
+
accepted_at: null;
|
|
36
|
+
expires_at: {
|
|
37
|
+
gt: Date;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
orderBy?: unknown;
|
|
41
|
+
}) => Promise<{
|
|
42
|
+
id: bigint | string | number;
|
|
43
|
+
intended_role: string;
|
|
44
|
+
parent_id: bigint | string | number | null;
|
|
45
|
+
} | null>;
|
|
46
|
+
update: (args: {
|
|
47
|
+
where: {
|
|
48
|
+
id: unknown;
|
|
49
|
+
};
|
|
50
|
+
data: {
|
|
51
|
+
accepted_at: Date;
|
|
52
|
+
accepted_by_user_id: unknown;
|
|
53
|
+
};
|
|
54
|
+
}) => Promise<unknown>;
|
|
55
|
+
};
|
|
56
|
+
export type PrismaLikeClient<TUser> = {
|
|
57
|
+
user: PrismaLikeUserDelegate<TUser>;
|
|
58
|
+
invitation: PrismaLikeInvitationDelegate;
|
|
59
|
+
$transaction: <T>(fn: (tx: {
|
|
60
|
+
user: PrismaLikeUserDelegate<TUser>;
|
|
61
|
+
invitation: PrismaLikeInvitationDelegate;
|
|
62
|
+
}) => Promise<T>) => Promise<T>;
|
|
63
|
+
};
|
|
64
|
+
export type CreateGetOrCreateAppUserOptions<TUser extends BaseAppUser> = {
|
|
65
|
+
/** Returns the spoke's PrismaClient (lazily). */
|
|
66
|
+
db: () => Promise<PrismaLikeClient<TUser>>;
|
|
67
|
+
/** Fallback role when no admin email + no Cognito groups. */
|
|
68
|
+
defaultRole: string;
|
|
69
|
+
/** Starting credit balance per role. */
|
|
70
|
+
computeCreditBalance: (role: string) => number;
|
|
71
|
+
/** Emails auto-promoted to "admin" role on first sign-in (case-insensitive). */
|
|
72
|
+
adminEmails?: string[];
|
|
73
|
+
/**
|
|
74
|
+
* Hash value written to User.password on creation. Schema-inherited
|
|
75
|
+
* not-null constraint; never used to authenticate (Cognito does that).
|
|
76
|
+
* Default: a recognizable placeholder string.
|
|
77
|
+
*/
|
|
78
|
+
placeholderPasswordHash?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Extra column values written on creation. Use this for spoke-specific
|
|
81
|
+
* defaults (e.g. is_active: true, must_change_password: false).
|
|
82
|
+
*/
|
|
83
|
+
extraCreateFields?: Record<string, unknown>;
|
|
84
|
+
};
|
|
85
|
+
export type AppUserWithImpersonation<TUser extends BaseAppUser> = TUser & {
|
|
86
|
+
/** Stringified admin id when this session is impersonated; absent otherwise. */
|
|
87
|
+
impersonatedBy?: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Build a `getOrCreateAppUser(session)` function configured for this spoke.
|
|
91
|
+
*
|
|
92
|
+
* Returned function is idempotent: subsequent calls with the same email
|
|
93
|
+
* return the existing row. First-time emails are created inside a transaction
|
|
94
|
+
* that also auto-accepts a matching Invitation row if present.
|
|
95
|
+
*/
|
|
96
|
+
export declare function createGetOrCreateAppUser<TUser extends BaseAppUser>(opts: CreateGetOrCreateAppUserOptions<TUser>): (session: Session) => Promise<AppUserWithImpersonation<TUser>>;
|
|
97
|
+
//# sourceMappingURL=jit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jit.d.ts","sourceRoot":"","sources":["../../src/server/jit.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,16 +27,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
|
-
// src/
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
30
|
+
// src/server.ts
|
|
31
|
+
var server_exports = {};
|
|
32
|
+
__export(server_exports, {
|
|
33
33
|
AuthError: () => AuthError,
|
|
34
|
+
IMPERSONATE_COOKIE_NAME: () => IMPERSONATE_COOKIE_NAME,
|
|
35
|
+
IMPERSONATE_TTL_SECONDS: () => IMPERSONATE_TTL_SECONDS,
|
|
34
36
|
createAuth: () => createAuth,
|
|
37
|
+
createGetOrCreateAppUser: () => createGetOrCreateAppUser,
|
|
35
38
|
getUserGroups: () => getUserGroups,
|
|
36
39
|
hasGroup: () => hasGroup,
|
|
37
|
-
|
|
40
|
+
mintImpersonationToken: () => mintImpersonationToken,
|
|
41
|
+
requireGroup: () => requireGroup,
|
|
42
|
+
verifyImpersonationToken: () => verifyImpersonationToken
|
|
38
43
|
});
|
|
39
|
-
module.exports = __toCommonJS(
|
|
44
|
+
module.exports = __toCommonJS(server_exports);
|
|
45
|
+
|
|
46
|
+
// src/server/createAuth.ts
|
|
40
47
|
var import_next_auth = __toESM(require("next-auth"));
|
|
41
48
|
var import_credentials = __toESM(require("next-auth/providers/credentials"));
|
|
42
49
|
var import_cognito = __toESM(require("next-auth/providers/cognito"));
|
|
@@ -233,12 +240,153 @@ function createAuth(opts) {
|
|
|
233
240
|
};
|
|
234
241
|
return (0, import_next_auth.default)(config);
|
|
235
242
|
}
|
|
243
|
+
|
|
244
|
+
// src/server/jit.ts
|
|
245
|
+
var import_server_only2 = require("server-only");
|
|
246
|
+
var import_headers = require("next/headers");
|
|
247
|
+
|
|
248
|
+
// src/server/impersonation.ts
|
|
249
|
+
var import_server_only = require("server-only");
|
|
250
|
+
var import_jwt = require("next-auth/jwt");
|
|
251
|
+
var import_server = require("@augmenting-integrations/aws/server");
|
|
252
|
+
var IMPERSONATE_COOKIE_NAME = "__impersonate";
|
|
253
|
+
var IMPERSONATE_TTL_SECONDS = 3600;
|
|
254
|
+
var IMPERSONATE_JWT_SALT = "impersonate.v1";
|
|
255
|
+
var cachedSecret = null;
|
|
256
|
+
async function getAuthSecret() {
|
|
257
|
+
if (cachedSecret) return cachedSecret;
|
|
258
|
+
const arn = process.env.AUTH_SECRET_ARN;
|
|
259
|
+
const fromSm = arn ? await (0, import_server.getSecret)(arn) : null;
|
|
260
|
+
const secret = fromSm ?? process.env.AUTH_SECRET;
|
|
261
|
+
if (!secret) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
"AUTH_SECRET (or AUTH_SECRET_ARN) must be set to mint/verify impersonation tokens"
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
cachedSecret = secret;
|
|
267
|
+
return secret;
|
|
268
|
+
}
|
|
269
|
+
async function mintImpersonationToken(args) {
|
|
270
|
+
const secret = await getAuthSecret();
|
|
271
|
+
const nowSec = Math.floor((args.now?.getTime() ?? Date.now()) / 1e3);
|
|
272
|
+
const exp = nowSec + IMPERSONATE_TTL_SECONDS;
|
|
273
|
+
const token = await (0, import_jwt.encode)({
|
|
274
|
+
secret,
|
|
275
|
+
salt: IMPERSONATE_JWT_SALT,
|
|
276
|
+
maxAge: IMPERSONATE_TTL_SECONDS,
|
|
277
|
+
token: {
|
|
278
|
+
impersonatedBy: String(args.adminId),
|
|
279
|
+
sub: String(args.targetId),
|
|
280
|
+
iat: nowSec,
|
|
281
|
+
exp
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
return { token, expiresAt: new Date(exp * 1e3) };
|
|
285
|
+
}
|
|
286
|
+
async function verifyImpersonationToken(token) {
|
|
287
|
+
try {
|
|
288
|
+
const secret = await getAuthSecret();
|
|
289
|
+
const decoded = await (0, import_jwt.decode)({
|
|
290
|
+
token,
|
|
291
|
+
secret,
|
|
292
|
+
salt: IMPERSONATE_JWT_SALT
|
|
293
|
+
});
|
|
294
|
+
if (!decoded) return null;
|
|
295
|
+
const impersonatedBy = decoded["impersonatedBy"];
|
|
296
|
+
const sub = decoded["sub"];
|
|
297
|
+
const iat = decoded["iat"];
|
|
298
|
+
const exp = decoded["exp"];
|
|
299
|
+
if (typeof impersonatedBy !== "string" || typeof sub !== "string" || typeof iat !== "number" || typeof exp !== "number") {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
if (exp * 1e3 < Date.now()) return null;
|
|
303
|
+
return { impersonatedBy, sub, iat, exp };
|
|
304
|
+
} catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/server/jit.ts
|
|
310
|
+
var DEFAULT_PLACEHOLDER_HASH = "$2y$12$.cognito-managed.never.used-for-login.placeholder";
|
|
311
|
+
function createGetOrCreateAppUser(opts) {
|
|
312
|
+
const adminEmailsLower = (opts.adminEmails ?? []).map((s) => s.toLowerCase());
|
|
313
|
+
const placeholder = opts.placeholderPasswordHash ?? DEFAULT_PLACEHOLDER_HASH;
|
|
314
|
+
return async function getOrCreateAppUser(session) {
|
|
315
|
+
const email = session.user?.email;
|
|
316
|
+
if (!email) {
|
|
317
|
+
throw new Error("getOrCreateAppUser called with a session that has no user.email");
|
|
318
|
+
}
|
|
319
|
+
const db = await opts.db();
|
|
320
|
+
try {
|
|
321
|
+
const cookieStore = await (0, import_headers.cookies)();
|
|
322
|
+
const cookie = cookieStore.get(IMPERSONATE_COOKIE_NAME);
|
|
323
|
+
if (cookie?.value) {
|
|
324
|
+
const claims = await verifyImpersonationToken(cookie.value);
|
|
325
|
+
if (claims) {
|
|
326
|
+
const [admin, target] = await Promise.all([
|
|
327
|
+
db.user.findUnique({ where: { id: claims.impersonatedBy } }),
|
|
328
|
+
db.user.findUnique({ where: { id: claims.sub } })
|
|
329
|
+
]);
|
|
330
|
+
if (admin && admin.role === "admin" && target) {
|
|
331
|
+
return Object.assign(target, {
|
|
332
|
+
impersonatedBy: claims.impersonatedBy
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
339
|
+
const existing = await db.user.findUnique({ where: { email } });
|
|
340
|
+
if (existing) return existing;
|
|
341
|
+
const groups = session.user.groups ?? [];
|
|
342
|
+
const fallbackRole = adminEmailsLower.includes(email.toLowerCase()) ? "admin" : groups[0] ?? opts.defaultRole;
|
|
343
|
+
const name = session.user.name ?? email.split("@")[0];
|
|
344
|
+
return db.$transaction(async (tx) => {
|
|
345
|
+
const pendingInvite = await tx.invitation.findFirst({
|
|
346
|
+
where: {
|
|
347
|
+
email,
|
|
348
|
+
accepted_at: null,
|
|
349
|
+
expires_at: { gt: /* @__PURE__ */ new Date() }
|
|
350
|
+
},
|
|
351
|
+
orderBy: { created_at: "desc" }
|
|
352
|
+
});
|
|
353
|
+
const role = pendingInvite ? pendingInvite.intended_role : fallbackRole;
|
|
354
|
+
const parent_id = pendingInvite ? pendingInvite.parent_id : null;
|
|
355
|
+
const created = await tx.user.create({
|
|
356
|
+
data: {
|
|
357
|
+
email,
|
|
358
|
+
name,
|
|
359
|
+
role,
|
|
360
|
+
parent_id,
|
|
361
|
+
password: placeholder,
|
|
362
|
+
credit_balance: opts.computeCreditBalance(role),
|
|
363
|
+
...opts.extraCreateFields ?? {}
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
if (pendingInvite) {
|
|
367
|
+
await tx.invitation.update({
|
|
368
|
+
where: { id: pendingInvite.id },
|
|
369
|
+
data: {
|
|
370
|
+
accepted_at: /* @__PURE__ */ new Date(),
|
|
371
|
+
accepted_by_user_id: created.id
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
return created;
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
}
|
|
236
379
|
// Annotate the CommonJS export names for ESM import in node:
|
|
237
380
|
0 && (module.exports = {
|
|
238
381
|
AuthError,
|
|
382
|
+
IMPERSONATE_COOKIE_NAME,
|
|
383
|
+
IMPERSONATE_TTL_SECONDS,
|
|
239
384
|
createAuth,
|
|
385
|
+
createGetOrCreateAppUser,
|
|
240
386
|
getUserGroups,
|
|
241
387
|
hasGroup,
|
|
242
|
-
|
|
388
|
+
mintImpersonationToken,
|
|
389
|
+
requireGroup,
|
|
390
|
+
verifyImpersonationToken
|
|
243
391
|
});
|
|
244
|
-
//# sourceMappingURL=
|
|
392
|
+
//# sourceMappingURL=server.cjs.map
|