@wizeworks/lens-cloud 0.0.0-stage → 0.3.0-alpha.2
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/LICENSE +21 -0
- package/README.md +115 -2
- package/dist/alerts/evaluate.d.ts +10 -0
- package/dist/alerts/evaluate.d.ts.map +1 -0
- package/dist/alerts/evaluate.js +81 -0
- package/dist/alerts/evaluate.js.map +1 -0
- package/dist/alerts/notify.d.ts +28 -0
- package/dist/alerts/notify.d.ts.map +1 -0
- package/dist/alerts/notify.js +52 -0
- package/dist/alerts/notify.js.map +1 -0
- package/dist/app.d.ts +26 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +2 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/auth.d.ts +51 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +63 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/bridge.d.ts +7 -0
- package/dist/auth/bridge.d.ts.map +1 -0
- package/dist/auth/bridge.js +39 -0
- package/dist/auth/bridge.js.map +1 -0
- package/dist/auth/crypto.d.ts +14 -0
- package/dist/auth/crypto.d.ts.map +1 -0
- package/dist/auth/crypto.js +16 -0
- package/dist/auth/crypto.js.map +1 -0
- package/dist/auth/delete-user.d.ts +16 -0
- package/dist/auth/delete-user.d.ts.map +1 -0
- package/dist/auth/delete-user.js +21 -0
- package/dist/auth/delete-user.js.map +1 -0
- package/dist/auth/deps.d.ts +27 -0
- package/dist/auth/deps.d.ts.map +1 -0
- package/dist/auth/deps.js +3 -0
- package/dist/auth/deps.js.map +1 -0
- package/dist/auth/email-token.d.ts +8 -0
- package/dist/auth/email-token.d.ts.map +1 -0
- package/dist/auth/email-token.js +15 -0
- package/dist/auth/email-token.js.map +1 -0
- package/dist/auth/hooks.d.ts +4 -0
- package/dist/auth/hooks.d.ts.map +1 -0
- package/dist/auth/hooks.js +143 -0
- package/dist/auth/hooks.js.map +1 -0
- package/dist/auth/options.d.ts +17 -0
- package/dist/auth/options.d.ts.map +1 -0
- package/dist/auth/options.js +98 -0
- package/dist/auth/options.js.map +1 -0
- package/dist/auth/password.d.ts +10 -0
- package/dist/auth/password.d.ts.map +1 -0
- package/dist/auth/password.js +22 -0
- package/dist/auth/password.js.map +1 -0
- package/dist/auth/plugins.d.ts +4 -0
- package/dist/auth/plugins.d.ts.map +1 -0
- package/dist/auth/plugins.js +45 -0
- package/dist/auth/plugins.js.map +1 -0
- package/dist/auth/principal.d.ts +38 -0
- package/dist/auth/principal.d.ts.map +1 -0
- package/dist/auth/principal.js +69 -0
- package/dist/auth/principal.js.map +1 -0
- package/dist/auth/roles.d.ts +50 -0
- package/dist/auth/roles.d.ts.map +1 -0
- package/dist/auth/roles.js +18 -0
- package/dist/auth/roles.js.map +1 -0
- package/dist/auth/schema-names.d.ts +130 -0
- package/dist/auth/schema-names.d.ts.map +1 -0
- package/dist/auth/schema-names.js +53 -0
- package/dist/auth/schema-names.js.map +1 -0
- package/dist/auth/secret.d.ts +12 -0
- package/dist/auth/secret.d.ts.map +1 -0
- package/dist/auth/secret.js +32 -0
- package/dist/auth/secret.js.map +1 -0
- package/dist/auth/security-notices.d.ts +34 -0
- package/dist/auth/security-notices.d.ts.map +1 -0
- package/dist/auth/security-notices.js +81 -0
- package/dist/auth/security-notices.js.map +1 -0
- package/dist/auth/signup.d.ts +23 -0
- package/dist/auth/signup.d.ts.map +1 -0
- package/dist/auth/signup.js +61 -0
- package/dist/auth/signup.js.map +1 -0
- package/dist/auth/sso.d.ts +21 -0
- package/dist/auth/sso.d.ts.map +1 -0
- package/dist/auth/sso.js +35 -0
- package/dist/auth/sso.js.map +1 -0
- package/dist/auth/wire.d.ts +16 -0
- package/dist/auth/wire.d.ts.map +1 -0
- package/dist/auth/wire.js +47 -0
- package/dist/auth/wire.js.map +1 -0
- package/dist/cli-args.d.ts +5 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +86 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +44 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/db/db.d.ts +22 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +64 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/migrate.d.ts +6 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +35 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema-auth.d.ts +2 -0
- package/dist/db/schema-auth.d.ts.map +1 -0
- package/dist/db/schema-auth.js +93 -0
- package/dist/db/schema-auth.js.map +1 -0
- package/dist/db/schema-mail.d.ts +2 -0
- package/dist/db/schema-mail.d.ts.map +1 -0
- package/dist/db/schema-mail.js +8 -0
- package/dist/db/schema-mail.js.map +1 -0
- package/dist/db/schema-notify.d.ts +2 -0
- package/dist/db/schema-notify.d.ts.map +1 -0
- package/dist/db/schema-notify.js +26 -0
- package/dist/db/schema-notify.js.map +1 -0
- package/dist/db/schema-sso.d.ts +2 -0
- package/dist/db/schema-sso.d.ts.map +1 -0
- package/dist/db/schema-sso.js +17 -0
- package/dist/db/schema-sso.js.map +1 -0
- package/dist/db/schema.d.ts +3 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +208 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/http/http.d.ts +19 -0
- package/dist/http/http.d.ts.map +1 -0
- package/dist/http/http.js +66 -0
- package/dist/http/http.js.map +1 -0
- package/dist/http/limiter.d.ts +11 -0
- package/dist/http/limiter.d.ts.map +1 -0
- package/dist/http/limiter.js +31 -0
- package/dist/http/limiter.js.map +1 -0
- package/dist/http/router.d.ts +21 -0
- package/dist/http/router.d.ts.map +1 -0
- package/dist/http/router.js +28 -0
- package/dist/http/router.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs.d.ts +4 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +53 -0
- package/dist/jobs.js.map +1 -0
- package/dist/mail/blocks.d.ts +14 -0
- package/dist/mail/blocks.d.ts.map +1 -0
- package/dist/mail/blocks.js +35 -0
- package/dist/mail/blocks.js.map +1 -0
- package/dist/mail/content.d.ts +68 -0
- package/dist/mail/content.d.ts.map +1 -0
- package/dist/mail/content.js +6 -0
- package/dist/mail/content.js.map +1 -0
- package/dist/mail/emails-digest.d.ts +10 -0
- package/dist/mail/emails-digest.d.ts.map +1 -0
- package/dist/mail/emails-digest.js +55 -0
- package/dist/mail/emails-digest.js.map +1 -0
- package/dist/mail/emails-health.d.ts +6 -0
- package/dist/mail/emails-health.d.ts.map +1 -0
- package/dist/mail/emails-health.js +36 -0
- package/dist/mail/emails-health.js.map +1 -0
- package/dist/mail/emails-security.d.ts +14 -0
- package/dist/mail/emails-security.d.ts.map +1 -0
- package/dist/mail/emails-security.js +27 -0
- package/dist/mail/emails-security.js.map +1 -0
- package/dist/mail/emails-team.d.ts +11 -0
- package/dist/mail/emails-team.d.ts.map +1 -0
- package/dist/mail/emails-team.js +45 -0
- package/dist/mail/emails-team.js.map +1 -0
- package/dist/mail/emails.d.ts +9 -0
- package/dist/mail/emails.d.ts.map +1 -0
- package/dist/mail/emails.js +49 -0
- package/dist/mail/emails.js.map +1 -0
- package/dist/mail/layout.d.ts +6 -0
- package/dist/mail/layout.d.ts.map +1 -0
- package/dist/mail/layout.js +10 -0
- package/dist/mail/layout.js.map +1 -0
- package/dist/mail/mailer.d.ts +25 -0
- package/dist/mail/mailer.d.ts.map +1 -0
- package/dist/mail/mailer.js +49 -0
- package/dist/mail/mailer.js.map +1 -0
- package/dist/mail/preview.d.ts +6 -0
- package/dist/mail/preview.d.ts.map +1 -0
- package/dist/mail/preview.js +26 -0
- package/dist/mail/preview.js.map +1 -0
- package/dist/mail/resend.d.ts +11 -0
- package/dist/mail/resend.d.ts.map +1 -0
- package/dist/mail/resend.js +34 -0
- package/dist/mail/resend.js.map +1 -0
- package/dist/mail/samples.d.ts +3 -0
- package/dist/mail/samples.d.ts.map +1 -0
- package/dist/mail/samples.js +63 -0
- package/dist/mail/samples.js.map +1 -0
- package/dist/mail/stop-route.d.ts +4 -0
- package/dist/mail/stop-route.d.ts.map +1 -0
- package/dist/mail/stop-route.js +41 -0
- package/dist/mail/stop-route.js.map +1 -0
- package/dist/mail/svix.d.ts +8 -0
- package/dist/mail/svix.d.ts.map +1 -0
- package/dist/mail/svix.js +17 -0
- package/dist/mail/svix.js.map +1 -0
- package/dist/mail/templates.d.ts +12 -0
- package/dist/mail/templates.d.ts.map +1 -0
- package/dist/mail/templates.js +18 -0
- package/dist/mail/templates.js.map +1 -0
- package/dist/mail/text.d.ts +4 -0
- package/dist/mail/text.d.ts.map +1 -0
- package/dist/mail/text.js +17 -0
- package/dist/mail/text.js.map +1 -0
- package/dist/mail/theme.d.ts +26 -0
- package/dist/mail/theme.d.ts.map +1 -0
- package/dist/mail/theme.js +21 -0
- package/dist/mail/theme.js.map +1 -0
- package/dist/mail/unsubscribe.d.ts +14 -0
- package/dist/mail/unsubscribe.d.ts.map +1 -0
- package/dist/mail/unsubscribe.js +39 -0
- package/dist/mail/unsubscribe.js.map +1 -0
- package/dist/mail/webhook.d.ts +5 -0
- package/dist/mail/webhook.d.ts.map +1 -0
- package/dist/mail/webhook.js +41 -0
- package/dist/mail/webhook.js.map +1 -0
- package/dist/notify/digest-data.d.ts +31 -0
- package/dist/notify/digest-data.d.ts.map +1 -0
- package/dist/notify/digest-data.js +37 -0
- package/dist/notify/digest-data.js.map +1 -0
- package/dist/notify/digest.d.ts +17 -0
- package/dist/notify/digest.d.ts.map +1 -0
- package/dist/notify/digest.js +76 -0
- package/dist/notify/digest.js.map +1 -0
- package/dist/notify/health.d.ts +6 -0
- package/dist/notify/health.d.ts.map +1 -0
- package/dist/notify/health.js +43 -0
- package/dist/notify/health.js.map +1 -0
- package/dist/notify/team-describe.d.ts +22 -0
- package/dist/notify/team-describe.d.ts.map +1 -0
- package/dist/notify/team-describe.js +67 -0
- package/dist/notify/team-describe.js.map +1 -0
- package/dist/notify/team.d.ts +6 -0
- package/dist/notify/team.d.ts.map +1 -0
- package/dist/notify/team.js +56 -0
- package/dist/notify/team.js.map +1 -0
- package/dist/repo/alerts.d.ts +52 -0
- package/dist/repo/alerts.d.ts.map +1 -0
- package/dist/repo/alerts.js +38 -0
- package/dist/repo/alerts.js.map +1 -0
- package/dist/repo/audit.d.ts +11 -0
- package/dist/repo/audit.d.ts.map +1 -0
- package/dist/repo/audit.js +17 -0
- package/dist/repo/audit.js.map +1 -0
- package/dist/repo/auth-prune.d.ts +4 -0
- package/dist/repo/auth-prune.d.ts.map +1 -0
- package/dist/repo/auth-prune.js +8 -0
- package/dist/repo/auth-prune.js.map +1 -0
- package/dist/repo/mail-prefs.d.ts +35 -0
- package/dist/repo/mail-prefs.d.ts.map +1 -0
- package/dist/repo/mail-prefs.js +34 -0
- package/dist/repo/mail-prefs.js.map +1 -0
- package/dist/repo/metric-read.d.ts +12 -0
- package/dist/repo/metric-read.d.ts.map +1 -0
- package/dist/repo/metric-read.js +11 -0
- package/dist/repo/metric-read.js.map +1 -0
- package/dist/repo/metric-write.d.ts +9 -0
- package/dist/repo/metric-write.d.ts.map +1 -0
- package/dist/repo/metric-write.js +53 -0
- package/dist/repo/metric-write.js.map +1 -0
- package/dist/repo/orgs.d.ts +37 -0
- package/dist/repo/orgs.d.ts.map +1 -0
- package/dist/repo/orgs.js +33 -0
- package/dist/repo/orgs.js.map +1 -0
- package/dist/repo/projects.d.ts +37 -0
- package/dist/repo/projects.d.ts.map +1 -0
- package/dist/repo/projects.js +57 -0
- package/dist/repo/projects.js.map +1 -0
- package/dist/repo/suppressions.d.ts +8 -0
- package/dist/repo/suppressions.d.ts.map +1 -0
- package/dist/repo/suppressions.js +16 -0
- package/dist/repo/suppressions.js.map +1 -0
- package/dist/repo/trace-read.d.ts +28 -0
- package/dist/repo/trace-read.d.ts.map +1 -0
- package/dist/repo/trace-read.js +76 -0
- package/dist/repo/trace-read.js.map +1 -0
- package/dist/repo/trace-write.d.ts +9 -0
- package/dist/repo/trace-write.d.ts.map +1 -0
- package/dist/repo/trace-write.js +82 -0
- package/dist/repo/trace-write.js.map +1 -0
- package/dist/repo/users.d.ts +13 -0
- package/dist/repo/users.d.ts.map +1 -0
- package/dist/repo/users.js +7 -0
- package/dist/repo/users.js.map +1 -0
- package/dist/routes/alerts.d.ts +4 -0
- package/dist/routes/alerts.d.ts.map +1 -0
- package/dist/routes/alerts.js +116 -0
- package/dist/routes/alerts.js.map +1 -0
- package/dist/routes/data.d.ts +7 -0
- package/dist/routes/data.d.ts.map +1 -0
- package/dist/routes/data.js +74 -0
- package/dist/routes/data.js.map +1 -0
- package/dist/routes/index.d.ts +5 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +25 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/ingest.d.ts +8 -0
- package/dist/routes/ingest.d.ts.map +1 -0
- package/dist/routes/ingest.js +38 -0
- package/dist/routes/ingest.js.map +1 -0
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +52 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/me.d.ts +4 -0
- package/dist/routes/me.d.ts.map +1 -0
- package/dist/routes/me.js +37 -0
- package/dist/routes/me.js.map +1 -0
- package/dist/routes/metrics.d.ts +13 -0
- package/dist/routes/metrics.d.ts.map +1 -0
- package/dist/routes/metrics.js +29 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/routes/models.d.ts +4 -0
- package/dist/routes/models.d.ts.map +1 -0
- package/dist/routes/models.js +71 -0
- package/dist/routes/models.js.map +1 -0
- package/dist/routes/notify.d.ts +4 -0
- package/dist/routes/notify.d.ts.map +1 -0
- package/dist/routes/notify.js +55 -0
- package/dist/routes/notify.js.map +1 -0
- package/dist/routes/orgs.d.ts +4 -0
- package/dist/routes/orgs.d.ts.map +1 -0
- package/dist/routes/orgs.js +73 -0
- package/dist/routes/orgs.js.map +1 -0
- package/dist/routes/projects.d.ts +4 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +55 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/shares.d.ts +11 -0
- package/dist/routes/shares.d.ts.map +1 -0
- package/dist/routes/shares.js +61 -0
- package/dist/routes/shares.js.map +1 -0
- package/dist/scale-exec.d.ts +11 -0
- package/dist/scale-exec.d.ts.map +1 -0
- package/dist/scale-exec.js +26 -0
- package/dist/scale-exec.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +126 -0
- package/dist/server.js.map +1 -0
- package/dist/settle.d.ts +26 -0
- package/dist/settle.d.ts.map +1 -0
- package/dist/settle.js +85 -0
- package/dist/settle.js.map +1 -0
- package/package.json +76 -3
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { fromNodeHeaders } from "better-auth/node";
|
|
2
|
+
import { bearerOf, fail, HttpError } from "../http/http.js";
|
|
3
|
+
import { membership } from "../repo/orgs.js";
|
|
4
|
+
import { keyByToken, projectInOrg } from "../repo/projects.js";
|
|
5
|
+
import { can } from "./roles.js";
|
|
6
|
+
/** The session of this request, read from the database each time (no cookie cache), so a revoked session stops at once. */
|
|
7
|
+
export async function sessionOf(ctx) {
|
|
8
|
+
const s = await ctx.app.auth.api.getSession({ headers: fromNodeHeaders(ctx.req.headers) });
|
|
9
|
+
if (!s)
|
|
10
|
+
return undefined;
|
|
11
|
+
const { id, email, name, emailVerified } = s.user;
|
|
12
|
+
return { user: { id, email, name, emailVerified, twoFactorEnabled: !!s.user.twoFactorEnabled }, sessionId: s.session.id };
|
|
13
|
+
}
|
|
14
|
+
export async function principalOf(ctx) {
|
|
15
|
+
const bearer = bearerOf(ctx.req);
|
|
16
|
+
if (bearer?.startsWith("wl_rk_")) {
|
|
17
|
+
const key = await keyByToken(ctx.app.db, bearer, ctx.app.config.now());
|
|
18
|
+
return key?.kind === "read" ? { kind: "key", key } : undefined;
|
|
19
|
+
}
|
|
20
|
+
const s = await sessionOf(ctx);
|
|
21
|
+
return s ? { kind: "user", ...s } : undefined;
|
|
22
|
+
}
|
|
23
|
+
/** A signed-in person, or 401. Changes also need the dashboard header, so other sites cannot send them. */
|
|
24
|
+
export async function requireUser(ctx) {
|
|
25
|
+
const p = await principalOf(ctx);
|
|
26
|
+
if (p?.kind !== "user")
|
|
27
|
+
return fail(401, "Sign in first.");
|
|
28
|
+
const method = ctx.req.method ?? "GET";
|
|
29
|
+
if (method !== "GET" && ctx.req.headers["x-wizelens-client"] !== "dashboard")
|
|
30
|
+
fail(403, "Missing the X-WizeLens-Client header.");
|
|
31
|
+
return p.user;
|
|
32
|
+
}
|
|
33
|
+
/** The caller's role in an organization, when it allows `permission`; otherwise 403 (or 404 when not a member). */
|
|
34
|
+
export async function requireOrg(ctx, orgId, permission) {
|
|
35
|
+
const user = await requireUser(ctx);
|
|
36
|
+
const m = await membership(ctx.app.db, user.id, orgId);
|
|
37
|
+
if (!m)
|
|
38
|
+
fail(404, "Organization not found.");
|
|
39
|
+
if (!can(m.role, permission))
|
|
40
|
+
fail(403, `Your role (${m.role}) cannot do this.`);
|
|
41
|
+
return { user, role: m.role };
|
|
42
|
+
}
|
|
43
|
+
/** Access to one project's data, for a person or a read key. */
|
|
44
|
+
export async function requireProject(ctx, projectId, permission = "read") {
|
|
45
|
+
const p = await principalOf(ctx);
|
|
46
|
+
if (!p)
|
|
47
|
+
return fail(401, "Sign in, or send a read key.");
|
|
48
|
+
if (p.kind === "key")
|
|
49
|
+
return keyAccess(ctx, p, projectId, permission);
|
|
50
|
+
const [row] = await ctx.app.db.query("SELECT org_id FROM projects WHERE id = $1", [projectId]);
|
|
51
|
+
const m = row ? await membership(ctx.app.db, p.user.id, row.org_id) : undefined;
|
|
52
|
+
const project = m ? await projectInOrg(ctx.app.db, m.id, projectId) : undefined;
|
|
53
|
+
if (!m || !project)
|
|
54
|
+
return fail(404, "Project not found.");
|
|
55
|
+
if (!can(m.role, permission))
|
|
56
|
+
fail(403, `Your role (${m.role}) cannot do this.`);
|
|
57
|
+
if (permission !== "read")
|
|
58
|
+
await requireUser(ctx);
|
|
59
|
+
return { project, actor: `user:${p.user.id}`, role: m.role, principal: p };
|
|
60
|
+
}
|
|
61
|
+
async function keyAccess(ctx, p, projectId, permission) {
|
|
62
|
+
if (permission !== "read")
|
|
63
|
+
throw new HttpError(403, "Read keys can only read.");
|
|
64
|
+
const project = p.key.projectIds.includes(projectId) ? await projectInOrg(ctx.app.db, p.key.orgId, projectId) : undefined;
|
|
65
|
+
if (!project)
|
|
66
|
+
return fail(404, "Project not found.");
|
|
67
|
+
return { project, actor: `key:${p.key.id}`, role: "viewer", principal: p };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=principal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principal.js","sourceRoot":"","sources":["../../src/auth/principal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type OrgRole } from "@wizeworks/lens-core";
|
|
2
|
+
/** Roles in an organization, from most to least power. */
|
|
3
|
+
export declare const ROLES: readonly ["owner", "admin", "member", "viewer"];
|
|
4
|
+
export type Role = OrgRole;
|
|
5
|
+
export declare const isRole: (v: unknown) => v is OrgRole;
|
|
6
|
+
/** Better Auth access control, built from the shared role data. */
|
|
7
|
+
export declare const ac: {
|
|
8
|
+
newRole<const TRoleStatements extends import("better-auth/plugins/access").Statements>(statements: import("better-auth/plugins/access").RoleInput<{
|
|
9
|
+
readonly organization: readonly ["update", "delete"];
|
|
10
|
+
readonly member: readonly ["create", "update", "delete"];
|
|
11
|
+
readonly invitation: readonly ["create", "cancel"];
|
|
12
|
+
readonly team: readonly ["create", "update", "delete"];
|
|
13
|
+
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
14
|
+
readonly project: readonly ["read", "create", "update", "delete"];
|
|
15
|
+
readonly key: readonly ["read", "create", "revoke"];
|
|
16
|
+
readonly alert: readonly ["read", "create", "update", "delete"];
|
|
17
|
+
readonly share: readonly ["create", "revoke"];
|
|
18
|
+
readonly audit: readonly ["read"];
|
|
19
|
+
}, TRoleStatements>): import("better-auth/plugins/access").Role<import("better-auth/plugins/access").ExactRoleStatements<TRoleStatements>, {
|
|
20
|
+
readonly organization: readonly ["update", "delete"];
|
|
21
|
+
readonly member: readonly ["create", "update", "delete"];
|
|
22
|
+
readonly invitation: readonly ["create", "cancel"];
|
|
23
|
+
readonly team: readonly ["create", "update", "delete"];
|
|
24
|
+
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
25
|
+
readonly project: readonly ["read", "create", "update", "delete"];
|
|
26
|
+
readonly key: readonly ["read", "create", "revoke"];
|
|
27
|
+
readonly alert: readonly ["read", "create", "update", "delete"];
|
|
28
|
+
readonly share: readonly ["create", "revoke"];
|
|
29
|
+
readonly audit: readonly ["read"];
|
|
30
|
+
}>;
|
|
31
|
+
statements: {
|
|
32
|
+
readonly organization: readonly ["update", "delete"];
|
|
33
|
+
readonly member: readonly ["create", "update", "delete"];
|
|
34
|
+
readonly invitation: readonly ["create", "cancel"];
|
|
35
|
+
readonly team: readonly ["create", "update", "delete"];
|
|
36
|
+
readonly ac: readonly ["create", "read", "update", "delete"];
|
|
37
|
+
readonly project: readonly ["read", "create", "update", "delete"];
|
|
38
|
+
readonly key: readonly ["read", "create", "revoke"];
|
|
39
|
+
readonly alert: readonly ["read", "create", "update", "delete"];
|
|
40
|
+
readonly share: readonly ["create", "revoke"];
|
|
41
|
+
readonly audit: readonly ["read"];
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export declare const authRoles: Record<Role, ReturnType<typeof ac.newRole>>;
|
|
45
|
+
/** What our own routes check. Read: data, members. Write: alerts, shares. Manage: projects, keys, settings. Own: delete the organization. */
|
|
46
|
+
export type Permission = "read" | "write" | "manage" | "own";
|
|
47
|
+
export declare const can: (role: Role, permission: Permission) => boolean;
|
|
48
|
+
/** An admin may not give a role above their own. */
|
|
49
|
+
export declare const canGrant: (actor: Role, target: Role) => boolean;
|
|
50
|
+
//# sourceMappingURL=roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.d.ts","sourceRoot":"","sources":["../../src/auth/roles.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6D,KAAK,OAAO,EAAsC,MAAM,sBAAsB,CAAC;AAGnJ,0DAA0D;AAC1D,eAAO,MAAM,KAAK,iDAAY,CAAC;AAC/B,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC;AAC3B,eAAO,MAAM,MAAM,8BAAY,CAAC;AAEhC,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAyC,CAAC;AACzD,eAAO,MAAM,SAAS,EAAgH,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;AAElL,6IAA6I;AAC7I,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAS7D,eAAO,MAAM,GAAG,SAAU,IAAI,cAAc,UAAU,KAAG,OAA8C,CAAC;AAExG,oDAAoD;AACpD,eAAO,MAAM,QAAQ,UAAW,IAAI,UAAU,IAAI,KAAG,OAAgD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ACCESS_STATEMENTS, isOrgRole, ORG_ROLES, ROLE_GRANTS, ROLE_RANK, roleAllows } from "@wizeworks/lens-core";
|
|
2
|
+
import { createAccessControl } from "better-auth/plugins/access";
|
|
3
|
+
/** Roles in an organization, from most to least power. */
|
|
4
|
+
export const ROLES = ORG_ROLES;
|
|
5
|
+
export const isRole = isOrgRole;
|
|
6
|
+
/** Better Auth access control, built from the shared role data. */
|
|
7
|
+
export const ac = createAccessControl(ACCESS_STATEMENTS);
|
|
8
|
+
export const authRoles = Object.fromEntries(ROLES.map((r) => [r, ac.newRole(ROLE_GRANTS[r])]));
|
|
9
|
+
const NEEDS = {
|
|
10
|
+
read: { project: ["read"] },
|
|
11
|
+
write: { alert: ["create"], share: ["create"] },
|
|
12
|
+
manage: { project: ["create"], key: ["create"] },
|
|
13
|
+
own: { organization: ["delete"] },
|
|
14
|
+
};
|
|
15
|
+
export const can = (role, permission) => roleAllows(role, NEEDS[permission]);
|
|
16
|
+
/** An admin may not give a role above their own. */
|
|
17
|
+
export const canGrant = (actor, target) => ROLE_RANK[actor] >= ROLE_RANK[target];
|
|
18
|
+
//# sourceMappingURL=roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.js","sourceRoot":"","sources":["../../src/auth/roles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAoB,SAAS,EAAE,SAAS,EAAgB,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACnJ,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE,0DAA0D;AAC1D,MAAM,CAAC,MAAM,KAAK,GAAG,SAAS,CAAC;AAE/B,MAAM,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC;AAEhC,mEAAmE;AACnE,MAAM,CAAC,MAAM,EAAE,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAqC,CAAC,CAAC,CAAC,CAAgD,CAAC;AAKlL,MAAM,KAAK,GAAoC;IAC7C,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE;IAC3B,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,EAAE;IAC/C,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE;IAChD,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,QAAQ,CAAC,EAAE;CAClC,CAAC;AAEF,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,IAAU,EAAE,UAAsB,EAAW,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAExG,oDAAoD;AACpD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAW,EAAE,MAAY,EAAW,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export declare const userNames: {
|
|
2
|
+
modelName: string;
|
|
3
|
+
fields: {
|
|
4
|
+
emailVerified: string;
|
|
5
|
+
createdAt: string;
|
|
6
|
+
updatedAt: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export declare const sessionNames: {
|
|
10
|
+
modelName: string;
|
|
11
|
+
fields: {
|
|
12
|
+
expiresAt: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
ipAddress: string;
|
|
16
|
+
userAgent: string;
|
|
17
|
+
userId: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export declare const accountNames: {
|
|
21
|
+
modelName: string;
|
|
22
|
+
fields: {
|
|
23
|
+
accountId: string;
|
|
24
|
+
providerId: string;
|
|
25
|
+
userId: string;
|
|
26
|
+
accessToken: string;
|
|
27
|
+
refreshToken: string;
|
|
28
|
+
idToken: string;
|
|
29
|
+
accessTokenExpiresAt: string;
|
|
30
|
+
refreshTokenExpiresAt: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export declare const verificationNames: {
|
|
36
|
+
modelName: string;
|
|
37
|
+
fields: {
|
|
38
|
+
expiresAt: string;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
updatedAt: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export declare const rateLimitNames: {
|
|
44
|
+
modelName: string;
|
|
45
|
+
fields: {
|
|
46
|
+
lastRequest: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export declare const orgSchema: {
|
|
50
|
+
readonly session: {
|
|
51
|
+
readonly fields: {
|
|
52
|
+
readonly activeOrganizationId: "active_org_id";
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
readonly organization: {
|
|
56
|
+
readonly modelName: "orgs";
|
|
57
|
+
readonly fields: {
|
|
58
|
+
readonly createdAt: "created_at";
|
|
59
|
+
};
|
|
60
|
+
readonly additionalFields: {
|
|
61
|
+
readonly retentionDays: {
|
|
62
|
+
readonly type: "number";
|
|
63
|
+
readonly required: true;
|
|
64
|
+
readonly defaultValue: 30;
|
|
65
|
+
readonly input: false;
|
|
66
|
+
readonly fieldName: "retention_days";
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
readonly member: {
|
|
71
|
+
readonly modelName: "members";
|
|
72
|
+
readonly fields: {
|
|
73
|
+
readonly organizationId: "org_id";
|
|
74
|
+
readonly userId: "user_id";
|
|
75
|
+
readonly createdAt: "created_at";
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
readonly invitation: {
|
|
79
|
+
readonly modelName: "invites";
|
|
80
|
+
readonly fields: {
|
|
81
|
+
readonly organizationId: "org_id";
|
|
82
|
+
readonly expiresAt: "expires_at";
|
|
83
|
+
readonly createdAt: "created_at";
|
|
84
|
+
readonly inviterId: "inviter_id";
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
export declare const twoFactorSchema: {
|
|
89
|
+
user: {
|
|
90
|
+
fields: {
|
|
91
|
+
twoFactorEnabled: string;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
twoFactor: {
|
|
95
|
+
modelName: string;
|
|
96
|
+
fields: {
|
|
97
|
+
backupCodes: string;
|
|
98
|
+
userId: string;
|
|
99
|
+
failedVerificationCount: string;
|
|
100
|
+
lockedUntil: string;
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
export declare const passkeySchema: {
|
|
105
|
+
passkey: {
|
|
106
|
+
modelName: string;
|
|
107
|
+
fields: {
|
|
108
|
+
publicKey: string;
|
|
109
|
+
userId: string;
|
|
110
|
+
credentialID: string;
|
|
111
|
+
deviceType: string;
|
|
112
|
+
backedUp: string;
|
|
113
|
+
createdAt: string;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
export declare const ssoSchema: {
|
|
118
|
+
ssoProvider: {
|
|
119
|
+
modelName: string;
|
|
120
|
+
fields: {
|
|
121
|
+
providerId: string;
|
|
122
|
+
oidcConfig: string;
|
|
123
|
+
samlConfig: string;
|
|
124
|
+
userId: string;
|
|
125
|
+
organizationId: string;
|
|
126
|
+
domainVerified: string;
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=schema-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-names.d.ts","sourceRoot":"","sources":["../../src/auth/schema-names.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,SAAS;IAAK,SAAS;IAAW,MAAM;QAAI,aAAa;QAAoB,SAAS;QAAgB,SAAS;;CAAkB,CAAC;AAE/I,eAAO,MAAM,YAAY;IACvB,SAAS;IACT,MAAM;QAAI,SAAS;QAAgB,SAAS;QAAgB,SAAS;QAAgB,SAAS;QAAgB,SAAS;QAAgB,MAAM;;CAC9I,CAAC;AAEF,eAAO,MAAM,YAAY;IACvB,SAAS;IACT,MAAM;QACJ,SAAS;QACT,UAAU;QACV,MAAM;QACN,WAAW;QACX,YAAY;QACZ,OAAO;QACP,oBAAoB;QACpB,qBAAqB;QACrB,SAAS;QACT,SAAS;;CAEZ,CAAC;AAEF,eAAO,MAAM,iBAAiB;IAAK,SAAS;IAAmB,MAAM;QAAI,SAAS;QAAgB,SAAS;QAAgB,SAAS;;CAAkB,CAAC;AAEvJ,eAAO,MAAM,cAAc;IAAK,SAAS;IAAiB,MAAM;QAAI,WAAW;;CAAoB,CAAC;AAEpG,eAAO,MAAM,SAAS;aACpB,OAAO;iBAAI,MAAM;qBAAI,oBAAoB,EAAE,eAAe;;;aAC1D,YAAY;iBACV,SAAS,EAAE,MAAM;iBACjB,MAAM;qBAAI,SAAS,EAAE,YAAY;;iBACjC,gBAAgB;qBAAI,aAAa;yBAAI,IAAI,EAAE,QAAQ;yBAAE,QAAQ;yBAAQ,YAAY,EAAE,EAAE;yBAAE,KAAK;yBAAS,SAAS,EAAE,gBAAgB;;;;aAElI,MAAM;iBAAI,SAAS,EAAE,SAAS;iBAAE,MAAM;qBAAI,cAAc,EAAE,QAAQ;qBAAE,MAAM,EAAE,SAAS;qBAAE,SAAS,EAAE,YAAY;;;aAC9G,UAAU;iBAAI,SAAS,EAAE,SAAS;iBAAE,MAAM;qBAAI,cAAc,EAAE,QAAQ;qBAAE,SAAS,EAAE,YAAY;qBAAE,SAAS,EAAE,YAAY;qBAAE,SAAS,EAAE,YAAY;;;CACzI,CAAC;AAEX,eAAO,MAAM,eAAe;IAC1B,IAAI;QAAI,MAAM;YAAI,gBAAgB;;;IAClC,SAAS;QACP,SAAS;QACT,MAAM;YAAI,WAAW;YAAkB,MAAM;YAAa,uBAAuB;YAA+B,WAAW;;;CAE9H,CAAC;AAEF,eAAO,MAAM,aAAa;IACxB,OAAO;QACL,SAAS;QACT,MAAM;YAAI,SAAS;YAAgB,MAAM;YAAa,YAAY;YAAmB,UAAU;YAAiB,QAAQ;YAAe,SAAS;;;CAEnJ,CAAC;AAEF,eAAO,MAAM,SAAS;IACpB,WAAW;QACT,SAAS;QACT,MAAM;YAAI,UAAU;YAAiB,UAAU;YAAiB,UAAU;YAAiB,MAAM;YAAa,cAAc;YAAY,cAAc;;;CAEzJ,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Better Auth's models mapped onto our snake_case tables (db/schema-auth.ts). A test checks Better Auth finds nothing to change.
|
|
2
|
+
export const userNames = { modelName: "users", fields: { emailVerified: "email_verified", createdAt: "created_at", updatedAt: "updated_at" } };
|
|
3
|
+
export const sessionNames = {
|
|
4
|
+
modelName: "sessions",
|
|
5
|
+
fields: { expiresAt: "expires_at", createdAt: "created_at", updatedAt: "updated_at", ipAddress: "ip_address", userAgent: "user_agent", userId: "user_id" },
|
|
6
|
+
};
|
|
7
|
+
export const accountNames = {
|
|
8
|
+
modelName: "accounts",
|
|
9
|
+
fields: {
|
|
10
|
+
accountId: "account_id",
|
|
11
|
+
providerId: "provider_id",
|
|
12
|
+
userId: "user_id",
|
|
13
|
+
accessToken: "access_token",
|
|
14
|
+
refreshToken: "refresh_token",
|
|
15
|
+
idToken: "id_token",
|
|
16
|
+
accessTokenExpiresAt: "access_token_expires_at",
|
|
17
|
+
refreshTokenExpiresAt: "refresh_token_expires_at",
|
|
18
|
+
createdAt: "created_at",
|
|
19
|
+
updatedAt: "updated_at",
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export const verificationNames = { modelName: "verifications", fields: { expiresAt: "expires_at", createdAt: "created_at", updatedAt: "updated_at" } };
|
|
23
|
+
export const rateLimitNames = { modelName: "rate_limits", fields: { lastRequest: "last_request" } };
|
|
24
|
+
export const orgSchema = {
|
|
25
|
+
session: { fields: { activeOrganizationId: "active_org_id" } },
|
|
26
|
+
organization: {
|
|
27
|
+
modelName: "orgs",
|
|
28
|
+
fields: { createdAt: "created_at" },
|
|
29
|
+
additionalFields: { retentionDays: { type: "number", required: true, defaultValue: 30, input: false, fieldName: "retention_days" } },
|
|
30
|
+
},
|
|
31
|
+
member: { modelName: "members", fields: { organizationId: "org_id", userId: "user_id", createdAt: "created_at" } },
|
|
32
|
+
invitation: { modelName: "invites", fields: { organizationId: "org_id", expiresAt: "expires_at", createdAt: "created_at", inviterId: "inviter_id" } },
|
|
33
|
+
};
|
|
34
|
+
export const twoFactorSchema = {
|
|
35
|
+
user: { fields: { twoFactorEnabled: "two_factor_enabled" } },
|
|
36
|
+
twoFactor: {
|
|
37
|
+
modelName: "two_factors",
|
|
38
|
+
fields: { backupCodes: "backup_codes", userId: "user_id", failedVerificationCount: "failed_verification_count", lockedUntil: "locked_until" },
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
export const passkeySchema = {
|
|
42
|
+
passkey: {
|
|
43
|
+
modelName: "passkeys",
|
|
44
|
+
fields: { publicKey: "public_key", userId: "user_id", credentialID: "credential_id", deviceType: "device_type", backedUp: "backed_up", createdAt: "created_at" },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
export const ssoSchema = {
|
|
48
|
+
ssoProvider: {
|
|
49
|
+
modelName: "sso_providers",
|
|
50
|
+
fields: { providerId: "provider_id", oidcConfig: "oidc_config", samlConfig: "saml_config", userId: "user_id", organizationId: "org_id", domainVerified: "domain_verified" },
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=schema-names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-names.js","sourceRoot":"","sources":["../../src/auth/schema-names.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Db } from "../db/db.js";
|
|
2
|
+
export interface AuthSecret {
|
|
3
|
+
version: number;
|
|
4
|
+
value: string;
|
|
5
|
+
}
|
|
6
|
+
/** "value", or "2:new,1:old" to rotate (newest first). Rotating signs everyone out; encrypted 2FA data still opens with the old one. */
|
|
7
|
+
export declare function parseSecrets(text: string): AuthSecret[];
|
|
8
|
+
/** The configured secrets. A trial on PGlite without one gets a random secret kept in its own database; Postgres must be given one. */
|
|
9
|
+
export declare function resolveSecrets(db: Db, configured: string | undefined): Promise<AuthSecret[]>;
|
|
10
|
+
/** Better Auth reads these over its options; clear them so only our config counts. */
|
|
11
|
+
export declare function clearAuthEnv(env?: NodeJS.ProcessEnv): void;
|
|
12
|
+
//# sourceMappingURL=secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../src/auth/secret.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAID,wIAAwI;AACxI,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAQvD;AAED,uIAAuI;AACvI,wBAAsB,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAQlG;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAEvE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
const MIN_LENGTH = 32;
|
|
3
|
+
/** "value", or "2:new,1:old" to rotate (newest first). Rotating signs everyone out; encrypted 2FA data still opens with the old one. */
|
|
4
|
+
export function parseSecrets(text) {
|
|
5
|
+
const parts = text.split(",").map((s) => s.trim()).filter(Boolean);
|
|
6
|
+
const secrets = parts.map((p, i) => {
|
|
7
|
+
const m = /^(\d+):(.+)$/.exec(p);
|
|
8
|
+
return m ? { version: Number(m[1]), value: m[2] } : { version: parts.length - i, value: p };
|
|
9
|
+
});
|
|
10
|
+
if (secrets.length === 0 || secrets.some((s) => s.value.length < MIN_LENGTH))
|
|
11
|
+
throw new Error(`Each auth secret needs at least ${MIN_LENGTH} characters (try: openssl rand -base64 32).`);
|
|
12
|
+
return secrets;
|
|
13
|
+
}
|
|
14
|
+
/** The configured secrets. A trial on PGlite without one gets a random secret kept in its own database; Postgres must be given one. */
|
|
15
|
+
export async function resolveSecrets(db, configured) {
|
|
16
|
+
if (configured)
|
|
17
|
+
return parseSecrets(configured);
|
|
18
|
+
if (db.kind === "postgres")
|
|
19
|
+
throw new Error("Set --auth-secret (or WIZELENS_CLOUD_AUTH_SECRET) to 32 or more random characters: it signs sessions and encrypts 2FA secrets.");
|
|
20
|
+
const [row] = await db.query("SELECT value FROM cloud_meta WHERE key = 'auth_secret'");
|
|
21
|
+
if (row)
|
|
22
|
+
return parseSecrets(row.value);
|
|
23
|
+
const value = randomBytes(36).toString("base64url");
|
|
24
|
+
await db.query("INSERT INTO cloud_meta (key, value) VALUES ('auth_secret', $1) ON CONFLICT (key) DO NOTHING", [value]);
|
|
25
|
+
return resolveSecrets(db, undefined);
|
|
26
|
+
}
|
|
27
|
+
/** Better Auth reads these over its options; clear them so only our config counts. */
|
|
28
|
+
export function clearAuthEnv(env = process.env) {
|
|
29
|
+
for (const k of ["BETTER_AUTH_TELEMETRY", "BETTER_AUTH_TELEMETRY_ENDPOINT", "BETTER_AUTH_TELEMETRY_DEBUG", "BETTER_AUTH_SECRET", "BETTER_AUTH_SECRETS", "AUTH_SECRET", "BETTER_AUTH_URL"])
|
|
30
|
+
delete env[k];
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=secret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.js","sourceRoot":"","sources":["../../src/auth/secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ1C,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB,wIAAwI;AACxI,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC/F,CAAC,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,UAAU,6CAA6C,CAAC,CAAC;IAC1L,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uIAAuI;AACvI,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAM,EAAE,UAA8B;IACzE,IAAI,UAAU;QAAE,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,EAAE,CAAC,IAAI,KAAK,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,gIAAgI,CAAC,CAAC;IAC9K,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CAAoB,wDAAwD,CAAC,CAAC;IAC1G,IAAI,GAAG;QAAE,OAAO,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,EAAE,CAAC,KAAK,CAAC,6FAA6F,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IACvH,OAAO,cAAc,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;AACvC,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,YAAY,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG;IAC/D,KAAK,MAAM,CAAC,IAAI,CAAC,uBAAuB,EAAE,gCAAgC,EAAE,6BAA6B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,iBAAiB,CAAC;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;AAC3M,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getSessionFromCtx } from "better-auth/api";
|
|
2
|
+
import type { BetterAuthOptions } from "better-auth";
|
|
3
|
+
import type { EmailContent } from "../mail/content.js";
|
|
4
|
+
import * as security from "../mail/emails-security.js";
|
|
5
|
+
import type { AuthDeps } from "./deps.js";
|
|
6
|
+
type DbHooks = NonNullable<BetterAuthOptions["databaseHooks"]>;
|
|
7
|
+
/** "Chrome on Windows". Versions are left out, so a browser update is not a new device. */
|
|
8
|
+
export declare function deviceOf(ua: string | null | undefined): string;
|
|
9
|
+
export declare const linksOf: (deps: Pick<AuthDeps, "publicUrl">) => security.SecurityLinks;
|
|
10
|
+
/** One security notice to the person's address now. */
|
|
11
|
+
export declare function sendSecurity(deps: AuthDeps, userId: string, content: (l: security.SecurityLinks) => EmailContent, to?: string): Promise<void>;
|
|
12
|
+
/** Database hooks for sessions and user changes: new devices, two-step sign-in on or off, and a changed email. */
|
|
13
|
+
export declare function securityDbHooks(deps: AuthDeps): Pick<DbHooks, "session" | "user">;
|
|
14
|
+
/** Before a passkey goes, keep its name for the notice. */
|
|
15
|
+
export declare function rememberPasskey(ctx: {
|
|
16
|
+
request?: Request;
|
|
17
|
+
body?: unknown;
|
|
18
|
+
context: {
|
|
19
|
+
adapter: {
|
|
20
|
+
findOne<T>(a: unknown): Promise<T | null>;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
type AfterCtx = Parameters<typeof getSessionFromCtx>[0] & {
|
|
25
|
+
path: string;
|
|
26
|
+
request?: Request;
|
|
27
|
+
context: {
|
|
28
|
+
returned?: unknown;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/** Notices from endpoints: a changed password, a passkey added or removed, new backup codes. */
|
|
32
|
+
export declare function securityAfter(deps: AuthDeps, ctx: AfterCtx): Promise<void>;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=security-notices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-notices.d.ts","sourceRoot":"","sources":["../../src/auth/security-notices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,QAAQ,MAAM,4BAA4B,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1C,KAAK,OAAO,GAAG,WAAW,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC,CAAC;AAM/D,2FAA2F;AAC3F,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAI9D;AAED,eAAO,MAAM,OAAO,SAAU,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,KAAG,QAAQ,CAAC,aAGnE,CAAC;AAEH,uDAAuD;AACvD,wBAAsB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,aAAa,KAAK,YAAY,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnJ;AAwBD,kHAAkH;AAClH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC,CAUjF;AAID,2DAA2D;AAC3D,wBAAsB,eAAe,CAAC,GAAG,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE;QAAE,OAAO,EAAE;YAAE,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAKpK;AAED,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAAC;AAE/H,gGAAgG;AAChG,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getSessionFromCtx } from "better-auth/api";
|
|
2
|
+
import * as security from "../mail/emails-security.js";
|
|
3
|
+
import { renderMail } from "../mail/templates.js";
|
|
4
|
+
import { changeOf } from "./email-token.js";
|
|
5
|
+
const BROWSERS = [[/Edg\//, "Edge"], [/OPR\//, "Opera"], [/Firefox\//, "Firefox"], [/Chrome\//, "Chrome"], [/Safari\//, "Safari"]];
|
|
6
|
+
const SYSTEMS = [[/iPhone|iPad/, "iOS"], [/Android/, "Android"], [/Windows/, "Windows"], [/Mac OS X|Macintosh/, "macOS"], [/CrOS/, "ChromeOS"], [/Linux/, "Linux"]];
|
|
7
|
+
/** "Chrome on Windows". Versions are left out, so a browser update is not a new device. */
|
|
8
|
+
export function deviceOf(ua) {
|
|
9
|
+
const browser = BROWSERS.find(([r]) => r.test(ua ?? ""))?.[1];
|
|
10
|
+
const system = SYSTEMS.find(([r]) => r.test(ua ?? ""))?.[1];
|
|
11
|
+
return browser && system ? `${browser} on ${system}` : browser || system || "an unknown device";
|
|
12
|
+
}
|
|
13
|
+
export const linksOf = (deps) => ({
|
|
14
|
+
account: `${deps.publicUrl}/_wizelens/#/account`,
|
|
15
|
+
forgot: `${deps.publicUrl}/_wizelens/#/forgot-password`,
|
|
16
|
+
});
|
|
17
|
+
/** One security notice to the person's address now. */
|
|
18
|
+
export async function sendSecurity(deps, userId, content, to) {
|
|
19
|
+
const [u] = to ? [{ email: to }] : await deps.db.query("SELECT email FROM users WHERE id = $1", [userId]);
|
|
20
|
+
if (u)
|
|
21
|
+
deps.mailer.send(renderMail(u.email, content(linksOf(deps))));
|
|
22
|
+
}
|
|
23
|
+
/** A sign-in on a browser and system this person never used gets a notice. The first device (sign-up) does not. */
|
|
24
|
+
async function onSession(deps, s) {
|
|
25
|
+
const device = deviceOf(s.userAgent);
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
const fresh = await deps.db.query("INSERT INTO known_devices (user_id, device, first_ms, last_ms) VALUES ($1, $2, $3, $3) ON CONFLICT DO NOTHING RETURNING device", [s.userId, device, now]);
|
|
28
|
+
if (fresh.length === 0)
|
|
29
|
+
return void (await deps.db.query("UPDATE known_devices SET last_ms = $3 WHERE user_id = $1 AND device = $2", [s.userId, device, now]));
|
|
30
|
+
const [n] = await deps.db.query("SELECT count(*)::int AS n FROM known_devices WHERE user_id = $1", [s.userId]);
|
|
31
|
+
if ((n?.n ?? 0) > 1)
|
|
32
|
+
await sendSecurity(deps, s.userId, (l) => security.newSignIn(l, device, s.ipAddress ?? "", now));
|
|
33
|
+
}
|
|
34
|
+
/** What a user update is about to change, kept until it is saved. */
|
|
35
|
+
const pending = new WeakMap();
|
|
36
|
+
async function onUserUpdated(deps, user, ctx) {
|
|
37
|
+
const change = ctx ? pending.get(ctx) : undefined;
|
|
38
|
+
if (!change)
|
|
39
|
+
return;
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
if (typeof change.twoFactorEnabled === "boolean")
|
|
42
|
+
await sendSecurity(deps, user.id, (l) => security.twoFactorChanged(l, change.twoFactorEnabled, now));
|
|
43
|
+
const moved = changeOf(ctx?.query?.token);
|
|
44
|
+
if (typeof change.email === "string" && moved && moved.from !== user.email)
|
|
45
|
+
await sendSecurity(deps, user.id, (l) => security.emailChanged(l, user.email, now), moved.from);
|
|
46
|
+
}
|
|
47
|
+
/** Database hooks for sessions and user changes: new devices, two-step sign-in on or off, and a changed email. */
|
|
48
|
+
export function securityDbHooks(deps) {
|
|
49
|
+
return {
|
|
50
|
+
session: { create: { after: async (s) => onSession(deps, s) } },
|
|
51
|
+
user: {
|
|
52
|
+
update: {
|
|
53
|
+
before: async (data, ctx) => void (ctx && pending.set(ctx, data)),
|
|
54
|
+
after: async (user, ctx) => onUserUpdated(deps, user, ctx),
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const PASSKEY_NAMES = new WeakMap();
|
|
60
|
+
/** Before a passkey goes, keep its name for the notice. */
|
|
61
|
+
export async function rememberPasskey(ctx) {
|
|
62
|
+
const id = ctx.body?.id;
|
|
63
|
+
if (!ctx.request || typeof id !== "string")
|
|
64
|
+
return;
|
|
65
|
+
const row = await ctx.context.adapter.findOne({ model: "passkey", where: [{ field: "id", value: id }] });
|
|
66
|
+
PASSKEY_NAMES.set(ctx.request, row?.name ?? "");
|
|
67
|
+
}
|
|
68
|
+
/** Notices from endpoints: a changed password, a passkey added or removed, new backup codes. */
|
|
69
|
+
export async function securityAfter(deps, ctx) {
|
|
70
|
+
const notices = {
|
|
71
|
+
"/change-password": (l, now) => security.passwordChanged(l, now),
|
|
72
|
+
"/passkey/verify-registration": (l, now) => security.passkeyChanged(l, true, ctx.context.returned?.name ?? "", now),
|
|
73
|
+
"/passkey/delete-passkey": (l, now) => security.passkeyChanged(l, false, (ctx.request && PASSKEY_NAMES.get(ctx.request)) ?? "", now),
|
|
74
|
+
"/two-factor/generate-backup-codes": (l, now) => security.backupCodesMade(l, now),
|
|
75
|
+
};
|
|
76
|
+
const make = notices[ctx.path];
|
|
77
|
+
const user = make && (await getSessionFromCtx(ctx))?.user;
|
|
78
|
+
if (make && user)
|
|
79
|
+
await sendSecurity(deps, user.id, (l) => make(l, Date.now()));
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=security-notices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-notices.js","sourceRoot":"","sources":["../../src/auth/security-notices.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type BetterAuthOptions } from "better-auth";
|
|
2
|
+
import type { AuthDeps } from "./deps.js";
|
|
3
|
+
type UserOptions = NonNullable<BetterAuthOptions["user"]>;
|
|
4
|
+
type Validate = NonNullable<UserOptions["validateUserInfo"]>;
|
|
5
|
+
export declare const SETUP_HEADER = "x-wizelens-setup-token";
|
|
6
|
+
export declare const INVITE_HEADER = "x-wizelens-invite";
|
|
7
|
+
/** Who may make an account: by SSO, only from a proven domain; the first needs the setup token; then the invited, or anyone with open sign-up. */
|
|
8
|
+
export declare function signupGate(deps: AuthDeps): Validate;
|
|
9
|
+
/** The provider proved the domain and the gate checked the email, so an SSO account starts confirmed. */
|
|
10
|
+
export declare function confirmSsoEmail<U extends {
|
|
11
|
+
email: string;
|
|
12
|
+
emailVerified?: boolean;
|
|
13
|
+
}>(user: U, ctx: unknown): Promise<{
|
|
14
|
+
data: U;
|
|
15
|
+
} | undefined>;
|
|
16
|
+
/** Runs after the new user is saved: the first account and open sign-ups get their own organization and project. */
|
|
17
|
+
export declare function afterSignup(deps: AuthDeps, createOrg: (userId: string, name: string) => Promise<unknown>): (user: {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
email: string;
|
|
21
|
+
}, ctx: unknown) => Promise<void>;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=signup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup.d.ts","sourceRoot":"","sources":["../../src/auth/signup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,iBAAiB,EAAqB,MAAM,aAAa,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG1C,KAAK,WAAW,GAAG,WAAW,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;AAC1D,KAAK,QAAQ,GAAG,WAAW,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAG7D,eAAO,MAAM,YAAY,2BAA2B,CAAC;AACrD,eAAO,MAAM,aAAa,sBAAsB,CAAC;AAmBjD,kJAAkJ;AAClJ,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAcnD;AAKD,yGAAyG;AACzG,wBAAsB,eAAe,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG,SAAS,CAAC,CAKnJ;AAED,oHAAoH;AACpH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,UACnF;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,OAAO,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC,CAW9F"}
|