cauthy-auth 0.5.2 → 0.9.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 +68 -3
- package/dist/{cauthy-BCTCqubt.d.ts → cauthy-Bn3iv50y.d.ts} +1 -1
- package/dist/{cauthy-hswMoS7B.d.mts → cauthy-C29HCvag.d.mts} +1 -1
- package/dist/cli.js +490 -25
- package/dist/client.d.mts +273 -0
- package/dist/client.d.ts +273 -0
- package/dist/client.js +342 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +308 -0
- package/dist/client.mjs.map +1 -0
- package/dist/express.d.mts +24 -0
- package/dist/express.d.ts +24 -0
- package/dist/express.js +102 -0
- package/dist/express.js.map +1 -0
- package/dist/express.mjs +76 -0
- package/dist/express.mjs.map +1 -0
- package/dist/fastify.d.mts +2 -2
- package/dist/fastify.d.ts +2 -2
- package/dist/fastify.js +3 -1
- package/dist/fastify.js.map +1 -1
- package/dist/fastify.mjs +3 -1
- package/dist/fastify.mjs.map +1 -1
- package/dist/hono.d.mts +23 -0
- package/dist/hono.d.ts +23 -0
- package/dist/hono.js +50 -0
- package/dist/hono.js.map +1 -0
- package/dist/hono.mjs +24 -0
- package/dist/hono.mjs.map +1 -0
- package/dist/index.d.mts +44 -6
- package/dist/index.d.ts +44 -6
- package/dist/index.js +713 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +701 -37
- package/dist/index.mjs.map +1 -1
- package/dist/next.d.mts +24 -0
- package/dist/next.d.ts +24 -0
- package/dist/next.js +77 -0
- package/dist/next.js.map +1 -0
- package/dist/next.mjs +52 -0
- package/dist/next.mjs.map +1 -0
- package/dist/react.d.mts +592 -7
- package/dist/react.d.ts +592 -7
- package/dist/react.js +1025 -14
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1013 -10
- package/dist/react.mjs.map +1 -1
- package/dist/styles.css +271 -0
- package/dist/svelte.d.mts +26 -0
- package/dist/svelte.d.ts +26 -0
- package/dist/svelte.js +413 -0
- package/dist/svelte.js.map +1 -0
- package/dist/svelte.mjs +384 -0
- package/dist/svelte.mjs.map +1 -0
- package/dist/{system-DvcQjORr.d.ts → system-DhRr_WxR.d.ts} +139 -1
- package/dist/{system-CR8p4BjB.d.mts → system-ZD_Wa8Bm.d.mts} +139 -1
- package/dist/theme.d.mts +56 -1
- package/dist/theme.d.ts +56 -1
- package/dist/theme.js +181 -5
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +176 -4
- package/dist/theme.mjs.map +1 -1
- package/dist/validate-B_iC-4y8.d.mts +19 -0
- package/dist/validate-B_iC-4y8.d.ts +19 -0
- package/dist/vue.d.mts +28 -0
- package/dist/vue.d.ts +28 -0
- package/dist/vue.js +431 -0
- package/dist/vue.js.map +1 -0
- package/dist/vue.mjs +402 -0
- package/dist/vue.mjs.map +1 -0
- package/package.json +68 -7
- package/dist/permissions-B-a3eN0h.d.mts +0 -52
- package/dist/permissions-B-a3eN0h.d.ts +0 -52
package/dist/index.js
CHANGED
|
@@ -22,14 +22,26 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
CauthyApiError: () => CauthyApiError,
|
|
24
24
|
CauthyConfigError: () => CauthyConfigError,
|
|
25
|
+
CauthyTokenError: () => CauthyTokenError,
|
|
25
26
|
CauthyWebhookError: () => CauthyWebhookError,
|
|
27
|
+
MIN_PASSWORD: () => MIN_PASSWORD,
|
|
26
28
|
can: () => can,
|
|
29
|
+
checkCode: () => checkCode,
|
|
30
|
+
checkEmail: () => checkEmail,
|
|
31
|
+
checkName: () => checkName,
|
|
32
|
+
checkNewPassword: () => checkNewPassword,
|
|
33
|
+
checkPassword: () => checkPassword,
|
|
27
34
|
connectSystem: () => connectSystem,
|
|
28
35
|
createCauthy: () => createCauthy,
|
|
29
36
|
createSystem: () => createSystem,
|
|
37
|
+
definePolicy: () => definePolicy,
|
|
38
|
+
policySource: () => policySource,
|
|
39
|
+
pullPolicy: () => pullPolicy,
|
|
30
40
|
pushPolicy: () => pushPolicy,
|
|
31
41
|
pushTheme: () => pushTheme,
|
|
32
42
|
themeFromStackmyth: () => themeFromStackmyth,
|
|
43
|
+
tokensFromStackmyth: () => tokensFromStackmyth,
|
|
44
|
+
verifyToken: () => verifyToken,
|
|
33
45
|
verifyWebhook: () => verifyWebhook
|
|
34
46
|
});
|
|
35
47
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -145,10 +157,92 @@ function applies(d, o) {
|
|
|
145
157
|
if (o.mode === "dark" && LIGHT.test(scope)) return false;
|
|
146
158
|
const named = NAMED.exec(scope);
|
|
147
159
|
if (named && !/^(dark|light)$/i.test(named[1]) && named[1] !== o.theme) return false;
|
|
148
|
-
if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host/.test(scope)) return false;
|
|
160
|
+
if (!scope.startsWith("@") && !/:root|^html\b|^body\b|\[data-|^\*$|:host|^\.(dark|light)\b/.test(scope)) return false;
|
|
149
161
|
}
|
|
150
162
|
return true;
|
|
151
163
|
}
|
|
164
|
+
var TOKEN_CHARS = /^[A-Za-z0-9 #%.,()/'"_+*-]+$/;
|
|
165
|
+
var TOKEN_FUNCS = /* @__PURE__ */ new Set([
|
|
166
|
+
"var",
|
|
167
|
+
"calc",
|
|
168
|
+
"min",
|
|
169
|
+
"max",
|
|
170
|
+
"clamp",
|
|
171
|
+
"rgb",
|
|
172
|
+
"rgba",
|
|
173
|
+
"hsl",
|
|
174
|
+
"hsla",
|
|
175
|
+
"hwb",
|
|
176
|
+
"lab",
|
|
177
|
+
"lch",
|
|
178
|
+
"oklab",
|
|
179
|
+
"oklch",
|
|
180
|
+
"color",
|
|
181
|
+
"color-mix",
|
|
182
|
+
"light-dark",
|
|
183
|
+
"cubic-bezier",
|
|
184
|
+
"steps",
|
|
185
|
+
"linear",
|
|
186
|
+
"linear-gradient",
|
|
187
|
+
"radial-gradient",
|
|
188
|
+
"conic-gradient",
|
|
189
|
+
"repeating-linear-gradient",
|
|
190
|
+
"repeating-radial-gradient",
|
|
191
|
+
"drop-shadow",
|
|
192
|
+
"blur",
|
|
193
|
+
"translate",
|
|
194
|
+
"translatex",
|
|
195
|
+
"translatey",
|
|
196
|
+
"scale",
|
|
197
|
+
"rotate"
|
|
198
|
+
]);
|
|
199
|
+
function tokenValue(value) {
|
|
200
|
+
const v = value.replace(/\s+/g, " ").trim();
|
|
201
|
+
if (v === "" || v.length > 400 || !TOKEN_CHARS.test(v)) return null;
|
|
202
|
+
for (const m of v.matchAll(/([A-Za-z-]+)\(/g)) if (!TOKEN_FUNCS.has(m[1].toLowerCase())) return null;
|
|
203
|
+
if (/^\(|[ ,(]\(/.test(v)) return null;
|
|
204
|
+
return v;
|
|
205
|
+
}
|
|
206
|
+
var THEME_SCOPE = /^(?:\s*(?::root|html|body|\[data-(?:theme|mode|color-scheme)(?:=["']?[\w-]+["']?)?\])\s*,?)+$/i;
|
|
207
|
+
function tokensFromStackmyth(css, options = {}) {
|
|
208
|
+
const mode = options.mode ?? "light";
|
|
209
|
+
const all = (Array.isArray(css) ? css : [css]).flatMap(declarations);
|
|
210
|
+
const named = options.theme ?? all.map((d) => d.within.map((w) => NAMED.exec(w)?.[1]).find((n) => n && !/^(dark|light)$/i.test(n))).find(Boolean);
|
|
211
|
+
const vars = /* @__PURE__ */ new Map();
|
|
212
|
+
for (const d of all) {
|
|
213
|
+
if (!applies(d, { mode, theme: named })) continue;
|
|
214
|
+
if (!d.within.every((w) => w.startsWith("@") || THEME_SCOPE.test(w))) continue;
|
|
215
|
+
vars.set(d.name, d.value);
|
|
216
|
+
}
|
|
217
|
+
const inline = (value, depth = 0) => {
|
|
218
|
+
if (depth > 16) return null;
|
|
219
|
+
let failed = false;
|
|
220
|
+
let out2 = value;
|
|
221
|
+
for (let guard = 0; guard < 64; guard++) {
|
|
222
|
+
const next = out2.replace(/var\(\s*(--(?!sm-)[\w-]+)\s*(?:,\s*([^()]*(?:\([^()]*\)[^()]*)*))?\)/, (_m, name, fallback) => {
|
|
223
|
+
const known = vars.get(name);
|
|
224
|
+
const got = known !== void 0 ? inline(known, depth + 1) : fallback !== void 0 ? inline(fallback.trim(), depth + 1) : null;
|
|
225
|
+
if (got === null) failed = true;
|
|
226
|
+
return got ?? "";
|
|
227
|
+
});
|
|
228
|
+
if (failed) return null;
|
|
229
|
+
if (next === out2) break;
|
|
230
|
+
out2 = next;
|
|
231
|
+
}
|
|
232
|
+
return out2;
|
|
233
|
+
};
|
|
234
|
+
const tokens = {};
|
|
235
|
+
const warnings = [];
|
|
236
|
+
for (const [name, raw] of vars) {
|
|
237
|
+
if (!/^--sm-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) continue;
|
|
238
|
+
const written = inline(raw);
|
|
239
|
+
const value = written === null ? null : tokenValue(written);
|
|
240
|
+
if (value === null) warnings.push(`${name}: "${raw.length > 60 ? raw.slice(0, 57) + "\u2026" : raw}" is not a value Cauthy takes`);
|
|
241
|
+
else tokens[name] = value;
|
|
242
|
+
}
|
|
243
|
+
if (Object.keys(tokens).length === 0) warnings.push("no --sm-* token was found in these stylesheets");
|
|
244
|
+
return { tokens, warnings };
|
|
245
|
+
}
|
|
152
246
|
function resolve(value, vars, seen = 0) {
|
|
153
247
|
if (seen > 16) return null;
|
|
154
248
|
let unresolved = false;
|
|
@@ -295,7 +389,8 @@ function toWire(theme) {
|
|
|
295
389
|
panelIconUrl: "panel_icon_url",
|
|
296
390
|
texts: "texts",
|
|
297
391
|
inputBackground: "input_background",
|
|
298
|
-
inputBorder: "input_border"
|
|
392
|
+
inputBorder: "input_border",
|
|
393
|
+
tokens: "tokens"
|
|
299
394
|
};
|
|
300
395
|
const wire = {};
|
|
301
396
|
for (const [k, v] of Object.entries(theme)) {
|
|
@@ -329,18 +424,19 @@ function fromWire(wire) {
|
|
|
329
424
|
panel_icon_url: "panelIconUrl",
|
|
330
425
|
texts: "texts",
|
|
331
426
|
input_background: "inputBackground",
|
|
332
|
-
input_border: "inputBorder"
|
|
427
|
+
input_border: "inputBorder",
|
|
428
|
+
tokens: "tokens"
|
|
333
429
|
};
|
|
334
430
|
const theme = {};
|
|
335
431
|
for (const [k, v] of Object.entries(wire ?? {})) if (names[k] && v !== void 0 && v !== null && v !== "") theme[names[k]] = v;
|
|
336
432
|
return theme;
|
|
337
433
|
}
|
|
338
434
|
async function pushTheme(o) {
|
|
339
|
-
const
|
|
340
|
-
const issuer = (o.issuer ??
|
|
341
|
-
const clientId = o.clientId ??
|
|
342
|
-
const tenant = o.tenant ??
|
|
343
|
-
const token = o.token ??
|
|
435
|
+
const env4 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
436
|
+
const issuer = (o.issuer ?? env4("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
437
|
+
const clientId = o.clientId ?? env4("CAUTHY_CLIENT_ID") ?? "";
|
|
438
|
+
const tenant = o.tenant ?? env4("CAUTHY_TENANT") ?? "";
|
|
439
|
+
const token = o.token ?? env4("CAUTHY_TOKEN") ?? "";
|
|
344
440
|
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_CLIENT_ID: clientId, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
345
441
|
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a theme`);
|
|
346
442
|
}
|
|
@@ -520,6 +616,11 @@ function createSystem(given = {}) {
|
|
|
520
616
|
return fromWire(await data("GET", `/v1/oauth/clients/${id(of)}/theme`));
|
|
521
617
|
}
|
|
522
618
|
},
|
|
619
|
+
policy: {
|
|
620
|
+
async get(system) {
|
|
621
|
+
return (await data("GET", `/v1/systems/${id(system)}/policy`)).roles ?? [];
|
|
622
|
+
}
|
|
623
|
+
},
|
|
523
624
|
async publicJwks() {
|
|
524
625
|
if (!key) return { keys: [] };
|
|
525
626
|
const { kty, n, e } = asJwk(key) ?? await (0, import_jose2.exportJWK)(await signingKey());
|
|
@@ -528,6 +629,45 @@ function createSystem(given = {}) {
|
|
|
528
629
|
};
|
|
529
630
|
}
|
|
530
631
|
|
|
632
|
+
// src/validate.ts
|
|
633
|
+
var MIN_PASSWORD = 8;
|
|
634
|
+
var MAX_PASSWORD = 256;
|
|
635
|
+
var MAX_NAME = 120;
|
|
636
|
+
var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
637
|
+
function checkEmail(value) {
|
|
638
|
+
const v = typeof value === "string" ? value.trim() : "";
|
|
639
|
+
if (v === "") return { field: "email", code: "required", message: "Enter your email address." };
|
|
640
|
+
if (v.length > 254 || !EMAIL.test(v)) return { field: "email", code: "invalid_email", message: "That does not look like an email address." };
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
function checkPassword(value) {
|
|
644
|
+
if (typeof value !== "string" || value === "") return { field: "password", code: "required", message: "Enter your password." };
|
|
645
|
+
if (value.length > MAX_PASSWORD) return { field: "password", code: "too_long", message: "That password is too long." };
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
function checkNewPassword(value) {
|
|
649
|
+
const basic = checkPassword(value);
|
|
650
|
+
if (basic) return basic;
|
|
651
|
+
if (value.length < MIN_PASSWORD) {
|
|
652
|
+
return { field: "password", code: "too_short", message: `Use at least ${MIN_PASSWORD} characters.` };
|
|
653
|
+
}
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
function checkName(value) {
|
|
657
|
+
if (value === void 0 || value === null || value === "") return null;
|
|
658
|
+
if (typeof value !== "string" || value.trim().length > MAX_NAME) return { field: "name", code: "too_long", message: `Use at most ${MAX_NAME} characters.` };
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
function checkCode(value) {
|
|
662
|
+
const v = typeof value === "string" ? value.trim() : "";
|
|
663
|
+
if (v === "") return { field: "code", code: "required", message: "Enter the code." };
|
|
664
|
+
if (v.length > 40) return { field: "code", code: "too_long", message: "That is not a code." };
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
function firstProblem(...checks) {
|
|
668
|
+
return checks.find((c) => c !== null) ?? null;
|
|
669
|
+
}
|
|
670
|
+
|
|
531
671
|
// src/webhook.ts
|
|
532
672
|
var import_jose3 = require("jose");
|
|
533
673
|
var CauthyWebhookError = class extends Error {
|
|
@@ -549,8 +689,8 @@ function keysOf(issuer) {
|
|
|
549
689
|
}
|
|
550
690
|
var hex = (s) => typeof s === "string" && s !== "" ? s : void 0;
|
|
551
691
|
async function verifyWebhook(request, options = {}) {
|
|
552
|
-
const
|
|
553
|
-
const issuer = (options.issuer ??
|
|
692
|
+
const env4 = typeof process !== "undefined" ? process.env?.CAUTHY_ISSUER : void 0;
|
|
693
|
+
const issuer = (options.issuer ?? env4 ?? "").replace(/\/+$/, "");
|
|
554
694
|
if (issuer === "") throw new CauthyWebhookError("issuer (CAUTHY_ISSUER) is not set");
|
|
555
695
|
const signature = request.headers.get("x-cauthy-signature-jwt");
|
|
556
696
|
if (!signature) throw new CauthyWebhookError("no signature");
|
|
@@ -601,9 +741,19 @@ async function verifyWebhook(request, options = {}) {
|
|
|
601
741
|
|
|
602
742
|
// src/cauthy.ts
|
|
603
743
|
var SESSION_DAYS = 30;
|
|
604
|
-
var FLOW_SECONDS =
|
|
744
|
+
var FLOW_SECONDS = 3600;
|
|
745
|
+
var AGAIN_SECONDS = 120;
|
|
605
746
|
var SKEW_SECONDS = 30;
|
|
606
747
|
var ORGANIZATIONS_TTL_MS = 6e4;
|
|
748
|
+
var LOOK_TTL_MS = 6e4;
|
|
749
|
+
var PROVIDER_ID = /^(custom:)?[a-z0-9_-]{1,40}$/;
|
|
750
|
+
function providerIn(segment) {
|
|
751
|
+
try {
|
|
752
|
+
return decodeURIComponent(segment);
|
|
753
|
+
} catch {
|
|
754
|
+
return "";
|
|
755
|
+
}
|
|
756
|
+
}
|
|
607
757
|
function env2(name) {
|
|
608
758
|
return typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
609
759
|
}
|
|
@@ -636,6 +786,7 @@ function createCauthy(given = {}) {
|
|
|
636
786
|
const clientSecret = given.clientSecret ?? env2("CAUTHY_CLIENT_SECRET");
|
|
637
787
|
const secret = given.secret ?? env2("CAUTHY_SECRET") ?? "";
|
|
638
788
|
const system = given.system ?? env2("CAUTHY_SYSTEM") ?? "";
|
|
789
|
+
const tenant = given.tenant ?? env2("CAUTHY_TENANT") ?? "";
|
|
639
790
|
if (!/^https?:\/\//.test(issuer)) throw new CauthyConfigError("issuer (CAUTHY_ISSUER) must be Cauthy's address, e.g. https://id.example.com");
|
|
640
791
|
if (clientId === "") throw new CauthyConfigError("clientId (CAUTHY_CLIENT_ID) is required");
|
|
641
792
|
if (secret.length < 32) throw new CauthyConfigError("secret (CAUTHY_SECRET) must be at least 32 characters: it encrypts the session cookie");
|
|
@@ -644,18 +795,26 @@ function createCauthy(given = {}) {
|
|
|
644
795
|
const afterSignOut = localPath(given.afterSignOut, "/");
|
|
645
796
|
const cookieName = given.cookieName ?? "cauthy_session";
|
|
646
797
|
const flowCookie = `${cookieName}_flow`;
|
|
798
|
+
const againCookie = `${cookieName}_again`;
|
|
799
|
+
const stepCookie = `${cookieName}_step`;
|
|
647
800
|
const doFetch = (...args) => (given.fetch ?? fetch)(...args);
|
|
648
801
|
const key = keyOf(secret);
|
|
802
|
+
const pages = {
|
|
803
|
+
signIn: given.pages?.signIn ? localPath(given.pages.signIn, "/") : void 0,
|
|
804
|
+
resetPassword: given.pages?.resetPassword ? localPath(given.pages.resetPassword, "/") : void 0,
|
|
805
|
+
verifyEmail: given.pages?.verifyEmail ? localPath(given.pages.verifyEmail, "/") : void 0
|
|
806
|
+
};
|
|
807
|
+
const ownSystem = createSystem({ issuer, clientId, clientSecret, key: given.systemKey, fetch: given.fetch });
|
|
649
808
|
const originOf = (request) => (given.baseURL ?? new URL(request.url).origin).replace(/\/+$/, "");
|
|
650
809
|
const attrs = (request, maxAge) => ({ secure: originOf(request).startsWith("https://"), maxAge });
|
|
651
810
|
async function token(form) {
|
|
652
|
-
const
|
|
653
|
-
if (clientSecret)
|
|
811
|
+
const body2 = new URLSearchParams({ client_id: clientId, ...form });
|
|
812
|
+
if (clientSecret) body2.set("client_secret", clientSecret);
|
|
654
813
|
try {
|
|
655
814
|
const res = await doFetch(`${issuer}/v1/oauth/token`, {
|
|
656
815
|
method: "POST",
|
|
657
816
|
headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
|
|
658
|
-
body
|
|
817
|
+
body: body2
|
|
659
818
|
});
|
|
660
819
|
if (res.ok) return { ok: true, body: await res.json() };
|
|
661
820
|
return { ok: false, ended: res.status >= 400 && res.status < 500 };
|
|
@@ -685,12 +844,12 @@ function createCauthy(given = {}) {
|
|
|
685
844
|
return null;
|
|
686
845
|
}
|
|
687
846
|
}
|
|
688
|
-
async function stored(
|
|
689
|
-
if (typeof
|
|
690
|
-
const who = await userinfo(
|
|
847
|
+
async function stored(body2) {
|
|
848
|
+
if (typeof body2?.access_token !== "string" || typeof body2?.refresh_token !== "string") return null;
|
|
849
|
+
const who = await userinfo(body2.access_token);
|
|
691
850
|
if (who === null || who.u.id === "") return null;
|
|
692
|
-
const lifetime = typeof
|
|
693
|
-
return { at:
|
|
851
|
+
const lifetime = typeof body2.expires_in === "number" && body2.expires_in > 0 ? body2.expires_in : 300;
|
|
852
|
+
return { at: body2.access_token, rt: body2.refresh_token, exp: Math.floor(Date.now() / 1e3) + lifetime, ...who };
|
|
694
853
|
}
|
|
695
854
|
function toSession(s) {
|
|
696
855
|
return { user: s.u, tenantId: s.tid, roles: s.roles, accessToken: s.at, expiresAt: new Date(s.exp * 1e3).toISOString() };
|
|
@@ -764,8 +923,8 @@ function createCauthy(given = {}) {
|
|
|
764
923
|
const url = `${issuer}/v1/oauth/organizations${system ? `?system=${encodeURIComponent(system)}` : ""}`;
|
|
765
924
|
const res = await doFetch(url, { headers: { authorization: `Bearer ${session.accessToken}` } });
|
|
766
925
|
if (!res.ok) throw new Error(`cauthy-auth: reading organizations: ${res.status}`);
|
|
767
|
-
const
|
|
768
|
-
const value = (
|
|
926
|
+
const body2 = await res.json();
|
|
927
|
+
const value = (body2.data ?? []).map(
|
|
769
928
|
(o) => ({
|
|
770
929
|
id: String(o.id),
|
|
771
930
|
system: String(o.system ?? ""),
|
|
@@ -810,20 +969,28 @@ function createCauthy(given = {}) {
|
|
|
810
969
|
if (prompt === "login" || prompt === "none") q.set("prompt", prompt);
|
|
811
970
|
const locale = url.searchParams.get("locale") ?? url.searchParams.get("ui_locales");
|
|
812
971
|
if (locale && /^[a-zA-Z-]{2,10}$/.test(locale)) q.set("ui_locales", locale);
|
|
813
|
-
|
|
972
|
+
const passkey = url.searchParams.get("method") === "passkey" ? "#passkey" : "";
|
|
973
|
+
return redirect(`${issuer}/v1/oauth/authorize?${q}${passkey}`, [
|
|
814
974
|
writeCookie(flowCookie, await seal(flow, await key), attrs(request, FLOW_SECONDS))
|
|
815
975
|
]);
|
|
816
976
|
}
|
|
817
977
|
function failed(request, reason) {
|
|
818
978
|
const h = new Headers({ "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" });
|
|
819
979
|
h.append("set-cookie", writeCookie(flowCookie, "", attrs(request, 0)));
|
|
980
|
+
h.append("set-cookie", writeCookie(againCookie, "", attrs(request, 0)));
|
|
820
981
|
return new Response(`Sign-in could not be completed (${reason}). Go back and try again.`, { status: 400, headers: h });
|
|
821
982
|
}
|
|
822
983
|
async function callback(request, url) {
|
|
823
984
|
const had = readCookies(request.headers.get("cookie"));
|
|
824
985
|
const sealed = had.get(flowCookie);
|
|
825
986
|
const flow = sealed ? await unseal(sealed, await key) : null;
|
|
826
|
-
if (flow === null || url.searchParams.get("state") !== flow.state)
|
|
987
|
+
if (flow === null || url.searchParams.get("state") !== flow.state) {
|
|
988
|
+
if (had.get(againCookie) === void 0) {
|
|
989
|
+
const back = flow?.returnTo ?? afterSignIn;
|
|
990
|
+
return redirect(`${basePath}/login?return_to=${encodeURIComponent(back)}`, [writeCookie(againCookie, "1", attrs(request, AGAIN_SECONDS))]);
|
|
991
|
+
}
|
|
992
|
+
return failed(request, "state_mismatch");
|
|
993
|
+
}
|
|
827
994
|
const error = url.searchParams.get("error");
|
|
828
995
|
if (error !== null) {
|
|
829
996
|
if (error === "login_required") return redirect(flow.returnTo, [writeCookie(flowCookie, "", attrs(request, 0))]);
|
|
@@ -839,28 +1006,377 @@ function createCauthy(given = {}) {
|
|
|
839
1006
|
});
|
|
840
1007
|
const s = res.ok ? await stored(res.body) : null;
|
|
841
1008
|
if (s === null) return failed(request, "exchange_failed");
|
|
842
|
-
return redirect(flow.returnTo, [...await write(request, s, had), writeCookie(flowCookie, "", attrs(request, 0))]);
|
|
1009
|
+
return redirect(flow.returnTo, [...await write(request, s, had), writeCookie(flowCookie, "", attrs(request, 0)), writeCookie(againCookie, "", attrs(request, 0))]);
|
|
843
1010
|
}
|
|
844
1011
|
async function logout(request) {
|
|
845
1012
|
const { had, value } = await read(request);
|
|
846
1013
|
if (value !== null) {
|
|
847
|
-
const
|
|
848
|
-
if (clientSecret)
|
|
1014
|
+
const body2 = new URLSearchParams({ client_id: clientId, token: value.rt });
|
|
1015
|
+
if (clientSecret) body2.set("client_secret", clientSecret);
|
|
849
1016
|
await doFetch(`${issuer}/v1/oauth/revoke`, {
|
|
850
1017
|
method: "POST",
|
|
851
1018
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
852
|
-
body
|
|
1019
|
+
body: body2
|
|
853
1020
|
}).catch(() => void 0);
|
|
854
1021
|
orgCache.delete(value.at);
|
|
855
1022
|
}
|
|
856
1023
|
const q = new URLSearchParams({ client_id: clientId, post_logout_redirect_uri: `${originOf(request)}${afterSignOut}` });
|
|
857
1024
|
return redirect(`${issuer}/v1/oauth/logout?${q}`, clearParts(cookieName, attrs(request, 0), had));
|
|
858
1025
|
}
|
|
1026
|
+
const STEP_SECONDS = 600;
|
|
1027
|
+
const problem = (status2, code, message, field, headers) => json({ error: { code, message, ...field ? { field } : {} } }, status2, headers);
|
|
1028
|
+
const invalid = (p) => problem(400, p.code, p.message, p.field);
|
|
1029
|
+
function ownRequest(request) {
|
|
1030
|
+
const origin = request.headers.get("origin");
|
|
1031
|
+
if (origin !== null) return origin === originOf(request) || origin === new URL(request.url).origin;
|
|
1032
|
+
return (request.headers.get("sec-fetch-site") ?? "same-origin") === "same-origin";
|
|
1033
|
+
}
|
|
1034
|
+
async function body(request) {
|
|
1035
|
+
if (!(request.headers.get("content-type") ?? "").includes("application/json")) return null;
|
|
1036
|
+
try {
|
|
1037
|
+
const b = await request.json();
|
|
1038
|
+
return typeof b === "object" && b !== null && !Array.isArray(b) ? b : null;
|
|
1039
|
+
} catch {
|
|
1040
|
+
return null;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
const clientIp = given.clientIp ?? ((request) => request.headers.get("x-forwarded-for")?.split(",").pop()?.trim() || void 0);
|
|
1044
|
+
let noProofUntil = 0;
|
|
1045
|
+
async function passedOn(request) {
|
|
1046
|
+
const ip = clientIp(request);
|
|
1047
|
+
if (!ip || Date.now() < noProofUntil) return {};
|
|
1048
|
+
try {
|
|
1049
|
+
return { "x-cauthy-client-ip": ip, "x-cauthy-system-token": await ownSystem.token() };
|
|
1050
|
+
} catch {
|
|
1051
|
+
noProofUntil = Date.now() + 5 * 6e4;
|
|
1052
|
+
return {};
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
async function ask(request, method, path, payload, page, bearer2) {
|
|
1056
|
+
try {
|
|
1057
|
+
const res = await doFetch(`${issuer}${path}`, {
|
|
1058
|
+
method,
|
|
1059
|
+
headers: {
|
|
1060
|
+
"x-tenant-key": tenant,
|
|
1061
|
+
accept: "application/json",
|
|
1062
|
+
// A link Cauthy mails opens the application's page for it when it
|
|
1063
|
+
// has one; otherwise Cauthy's own. Either way in the reader's language.
|
|
1064
|
+
...page ? { "x-cauthy-link-to": `${originOf(request)}${page}`, "x-cauthy-client-id": clientId } : { "x-cauthy-links": "hosted" },
|
|
1065
|
+
...await passedOn(request),
|
|
1066
|
+
...bearer2 ? { authorization: `Bearer ${bearer2}` } : {},
|
|
1067
|
+
...request.headers.get("accept-language") ? { "accept-language": request.headers.get("accept-language") } : {},
|
|
1068
|
+
...request.headers.get("user-agent") ? { "user-agent": request.headers.get("user-agent") } : {},
|
|
1069
|
+
...payload === void 0 ? {} : { "content-type": "application/json" }
|
|
1070
|
+
},
|
|
1071
|
+
body: payload === void 0 ? void 0 : JSON.stringify(payload)
|
|
1072
|
+
});
|
|
1073
|
+
const b = res.status === 204 || res.status === 202 ? null : await res.json().catch(() => null);
|
|
1074
|
+
if (res.ok) return { status: res.status, data: b?.data ?? b };
|
|
1075
|
+
const first = b?.errors?.[0];
|
|
1076
|
+
return { status: res.status, data: null, error: { code: String(first?.code ?? "error"), message: String(first?.message ?? "The request could not be completed.") } };
|
|
1077
|
+
} catch {
|
|
1078
|
+
return { status: 503, data: null, error: { code: "unavailable", message: "Sign-in is unavailable right now. Try again in a moment." } };
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
const accountOf = (d) => ({
|
|
1082
|
+
id: String(d?.id ?? ""),
|
|
1083
|
+
email: String(d?.email ?? ""),
|
|
1084
|
+
name: String(d?.name ?? ""),
|
|
1085
|
+
emailVerified: d?.email_verified === true,
|
|
1086
|
+
hasPassword: d?.has_password === true,
|
|
1087
|
+
mfaEnabled: d?.mfa_enabled === true
|
|
1088
|
+
});
|
|
1089
|
+
async function signedInAs(request) {
|
|
1090
|
+
const r = await resolve2(request);
|
|
1091
|
+
const headers = new Headers();
|
|
1092
|
+
for (const c of r.cookies) headers.append("set-cookie", c);
|
|
1093
|
+
if (r.session === null) return { response: problem(401, "unauthenticated", "Sign in first.", void 0, headers) };
|
|
1094
|
+
return { token: r.session.accessToken, kept: r.cookies.length === 0 ? (await read(request)).value : null, headers };
|
|
1095
|
+
}
|
|
1096
|
+
async function signedIn(request, tokens, extra = []) {
|
|
1097
|
+
const s = await stored(tokens);
|
|
1098
|
+
if (s === null) return problem(502, "unavailable", "Sign-in is unavailable right now. Try again in a moment.");
|
|
1099
|
+
const h = new Headers();
|
|
1100
|
+
for (const c of [...await write(request, s, readCookies(request.headers.get("cookie"))), ...extra]) h.append("set-cookie", c);
|
|
1101
|
+
return json({ status: "signed_in", session: await toPublic(toSession(s)) }, 200, h);
|
|
1102
|
+
}
|
|
1103
|
+
let lookCache = null;
|
|
1104
|
+
async function ownRoute(request, url, route) {
|
|
1105
|
+
const post = request.method === "POST";
|
|
1106
|
+
const own = ["/sign-in/email", "/sign-in/mfa", "/sign-up/email", "/forgot-password", "/reset-password", "/verify-email", "/sign-in/magic-link", "/sign-in/magic-link/verify", "/sign-in/email-code", "/me", "/me/password", "/me/mfa/setup", "/me/mfa/verify", "/me/mfa/disable", "/me/mfa/recovery-codes", "/me/sessions", "/me/sessions/end", "/me/email", "/me/identities", "/me/identities/unlink", "/sign-out", "/methods", "/look"].includes(route) || route.startsWith("/sign-in/social/") || route.startsWith("/callback/") || route.startsWith("/me/identities/link/");
|
|
1107
|
+
if (!own) return null;
|
|
1108
|
+
if (tenant === "") {
|
|
1109
|
+
return problem(500, "not_configured", "cauthy-auth: tenant (CAUTHY_TENANT) is required for the application's own sign-in screens");
|
|
1110
|
+
}
|
|
1111
|
+
if (route === "/look" && !post) {
|
|
1112
|
+
if (lookCache === null || lookCache.until < Date.now()) {
|
|
1113
|
+
const res = await ask(request, "GET", `/v1/auth/look/${encodeURIComponent(clientId)}`);
|
|
1114
|
+
lookCache = { until: Date.now() + LOOK_TTL_MS, look: res.error ? {} : res.data ?? {} };
|
|
1115
|
+
}
|
|
1116
|
+
return json(lookCache.look);
|
|
1117
|
+
}
|
|
1118
|
+
if (route === "/methods" && !post) {
|
|
1119
|
+
const [providers, settings] = await Promise.all([ask(request, "GET", "/v1/auth/social/providers"), ask(request, "GET", "/v1/auth/settings")]);
|
|
1120
|
+
const list = Array.isArray(providers.data) ? providers.data : [];
|
|
1121
|
+
return json({
|
|
1122
|
+
providers: list.map((p) => ({ id: String(p.provider ?? p.id ?? ""), name: String(p.label ?? p.name ?? p.provider ?? "") })).filter((p) => p.id !== ""),
|
|
1123
|
+
signUp: settings.data?.allow_signup !== false,
|
|
1124
|
+
// A sign-in link needs a page of the application to open.
|
|
1125
|
+
magicLink: settings.data?.allow_magic_link === true && pages.signIn !== void 0,
|
|
1126
|
+
// Asked for on Cauthy's page: the person goes there for that step and comes back.
|
|
1127
|
+
passkey: true
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
if (route.startsWith("/sign-in/social/") && !post) {
|
|
1131
|
+
const provider = providerIn(route.slice("/sign-in/social/".length));
|
|
1132
|
+
if (!PROVIDER_ID.test(provider)) return new Response("Not found", { status: 404 });
|
|
1133
|
+
const returnTo = localPath(url.searchParams.get("return_to"), afterSignIn);
|
|
1134
|
+
const callback2 = `${basePath}/callback/${provider}`;
|
|
1135
|
+
const started = await ask(request, "GET", `/v1/auth/social/${provider}?redirect_uri=${encodeURIComponent(`${originOf(request)}${callback2}`)}`, void 0, callback2);
|
|
1136
|
+
if (started.error || typeof started.data?.redirect_url !== "string") {
|
|
1137
|
+
return redirect(`${returnTo}${returnTo.includes("?") ? "&" : "?"}auth_error=${encodeURIComponent(started.error?.code ?? "provider_unavailable")}`);
|
|
1138
|
+
}
|
|
1139
|
+
const step = { kind: "social", token: String(started.data.state ?? ""), provider, returnTo };
|
|
1140
|
+
return redirect(started.data.redirect_url, [writeCookie(stepCookie, await seal(step, await key), attrs(request, STEP_SECONDS))]);
|
|
1141
|
+
}
|
|
1142
|
+
if (route.startsWith("/callback/") && !post) {
|
|
1143
|
+
const provider = providerIn(route.slice("/callback/".length));
|
|
1144
|
+
const sealed = readCookies(request.headers.get("cookie")).get(stepCookie);
|
|
1145
|
+
const step = sealed ? await unseal(sealed, await key) : null;
|
|
1146
|
+
const clear = writeCookie(stepCookie, "", attrs(request, 0));
|
|
1147
|
+
const back = step?.returnTo ?? afterSignIn;
|
|
1148
|
+
const failedTo = (code2) => redirect(`${back}${back.includes("?") ? "&" : "?"}auth_error=${encodeURIComponent(code2)}`, [clear]);
|
|
1149
|
+
if (step === null || step.kind !== "social" && step.kind !== "link" || step.provider !== provider || url.searchParams.get("state") !== step.token) return failedTo("state_mismatch");
|
|
1150
|
+
const code = url.searchParams.get("code");
|
|
1151
|
+
if (!code) return failedTo(url.searchParams.get("error") === "access_denied" ? "cancelled" : "provider_refused");
|
|
1152
|
+
const q = new URLSearchParams({ code, state: step.token, redirect_uri: `${originOf(request)}${basePath}/callback/${provider}` });
|
|
1153
|
+
if (step.kind === "link") {
|
|
1154
|
+
const mine = await signedInAs(request);
|
|
1155
|
+
if ("response" in mine) return failedTo("unauthenticated");
|
|
1156
|
+
const linked = await ask(request, "GET", `/v1/auth/identities/${provider}/link/callback?${q}`, void 0, void 0, mine.token);
|
|
1157
|
+
if (linked.error) return failedTo(linked.error.code);
|
|
1158
|
+
const h = new Headers(mine.headers);
|
|
1159
|
+
h.append("set-cookie", clear);
|
|
1160
|
+
return redirect(`${back}${back.includes("?") ? "&" : "?"}linked=${encodeURIComponent(provider)}`, [...h.getSetCookie()]);
|
|
1161
|
+
}
|
|
1162
|
+
const done = await ask(request, "GET", `/v1/auth/social/${provider}/callback?${q}`);
|
|
1163
|
+
if (done.error) return failedTo(done.error.code);
|
|
1164
|
+
const s = await stored(done.data);
|
|
1165
|
+
if (s === null) return failedTo("unavailable");
|
|
1166
|
+
return redirect(back, [...await write(request, s, readCookies(request.headers.get("cookie"))), clear]);
|
|
1167
|
+
}
|
|
1168
|
+
if (route.startsWith("/me/identities/link/") && !post) {
|
|
1169
|
+
const provider = providerIn(route.slice("/me/identities/link/".length));
|
|
1170
|
+
if (!PROVIDER_ID.test(provider)) return new Response("Not found", { status: 404 });
|
|
1171
|
+
const returnTo = localPath(url.searchParams.get("return_to"), afterSignIn);
|
|
1172
|
+
const failed2 = (code) => redirect(`${returnTo}${returnTo.includes("?") ? "&" : "?"}auth_error=${encodeURIComponent(code)}`);
|
|
1173
|
+
const mine = await signedInAs(request);
|
|
1174
|
+
if ("response" in mine) return failed2("unauthenticated");
|
|
1175
|
+
const callback2 = `${basePath}/callback/${provider}`;
|
|
1176
|
+
const started = await ask(request, "GET", `/v1/auth/identities/${provider}/link?redirect_uri=${encodeURIComponent(`${originOf(request)}${callback2}`)}`, void 0, callback2, mine.token);
|
|
1177
|
+
if (started.error || typeof started.data?.redirect_url !== "string") return failed2(started.error?.code ?? "provider_unavailable");
|
|
1178
|
+
const step = { kind: "link", token: String(started.data.state ?? ""), provider, returnTo };
|
|
1179
|
+
return redirect(started.data.redirect_url, [...mine.headers.getSetCookie(), writeCookie(stepCookie, await seal(step, await key), attrs(request, STEP_SECONDS))]);
|
|
1180
|
+
}
|
|
1181
|
+
if (route === "/me/identities" && !post) {
|
|
1182
|
+
const mine = await signedInAs(request);
|
|
1183
|
+
if ("response" in mine) return mine.response;
|
|
1184
|
+
const res = await ask(request, "GET", "/v1/auth/identities", void 0, void 0, mine.token);
|
|
1185
|
+
if (res.error) return problem(res.status, res.error.code, res.error.message, void 0, mine.headers);
|
|
1186
|
+
const list = Array.isArray(res.data) ? res.data : [];
|
|
1187
|
+
return json({ identities: list.map((x) => ({ provider: String(x.provider ?? ""), email: String(x.email ?? ""), name: String(x.name ?? ""), linkedAt: String(x.linked_at ?? "") })) }, 200, mine.headers);
|
|
1188
|
+
}
|
|
1189
|
+
if ((route === "/me" || route === "/me/sessions") && !post) {
|
|
1190
|
+
const mine = await signedInAs(request);
|
|
1191
|
+
if ("response" in mine) return mine.response;
|
|
1192
|
+
const res = await ask(request, "GET", route === "/me" ? "/v1/auth/me" : "/v1/auth/me/sessions", void 0, void 0, mine.token);
|
|
1193
|
+
if (res.error) return problem(res.status, res.error.code, res.error.message, void 0, mine.headers);
|
|
1194
|
+
if (route === "/me/sessions") {
|
|
1195
|
+
const list = Array.isArray(res.data) ? res.data : [];
|
|
1196
|
+
return json({ sessions: list.map((x) => ({ device: String(x.device ?? ""), ip: String(x.ip ?? ""), createdAt: String(x.created_at ?? ""), expiresAt: String(x.expires_at ?? "") })) }, 200, mine.headers);
|
|
1197
|
+
}
|
|
1198
|
+
return json({ account: accountOf(res.data) }, 200, mine.headers);
|
|
1199
|
+
}
|
|
1200
|
+
if (!post) return new Response("Method not allowed", { status: 405, headers: { allow: "POST" } });
|
|
1201
|
+
if (!ownRequest(request)) return problem(403, "cross_site", "This request did not come from this application.");
|
|
1202
|
+
if (route === "/sign-out") {
|
|
1203
|
+
const { had, value } = await read(request);
|
|
1204
|
+
if (value !== null) {
|
|
1205
|
+
const form = new URLSearchParams({ client_id: clientId, token: value.rt });
|
|
1206
|
+
if (clientSecret) form.set("client_secret", clientSecret);
|
|
1207
|
+
await doFetch(`${issuer}/v1/oauth/revoke`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form }).catch(() => void 0);
|
|
1208
|
+
orgCache.delete(value.at);
|
|
1209
|
+
}
|
|
1210
|
+
const h = new Headers();
|
|
1211
|
+
for (const c of clearParts(cookieName, attrs(request, 0), had)) h.append("set-cookie", c);
|
|
1212
|
+
return json({ status: "signed_out" }, 200, h);
|
|
1213
|
+
}
|
|
1214
|
+
const b = await body(request);
|
|
1215
|
+
if (b === null) return problem(400, "bad_request", "Send the form as JSON.");
|
|
1216
|
+
if (route === "/sign-in/email") {
|
|
1217
|
+
const bad = firstProblem(checkEmail(b.email), checkPassword(b.password));
|
|
1218
|
+
if (bad) return invalid(bad);
|
|
1219
|
+
const res = await ask(request, "POST", "/v1/auth/login", { email: String(b.email).trim(), password: b.password });
|
|
1220
|
+
if (res.error) return problem(res.status, res.error.code, res.error.message);
|
|
1221
|
+
if (res.data?.mfa_required === true) {
|
|
1222
|
+
const step = { kind: "mfa", token: String(res.data.mfa_token ?? ""), returnTo: afterSignIn };
|
|
1223
|
+
return json({ status: "mfa_required" }, 200, new Headers({ "set-cookie": writeCookie(stepCookie, await seal(step, await key), attrs(request, STEP_SECONDS)) }));
|
|
1224
|
+
}
|
|
1225
|
+
return signedIn(request, res.data);
|
|
1226
|
+
}
|
|
1227
|
+
if (route === "/sign-in/mfa") {
|
|
1228
|
+
const bad = checkCode(b.code);
|
|
1229
|
+
if (bad) return invalid(bad);
|
|
1230
|
+
const sealed = readCookies(request.headers.get("cookie")).get(stepCookie);
|
|
1231
|
+
const step = sealed ? await unseal(sealed, await key) : null;
|
|
1232
|
+
if (step === null || step.kind !== "mfa") return problem(400, "step_expired", "Sign in again: the code was asked for too long ago.");
|
|
1233
|
+
const res = await ask(request, "POST", "/v1/auth/mfa/challenge", { mfa_token: step.token, code: String(b.code).trim() });
|
|
1234
|
+
if (res.error) return problem(res.status, res.error.code, res.error.message, "code");
|
|
1235
|
+
return signedIn(request, res.data, [writeCookie(stepCookie, "", attrs(request, 0))]);
|
|
1236
|
+
}
|
|
1237
|
+
if (route === "/sign-up/email") {
|
|
1238
|
+
const bad = firstProblem(checkName(b.name), checkEmail(b.email), checkNewPassword(b.password));
|
|
1239
|
+
if (bad) return invalid(bad);
|
|
1240
|
+
const email = String(b.email).trim();
|
|
1241
|
+
const made = await ask(request, "POST", "/v1/auth/register", { email, password: b.password, display_name: typeof b.name === "string" ? b.name.trim() : "" }, pages.verifyEmail);
|
|
1242
|
+
if (made.error) return problem(made.status, made.error.code, made.error.message);
|
|
1243
|
+
if (made.data?.verification_required === true) return json({ status: "verification_required", email });
|
|
1244
|
+
const res = await ask(request, "POST", "/v1/auth/login", { email, password: b.password });
|
|
1245
|
+
if (res.error || res.data?.mfa_required === true) return json({ status: "verification_required", email });
|
|
1246
|
+
return signedIn(request, res.data);
|
|
1247
|
+
}
|
|
1248
|
+
if (route === "/forgot-password") {
|
|
1249
|
+
const bad = checkEmail(b.email);
|
|
1250
|
+
if (bad) return invalid(bad);
|
|
1251
|
+
await ask(request, "POST", "/v1/auth/password/reset", { email: String(b.email).trim() }, pages.resetPassword);
|
|
1252
|
+
return json({ status: "sent" });
|
|
1253
|
+
}
|
|
1254
|
+
if (route.startsWith("/me")) {
|
|
1255
|
+
const mine = await signedInAs(request);
|
|
1256
|
+
if ("response" in mine) return mine.response;
|
|
1257
|
+
const as = (path, payload, method = "POST") => ask(request, method, path, payload, void 0, mine.token);
|
|
1258
|
+
const refused = (res, field) => problem(res.status, res.error.code, res.error.message, field, mine.headers);
|
|
1259
|
+
const password = typeof b.password === "string" ? b.password : "";
|
|
1260
|
+
if (route === "/me") {
|
|
1261
|
+
const bad = checkName(b.name);
|
|
1262
|
+
if (bad) return invalid(bad);
|
|
1263
|
+
const res = await as("/v1/auth/me", { name: typeof b.name === "string" ? b.name.trim() : "" }, "PATCH");
|
|
1264
|
+
if (res.error) return refused(res, "name");
|
|
1265
|
+
const h = mine.headers;
|
|
1266
|
+
if (mine.kept !== null) {
|
|
1267
|
+
const again = await stored({ access_token: mine.kept.at, refresh_token: mine.kept.rt, expires_in: Math.max(1, mine.kept.exp - Math.floor(Date.now() / 1e3)) });
|
|
1268
|
+
if (again !== null) for (const c of await write(request, again, readCookies(request.headers.get("cookie")))) h.append("set-cookie", c);
|
|
1269
|
+
}
|
|
1270
|
+
return json({ status: "saved", account: accountOf(res.data) }, 200, h);
|
|
1271
|
+
}
|
|
1272
|
+
if (route === "/me/password") {
|
|
1273
|
+
const bad = checkNewPassword(b.newPassword);
|
|
1274
|
+
if (bad) return problem(400, bad.code, bad.message, "newPassword");
|
|
1275
|
+
const res = await as("/v1/auth/me/password", { current_password: password, new_password: b.newPassword });
|
|
1276
|
+
if (res.error) return refused(res, res.error.code === "wrong_password" ? "password" : res.error.code === "weak_password" ? "newPassword" : void 0);
|
|
1277
|
+
const next = await stored(res.data);
|
|
1278
|
+
const h = new Headers();
|
|
1279
|
+
const had = readCookies(request.headers.get("cookie"));
|
|
1280
|
+
for (const c of next !== null ? await write(request, next, had) : clearParts(cookieName, attrs(request, 0), had)) h.append("set-cookie", c);
|
|
1281
|
+
return json({ status: "changed", signedIn: next !== null }, 200, h);
|
|
1282
|
+
}
|
|
1283
|
+
if (route === "/me/mfa/setup") {
|
|
1284
|
+
const res = await as("/v1/auth/mfa/setup", { password });
|
|
1285
|
+
if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : void 0);
|
|
1286
|
+
return json({ status: "started", secret: String(res.data?.secret ?? ""), otpauthUri: String(res.data?.otpauth_uri ?? "") }, 200, mine.headers);
|
|
1287
|
+
}
|
|
1288
|
+
if (route === "/me/mfa/verify") {
|
|
1289
|
+
const bad = checkCode(b.code);
|
|
1290
|
+
if (bad) return invalid(bad);
|
|
1291
|
+
const res = await as("/v1/auth/mfa/verify", { code: String(b.code).trim() });
|
|
1292
|
+
if (res.error) return refused(res, "code");
|
|
1293
|
+
return json({ status: "enabled", recoveryCodes: Array.isArray(res.data?.recovery_codes) ? res.data.recovery_codes.map(String) : [] }, 200, mine.headers);
|
|
1294
|
+
}
|
|
1295
|
+
if (route === "/me/mfa/disable") {
|
|
1296
|
+
const res = await as("/v1/auth/me/mfa/disable", { password });
|
|
1297
|
+
if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : void 0);
|
|
1298
|
+
return json({ status: "disabled" }, 200, mine.headers);
|
|
1299
|
+
}
|
|
1300
|
+
if (route === "/me/mfa/recovery-codes") {
|
|
1301
|
+
const res = await as("/v1/auth/me/mfa/recovery-codes", { password });
|
|
1302
|
+
if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : void 0);
|
|
1303
|
+
return json({ status: "renewed", recoveryCodes: Array.isArray(res.data?.recovery_codes) ? res.data.recovery_codes.map(String) : [] }, 200, mine.headers);
|
|
1304
|
+
}
|
|
1305
|
+
if (route === "/me/email") {
|
|
1306
|
+
const bad = checkEmail(b.email);
|
|
1307
|
+
if (bad) return invalid(bad);
|
|
1308
|
+
const email = String(b.email).trim();
|
|
1309
|
+
const res = await as("/v1/auth/me/email", { email, password });
|
|
1310
|
+
if (res.error) return refused(res, res.error.code === "step_up_required" ? "password" : ["invalid_email", "email_unchanged", "email_taken"].includes(res.error.code) ? "email" : void 0);
|
|
1311
|
+
return json({ status: "sent", email }, 200, mine.headers);
|
|
1312
|
+
}
|
|
1313
|
+
if (route === "/me/identities/unlink") {
|
|
1314
|
+
const provider = typeof b.provider === "string" ? b.provider : "";
|
|
1315
|
+
if (!PROVIDER_ID.test(provider)) return problem(400, "invalid_provider", "Say which provider to unlink.", "provider", mine.headers);
|
|
1316
|
+
const res = await as(`/v1/auth/identities/${provider}`, void 0, "DELETE");
|
|
1317
|
+
if (res.error) return refused(res);
|
|
1318
|
+
return json({ status: "unlinked", provider }, 200, mine.headers);
|
|
1319
|
+
}
|
|
1320
|
+
if (route === "/me/sessions/end") {
|
|
1321
|
+
const res = await as("/v1/auth/me/sessions", void 0, "DELETE");
|
|
1322
|
+
if (res.error) return refused(res);
|
|
1323
|
+
const h = new Headers();
|
|
1324
|
+
for (const c of clearParts(cookieName, attrs(request, 0), readCookies(request.headers.get("cookie")))) h.append("set-cookie", c);
|
|
1325
|
+
orgCache.delete(mine.token);
|
|
1326
|
+
return json({ status: "signed_out" }, 200, h);
|
|
1327
|
+
}
|
|
1328
|
+
return null;
|
|
1329
|
+
}
|
|
1330
|
+
const isToken = (v) => typeof v === "string" && v !== "" && v.length <= 512;
|
|
1331
|
+
const badLink = () => problem(422, "invalid_token", "This link is not valid, or it has expired.");
|
|
1332
|
+
if (route === "/reset-password") {
|
|
1333
|
+
if (!isToken(b.token)) return badLink();
|
|
1334
|
+
const bad = checkNewPassword(b.password);
|
|
1335
|
+
if (bad) return invalid(bad);
|
|
1336
|
+
const res = await ask(request, "POST", "/v1/auth/password/change", { token: String(b.token), new_password: b.password });
|
|
1337
|
+
if (res.error) return problem(res.status, res.error.code, res.error.message, res.error.code === "weak_password" ? "password" : void 0);
|
|
1338
|
+
return json({ status: "changed" });
|
|
1339
|
+
}
|
|
1340
|
+
if (route === "/verify-email") {
|
|
1341
|
+
if (!isToken(b.token)) return badLink();
|
|
1342
|
+
const res = await ask(request, "POST", "/v1/auth/email/verify", { token: String(b.token) });
|
|
1343
|
+
if (res.error) return problem(res.status, res.error.code, res.error.message);
|
|
1344
|
+
return json({ status: "verified" });
|
|
1345
|
+
}
|
|
1346
|
+
if (route === "/sign-in/magic-link") {
|
|
1347
|
+
const bad = checkEmail(b.email);
|
|
1348
|
+
if (bad) return invalid(bad);
|
|
1349
|
+
if (pages.signIn === void 0) return problem(500, "not_configured", "cauthy-auth: pages.signIn is required for sign-in links: it is the page the link opens");
|
|
1350
|
+
const email = String(b.email).trim();
|
|
1351
|
+
await ask(request, "POST", "/v1/auth/magic-link/request", { email }, pages.signIn);
|
|
1352
|
+
const step = { kind: "code", token: email, returnTo: afterSignIn };
|
|
1353
|
+
return json({ status: "sent" }, 200, new Headers({ "set-cookie": writeCookie(stepCookie, await seal(step, await key), attrs(request, STEP_SECONDS)) }));
|
|
1354
|
+
}
|
|
1355
|
+
if (route === "/sign-in/email-code") {
|
|
1356
|
+
const bad = checkCode(b.code);
|
|
1357
|
+
if (bad) return invalid(bad);
|
|
1358
|
+
const sealed = readCookies(request.headers.get("cookie")).get(stepCookie);
|
|
1359
|
+
const step = sealed ? await unseal(sealed, await key) : null;
|
|
1360
|
+
if (step === null || step.kind !== "code") return problem(400, "step_expired", "Ask for a new link: this one was asked for too long ago.");
|
|
1361
|
+
const res = await ask(request, "POST", "/v1/auth/magic-link/verify-code", { email: step.token, code: String(b.code).trim() });
|
|
1362
|
+
if (res.error) return problem(res.status, res.error.code, res.error.message, "code");
|
|
1363
|
+
return signedIn(request, res.data, [writeCookie(stepCookie, "", attrs(request, 0))]);
|
|
1364
|
+
}
|
|
1365
|
+
if (route === "/sign-in/magic-link/verify") {
|
|
1366
|
+
if (!isToken(b.token)) return badLink();
|
|
1367
|
+
const res = await ask(request, "POST", "/v1/auth/magic-link/verify", { token: String(b.token) });
|
|
1368
|
+
if (res.error) return problem(res.status, res.error.code, res.error.message);
|
|
1369
|
+
return signedIn(request, res.data);
|
|
1370
|
+
}
|
|
1371
|
+
return null;
|
|
1372
|
+
}
|
|
859
1373
|
async function handler(request) {
|
|
860
1374
|
const url = new URL(request.url);
|
|
861
1375
|
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) return new Response("Not found", { status: 404 });
|
|
862
1376
|
const route = url.pathname.slice(basePath.length).replace(/\/+$/, "") || "/";
|
|
863
1377
|
const get = request.method === "GET" || request.method === "HEAD";
|
|
1378
|
+
const mine = await ownRoute(request, url, route);
|
|
1379
|
+
if (mine !== null) return mine;
|
|
864
1380
|
if (route === "/login" && get) return login(request, url);
|
|
865
1381
|
if (route === "/callback" && get) return callback(request, url);
|
|
866
1382
|
if (route === "/logout" && (get || request.method === "POST")) return logout(request);
|
|
@@ -904,7 +1420,7 @@ function createCauthy(given = {}) {
|
|
|
904
1420
|
return {
|
|
905
1421
|
handler,
|
|
906
1422
|
webhook,
|
|
907
|
-
system:
|
|
1423
|
+
system: ownSystem,
|
|
908
1424
|
getSession,
|
|
909
1425
|
status,
|
|
910
1426
|
resolve: resolve2,
|
|
@@ -929,12 +1445,101 @@ function can(organization, resource, action) {
|
|
|
929
1445
|
if ((organization.denied ?? []).some((d) => matches(d, resource, action))) return false;
|
|
930
1446
|
return organization.permissions.some((p) => matches(p, resource, action));
|
|
931
1447
|
}
|
|
1448
|
+
function definePolicy(roles) {
|
|
1449
|
+
const entry = (p) => `${p.resource}:${p.action}`;
|
|
1450
|
+
const byName = new Map(roles.map((r) => [r.role, r]));
|
|
1451
|
+
const split = (permission) => {
|
|
1452
|
+
const i = permission.lastIndexOf(":");
|
|
1453
|
+
return i < 0 ? [permission, ""] : [permission.slice(0, i), permission.slice(i + 1)];
|
|
1454
|
+
};
|
|
1455
|
+
return {
|
|
1456
|
+
roles: roles.map((r) => ({
|
|
1457
|
+
role: r.role,
|
|
1458
|
+
...r.name !== void 0 ? { name: r.name } : {},
|
|
1459
|
+
...r.description !== void 0 ? { description: r.description } : {},
|
|
1460
|
+
...r.inherits ? { inherits: [...r.inherits] } : {},
|
|
1461
|
+
...r.permissions ? { permissions: r.permissions.map((p) => ({ ...p })) } : {}
|
|
1462
|
+
})),
|
|
1463
|
+
roleNames: roles.map((r) => r.role),
|
|
1464
|
+
permissions: [...new Set(roles.flatMap((r) => (r.permissions ?? []).map(entry)))],
|
|
1465
|
+
can(organization, permission) {
|
|
1466
|
+
const [resource, action] = split(permission);
|
|
1467
|
+
return can(organization, resource, action);
|
|
1468
|
+
},
|
|
1469
|
+
hasRole(organization, ...wanted) {
|
|
1470
|
+
return organization != null && wanted.includes(organization.role);
|
|
1471
|
+
},
|
|
1472
|
+
permissionsOf(role) {
|
|
1473
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1474
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
1475
|
+
const walk = (name) => {
|
|
1476
|
+
if (seen.has(name)) return;
|
|
1477
|
+
seen.add(name);
|
|
1478
|
+
const r = byName.get(name);
|
|
1479
|
+
for (const p of r?.permissions ?? []) out2.add(entry(p));
|
|
1480
|
+
for (const parent of r?.inherits ?? []) walk(parent);
|
|
1481
|
+
};
|
|
1482
|
+
walk(role);
|
|
1483
|
+
return [...out2];
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
var IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1488
|
+
function policySource(system, roles) {
|
|
1489
|
+
const text = (v) => JSON.stringify(v);
|
|
1490
|
+
const key = (k) => IDENT.test(k) ? k : text(k);
|
|
1491
|
+
const one = (r) => {
|
|
1492
|
+
const parts = [`role: ${text(r.role)}`];
|
|
1493
|
+
if (r.name) parts.push(`name: ${text(r.name)}`);
|
|
1494
|
+
if (r.description) parts.push(`description: ${text(r.description)}`);
|
|
1495
|
+
if (r.inherits?.length) parts.push(`inherits: [${r.inherits.map(text).join(", ")}]`);
|
|
1496
|
+
if (r.permissions?.length) {
|
|
1497
|
+
const list = r.permissions.map((p) => ` { ${[...p.kind && p.kind !== "*" ? [`${key("kind")}: ${text(p.kind)}`] : [], `resource: ${text(p.resource)}`, `action: ${text(p.action)}`].join(", ")} },`);
|
|
1498
|
+
parts.push(`permissions: [
|
|
1499
|
+
${list.join("\n")}
|
|
1500
|
+
]`);
|
|
1501
|
+
}
|
|
1502
|
+
return ` {
|
|
1503
|
+
${parts.join(",\n ")},
|
|
1504
|
+
},`;
|
|
1505
|
+
};
|
|
1506
|
+
return `// The catalog of roles of ${text(system)}, as Cauthy has it.
|
|
1507
|
+
// Written by \`cauthy-auth policy --pull\`: run it again after the catalog
|
|
1508
|
+
// changes, and do not edit this file by hand.
|
|
1509
|
+
|
|
1510
|
+
import { definePolicy, type PermissionOf, type RoleOf } from 'cauthy-auth'
|
|
1511
|
+
|
|
1512
|
+
export const policy = definePolicy([
|
|
1513
|
+
${roles.map(one).join("\n")}
|
|
1514
|
+
])
|
|
1515
|
+
|
|
1516
|
+
export const { can, hasRole } = policy
|
|
1517
|
+
export type Role = RoleOf<typeof policy>
|
|
1518
|
+
export type Permission = PermissionOf<typeof policy>
|
|
1519
|
+
`;
|
|
1520
|
+
}
|
|
1521
|
+
async function pullPolicy(o = {}) {
|
|
1522
|
+
const env4 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
1523
|
+
const issuer = (o.issuer ?? env4("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
1524
|
+
const system = o.system ?? env4("CAUTHY_SYSTEM") ?? "";
|
|
1525
|
+
const tenant = o.tenant ?? env4("CAUTHY_TENANT") ?? "";
|
|
1526
|
+
const token = o.token ?? env4("CAUTHY_TOKEN") ?? "";
|
|
1527
|
+
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_SYSTEM: system, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
1528
|
+
if (v === "") throw new Error(`cauthy-auth: ${name} is required to read a policy`);
|
|
1529
|
+
}
|
|
1530
|
+
const res = await (o.fetch ?? fetch)(`${issuer}/v1/systems/${encodeURIComponent(system)}/policy`, {
|
|
1531
|
+
headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, accept: "application/json" }
|
|
1532
|
+
});
|
|
1533
|
+
const body = await res.json().catch(() => null);
|
|
1534
|
+
if (!res.ok) throw new Error(`cauthy-auth: Cauthy refused to give the policy (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
|
|
1535
|
+
return body?.data?.roles ?? [];
|
|
1536
|
+
}
|
|
932
1537
|
async function pushPolicy(o) {
|
|
933
|
-
const
|
|
934
|
-
const issuer = (o.issuer ??
|
|
935
|
-
const system = o.system ??
|
|
936
|
-
const tenant = o.tenant ??
|
|
937
|
-
const token = o.token ??
|
|
1538
|
+
const env4 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
1539
|
+
const issuer = (o.issuer ?? env4("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
1540
|
+
const system = o.system ?? env4("CAUTHY_SYSTEM") ?? "";
|
|
1541
|
+
const tenant = o.tenant ?? env4("CAUTHY_TENANT") ?? "";
|
|
1542
|
+
const token = o.token ?? env4("CAUTHY_TOKEN") ?? "";
|
|
938
1543
|
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_SYSTEM: system, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
939
1544
|
if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a policy`);
|
|
940
1545
|
}
|
|
@@ -957,10 +1562,10 @@ function sessionSecret() {
|
|
|
957
1562
|
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
958
1563
|
}
|
|
959
1564
|
async function connectSystem(o) {
|
|
960
|
-
const
|
|
961
|
-
const issuer = (o.issuer ??
|
|
962
|
-
const tenant = o.tenant ??
|
|
963
|
-
const token = o.token ??
|
|
1565
|
+
const env4 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
|
|
1566
|
+
const issuer = (o.issuer ?? env4("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
1567
|
+
const tenant = o.tenant ?? env4("CAUTHY_TENANT") ?? "";
|
|
1568
|
+
const token = o.token ?? env4("CAUTHY_TOKEN") ?? "";
|
|
964
1569
|
for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
|
|
965
1570
|
if (v === "") throw new Error(`cauthy-auth: ${name} is required to connect a system`);
|
|
966
1571
|
}
|
|
@@ -992,18 +1597,89 @@ async function connectSystem(o) {
|
|
|
992
1597
|
env: { ...d.env, CAUTHY_SECRET: sessionSecret() }
|
|
993
1598
|
};
|
|
994
1599
|
}
|
|
1600
|
+
|
|
1601
|
+
// src/verify.ts
|
|
1602
|
+
var import_jose4 = require("jose");
|
|
1603
|
+
var CauthyTokenError = class extends Error {
|
|
1604
|
+
constructor(reason) {
|
|
1605
|
+
super(`cauthy-auth: token refused: ${reason}`);
|
|
1606
|
+
this.reason = reason;
|
|
1607
|
+
this.name = "CauthyTokenError";
|
|
1608
|
+
}
|
|
1609
|
+
reason;
|
|
1610
|
+
};
|
|
1611
|
+
var keys2 = /* @__PURE__ */ new Map();
|
|
1612
|
+
function keysOf2(issuer) {
|
|
1613
|
+
let set = keys2.get(issuer);
|
|
1614
|
+
if (set === void 0) {
|
|
1615
|
+
set = (0, import_jose4.createRemoteJWKSet)(new URL(`${issuer}/.well-known/jwks.json`), { cooldownDuration: 3e4 });
|
|
1616
|
+
keys2.set(issuer, set);
|
|
1617
|
+
}
|
|
1618
|
+
return set;
|
|
1619
|
+
}
|
|
1620
|
+
function env3(name) {
|
|
1621
|
+
return typeof process !== "undefined" ? process.env?.[name] : void 0;
|
|
1622
|
+
}
|
|
1623
|
+
function bearer(from) {
|
|
1624
|
+
if (typeof from === "string") return from.replace(/^Bearer\s+/i, "").trim();
|
|
1625
|
+
const header = (from instanceof Headers ? from : from.headers).get("authorization") ?? "";
|
|
1626
|
+
return /^Bearer\s+/i.test(header) ? header.replace(/^Bearer\s+/i, "").trim() : "";
|
|
1627
|
+
}
|
|
1628
|
+
async function verifyToken(from, options = {}) {
|
|
1629
|
+
const issuer = (options.issuer ?? env3("CAUTHY_ISSUER") ?? "").replace(/\/+$/, "");
|
|
1630
|
+
if (issuer === "") throw new CauthyTokenError("issuer (CAUTHY_ISSUER) is not set");
|
|
1631
|
+
const token = bearer(from);
|
|
1632
|
+
if (token === "") throw new CauthyTokenError("no token");
|
|
1633
|
+
let claims;
|
|
1634
|
+
try {
|
|
1635
|
+
const verified = await (0, import_jose4.jwtVerify)(token, options.jwks ?? keysOf2(issuer), {
|
|
1636
|
+
algorithms: ["RS256"],
|
|
1637
|
+
issuer,
|
|
1638
|
+
audience: options.audience ?? env3("CAUTHY_AUDIENCE"),
|
|
1639
|
+
requiredClaims: ["exp", "sub"],
|
|
1640
|
+
currentDate: options.now
|
|
1641
|
+
});
|
|
1642
|
+
claims = verified.payload;
|
|
1643
|
+
} catch (err) {
|
|
1644
|
+
throw new CauthyTokenError(err instanceof Error ? err.message : "bad signature");
|
|
1645
|
+
}
|
|
1646
|
+
const tenantId = typeof claims.tid === "string" ? claims.tid : "";
|
|
1647
|
+
const wanted = options.tenantId ?? env3("CAUTHY_TENANT_ID");
|
|
1648
|
+
if (wanted && tenantId !== wanted) throw new CauthyTokenError("issued for another workspace");
|
|
1649
|
+
return {
|
|
1650
|
+
userId: String(claims.sub),
|
|
1651
|
+
tenantId,
|
|
1652
|
+
...typeof claims.email === "string" && claims.email !== "" ? { email: claims.email } : {},
|
|
1653
|
+
roles: Array.isArray(claims.roles) ? claims.roles.filter((r) => typeof r === "string") : [],
|
|
1654
|
+
...typeof claims.sys === "string" && claims.sys !== "" ? { system: claims.sys } : {},
|
|
1655
|
+
expiresAt: new Date(claims.exp * 1e3).toISOString(),
|
|
1656
|
+
token
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
995
1659
|
// Annotate the CommonJS export names for ESM import in node:
|
|
996
1660
|
0 && (module.exports = {
|
|
997
1661
|
CauthyApiError,
|
|
998
1662
|
CauthyConfigError,
|
|
1663
|
+
CauthyTokenError,
|
|
999
1664
|
CauthyWebhookError,
|
|
1665
|
+
MIN_PASSWORD,
|
|
1000
1666
|
can,
|
|
1667
|
+
checkCode,
|
|
1668
|
+
checkEmail,
|
|
1669
|
+
checkName,
|
|
1670
|
+
checkNewPassword,
|
|
1671
|
+
checkPassword,
|
|
1001
1672
|
connectSystem,
|
|
1002
1673
|
createCauthy,
|
|
1003
1674
|
createSystem,
|
|
1675
|
+
definePolicy,
|
|
1676
|
+
policySource,
|
|
1677
|
+
pullPolicy,
|
|
1004
1678
|
pushPolicy,
|
|
1005
1679
|
pushTheme,
|
|
1006
1680
|
themeFromStackmyth,
|
|
1681
|
+
tokensFromStackmyth,
|
|
1682
|
+
verifyToken,
|
|
1007
1683
|
verifyWebhook
|
|
1008
1684
|
});
|
|
1009
1685
|
//# sourceMappingURL=index.js.map
|