@ipul-labs/forge 1.22.0 → 1.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +153 -78
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/index-CNvSOdpC.js +27 -0
- package/dist/assets/index-CSH1OQYQ.css +1 -0
- package/dist/boot-check.js +98 -0
- package/dist/favicon.svg +8 -0
- package/dist/icon-192.png +0 -0
- package/dist/icon-512.png +0 -0
- package/dist/icon.svg +12 -0
- package/dist/index.html +33 -0
- package/dist/manifest.webmanifest +18 -0
- package/dist/theme-init.js +11 -0
- package/lib/bin/cli/analyze.d.ts.map +1 -1
- package/lib/bin/cli/analyze.js +3 -44
- package/lib/bin/cli/analyze.js.map +1 -1
- package/lib/bin/cli/apply.d.ts.map +1 -1
- package/lib/bin/cli/apply.js +2 -20
- package/lib/bin/cli/apply.js.map +1 -1
- package/lib/bin/cli/cifix.d.ts.map +1 -1
- package/lib/bin/cli/cifix.js +4 -28
- package/lib/bin/cli/cifix.js.map +1 -1
- package/lib/bin/cli/close.d.ts.map +1 -1
- package/lib/bin/cli/close.js +2 -33
- package/lib/bin/cli/close.js.map +1 -1
- package/lib/bin/cli/createIssue.d.ts.map +1 -1
- package/lib/bin/cli/createIssue.js +3 -12
- package/lib/bin/cli/createIssue.js.map +1 -1
- package/lib/bin/cli/doctor.d.ts.map +1 -1
- package/lib/bin/cli/doctor.js +3 -4
- package/lib/bin/cli/doctor.js.map +1 -1
- package/lib/bin/cli/init.js +1 -10
- package/lib/bin/cli/init.js.map +1 -1
- package/lib/bin/cli/issue.d.ts.map +1 -1
- package/lib/bin/cli/issue.js +3 -12
- package/lib/bin/cli/issue.js.map +1 -1
- package/lib/bin/cli/issues.d.ts.map +1 -1
- package/lib/bin/cli/issues.js +3 -12
- package/lib/bin/cli/issues.js.map +1 -1
- package/lib/bin/cli/merge.d.ts.map +1 -1
- package/lib/bin/cli/merge.js +4 -20
- package/lib/bin/cli/merge.js.map +1 -1
- package/lib/bin/cli/refine.d.ts.map +1 -1
- package/lib/bin/cli/refine.js +2 -26
- package/lib/bin/cli/refine.js.map +1 -1
- package/lib/bin/cli/repl.d.ts.map +1 -1
- package/lib/bin/cli/repl.js +4 -13
- package/lib/bin/cli/repl.js.map +1 -1
- package/lib/bin/cli/repos.d.ts.map +1 -1
- package/lib/bin/cli/repos.js +1 -11
- package/lib/bin/cli/repos.js.map +1 -1
- package/lib/bin/cli/resetPassword.d.ts +21 -0
- package/lib/bin/cli/resetPassword.d.ts.map +1 -0
- package/lib/bin/cli/resetPassword.js +69 -0
- package/lib/bin/cli/resetPassword.js.map +1 -0
- package/lib/bin/cli/resolution.d.ts.map +1 -1
- package/lib/bin/cli/resolution.js +2 -19
- package/lib/bin/cli/resolution.js.map +1 -1
- package/lib/bin/cli/serverClient.d.ts +2 -0
- package/lib/bin/cli/serverClient.d.ts.map +1 -1
- package/lib/bin/cli/serverClient.js +8 -4
- package/lib/bin/cli/serverClient.js.map +1 -1
- package/lib/bin/cli/session.d.ts.map +1 -1
- package/lib/bin/cli/session.js.map +1 -1
- package/lib/bin/cli/sessionStore.d.ts +1 -1
- package/lib/bin/cli/sessionStore.js +2 -2
- package/lib/bin/cli/sessionStore.js.map +1 -1
- package/lib/bin/cli/ship.d.ts.map +1 -1
- package/lib/bin/cli/ship.js +15 -23
- package/lib/bin/cli/ship.js.map +1 -1
- package/lib/bin/cli/update.d.ts.map +1 -1
- package/lib/bin/cli/update.js +34 -28
- package/lib/bin/cli/update.js.map +1 -1
- package/lib/bin/cli/updatePlan.d.ts +30 -0
- package/lib/bin/cli/updatePlan.d.ts.map +1 -0
- package/lib/bin/cli/updatePlan.js +50 -0
- package/lib/bin/cli/updatePlan.js.map +1 -0
- package/lib/bin/cli/utils.d.ts +3 -0
- package/lib/bin/cli/utils.d.ts.map +1 -1
- package/lib/bin/cli/utils.js +7 -23
- package/lib/bin/cli/utils.js.map +1 -1
- package/lib/bin/cli/validate.d.ts.map +1 -1
- package/lib/bin/cli/validate.js +3 -25
- package/lib/bin/cli/validate.js.map +1 -1
- package/lib/bin/cli/watch.d.ts.map +1 -1
- package/lib/bin/cli/watch.js +3 -12
- package/lib/bin/cli/watch.js.map +1 -1
- package/lib/bin/forge-agent.js +10 -22
- package/lib/bin/forge-agent.js.map +1 -1
- package/lib/server/app.d.ts +24 -0
- package/lib/server/app.d.ts.map +1 -0
- package/lib/server/app.js +58 -0
- package/lib/server/app.js.map +1 -0
- package/lib/server/forge/analyses/analysisStore.d.ts +53 -0
- package/lib/server/forge/analyses/analysisStore.d.ts.map +1 -0
- package/lib/server/forge/analyses/analysisStore.js +77 -0
- package/lib/server/forge/analyses/analysisStore.js.map +1 -0
- package/lib/server/forge/auth/apiTokens.d.ts +34 -0
- package/lib/server/forge/auth/apiTokens.d.ts.map +1 -0
- package/lib/server/forge/auth/apiTokens.js +44 -0
- package/lib/server/forge/auth/apiTokens.js.map +1 -0
- package/lib/server/forge/auth/auditLog.d.ts +36 -0
- package/lib/server/forge/auth/auditLog.d.ts.map +1 -0
- package/lib/server/forge/auth/auditLog.js +56 -0
- package/lib/server/forge/auth/auditLog.js.map +1 -0
- package/lib/server/forge/auth/authConfig.d.ts +13 -0
- package/lib/server/forge/auth/authConfig.d.ts.map +1 -0
- package/lib/server/forge/auth/authConfig.js +37 -0
- package/lib/server/forge/auth/authConfig.js.map +1 -0
- package/lib/server/forge/auth/createAuth.d.ts +130 -0
- package/lib/server/forge/auth/createAuth.d.ts.map +1 -0
- package/lib/server/forge/auth/createAuth.js +387 -0
- package/lib/server/forge/auth/createAuth.js.map +1 -0
- package/lib/server/forge/auth/middleware.d.ts +10 -0
- package/lib/server/forge/auth/middleware.d.ts.map +1 -0
- package/lib/server/forge/auth/middleware.js +22 -0
- package/lib/server/forge/auth/middleware.js.map +1 -0
- package/lib/server/forge/auth/oidc.d.ts +38 -0
- package/lib/server/forge/auth/oidc.d.ts.map +1 -0
- package/lib/server/forge/auth/oidc.js +63 -0
- package/lib/server/forge/auth/oidc.js.map +1 -0
- package/lib/server/forge/auth/passwords.d.ts +7 -0
- package/lib/server/forge/auth/passwords.d.ts.map +1 -0
- package/lib/server/forge/auth/passwords.js +28 -0
- package/lib/server/forge/auth/passwords.js.map +1 -0
- package/lib/server/forge/auth/resetPassword.d.ts +14 -0
- package/lib/server/forge/auth/resetPassword.d.ts.map +1 -0
- package/lib/server/forge/auth/resetPassword.js +29 -0
- package/lib/server/forge/auth/resetPassword.js.map +1 -0
- package/lib/server/forge/auth/roles.d.ts +7 -0
- package/lib/server/forge/auth/roles.d.ts.map +1 -0
- package/lib/server/forge/auth/roles.js +15 -0
- package/lib/server/forge/auth/roles.js.map +1 -0
- package/lib/server/forge/auth/sessions.d.ts +27 -0
- package/lib/server/forge/auth/sessions.d.ts.map +1 -0
- package/lib/server/forge/auth/sessions.js +37 -0
- package/lib/server/forge/auth/sessions.js.map +1 -0
- package/lib/server/forge/auth/testHarness.d.ts +104 -0
- package/lib/server/forge/auth/testHarness.d.ts.map +1 -0
- package/lib/server/forge/auth/testHarness.js +53 -0
- package/lib/server/forge/auth/testHarness.js.map +1 -0
- package/lib/server/forge/auth/tokenHash.d.ts +3 -0
- package/lib/server/forge/auth/tokenHash.d.ts.map +1 -0
- package/lib/server/forge/auth/tokenHash.js +4 -0
- package/lib/server/forge/auth/tokenHash.js.map +1 -0
- package/lib/server/forge/auth/users.d.ts +51 -0
- package/lib/server/forge/auth/users.d.ts.map +1 -0
- package/lib/server/forge/auth/users.js +118 -0
- package/lib/server/forge/auth/users.js.map +1 -0
- package/lib/server/forge/core/ForgeAgent.d.ts +43 -1
- package/lib/server/forge/core/ForgeAgent.d.ts.map +1 -1
- package/lib/server/forge/core/ForgeAgent.js +209 -61
- package/lib/server/forge/core/ForgeAgent.js.map +1 -1
- package/lib/server/forge/core/agentClient.d.ts +7 -23
- package/lib/server/forge/core/agentClient.d.ts.map +1 -1
- package/lib/server/forge/core/agentClient.js +10 -25
- package/lib/server/forge/core/agentClient.js.map +1 -1
- package/lib/server/forge/core/claudeClient.d.ts +24 -1
- package/lib/server/forge/core/claudeClient.d.ts.map +1 -1
- package/lib/server/forge/core/claudeClient.js +25 -7
- package/lib/server/forge/core/claudeClient.js.map +1 -1
- package/lib/server/forge/core/configStore.d.ts +11 -1
- package/lib/server/forge/core/configStore.d.ts.map +1 -1
- package/lib/server/forge/core/configStore.js +33 -2
- package/lib/server/forge/core/configStore.js.map +1 -1
- package/lib/server/forge/core/dependencyPaths.d.ts +19 -0
- package/lib/server/forge/core/dependencyPaths.d.ts.map +1 -0
- package/lib/server/forge/core/dependencyPaths.js +32 -0
- package/lib/server/forge/core/dependencyPaths.js.map +1 -0
- package/lib/server/forge/core/evidence.d.ts +43 -0
- package/lib/server/forge/core/evidence.d.ts.map +1 -0
- package/lib/server/forge/core/evidence.js +108 -0
- package/lib/server/forge/core/evidence.js.map +1 -0
- package/lib/server/forge/core/jobs.d.ts +2 -0
- package/lib/server/forge/core/jobs.d.ts.map +1 -1
- package/lib/server/forge/core/jobs.js +8 -0
- package/lib/server/forge/core/jobs.js.map +1 -1
- package/lib/server/forge/core/localRepo.d.ts +31 -10
- package/lib/server/forge/core/localRepo.d.ts.map +1 -1
- package/lib/server/forge/core/localRepo.js +48 -82
- package/lib/server/forge/core/localRepo.js.map +1 -1
- package/lib/server/forge/core/prompts.d.ts +3 -1
- package/lib/server/forge/core/prompts.d.ts.map +1 -1
- package/lib/server/forge/core/prompts.js +14 -5
- package/lib/server/forge/core/prompts.js.map +1 -1
- package/lib/server/forge/core/providers/azuredevops.d.ts +4 -2
- package/lib/server/forge/core/providers/azuredevops.d.ts.map +1 -1
- package/lib/server/forge/core/providers/azuredevops.js +12 -2
- package/lib/server/forge/core/providers/azuredevops.js.map +1 -1
- package/lib/server/forge/core/providers/base.d.ts +29 -4
- package/lib/server/forge/core/providers/base.d.ts.map +1 -1
- package/lib/server/forge/core/providers/base.js +21 -3
- package/lib/server/forge/core/providers/base.js.map +1 -1
- package/lib/server/forge/core/providers/github.d.ts +7 -7
- package/lib/server/forge/core/providers/github.d.ts.map +1 -1
- package/lib/server/forge/core/providers/github.js +13 -4
- package/lib/server/forge/core/providers/github.js.map +1 -1
- package/lib/server/forge/core/providers/gitlab.d.ts +20 -3
- package/lib/server/forge/core/providers/gitlab.d.ts.map +1 -1
- package/lib/server/forge/core/providers/gitlab.js +65 -5
- package/lib/server/forge/core/providers/gitlab.js.map +1 -1
- package/lib/server/forge/core/providers/types.d.ts +20 -0
- package/lib/server/forge/core/providers/types.d.ts.map +1 -1
- package/lib/server/forge/core/secretBox.d.ts +17 -0
- package/lib/server/forge/core/secretBox.d.ts.map +1 -0
- package/lib/server/forge/core/secretBox.js +74 -0
- package/lib/server/forge/core/secretBox.js.map +1 -0
- package/lib/server/forge/core/types.d.ts +7 -1
- package/lib/server/forge/core/types.d.ts.map +1 -1
- package/lib/server/forge/core/validator/clone.d.ts +5 -4
- package/lib/server/forge/core/validator/clone.d.ts.map +1 -1
- package/lib/server/forge/core/validator/clone.js +6 -86
- package/lib/server/forge/core/validator/clone.js.map +1 -1
- package/lib/server/forge/core/validator/cmd.d.ts.map +1 -1
- package/lib/server/forge/core/validator/cmd.js +4 -1
- package/lib/server/forge/core/validator/cmd.js.map +1 -1
- package/lib/server/forge/core/validator/projectType.d.ts.map +1 -1
- package/lib/server/forge/core/validator/projectType.js +2 -1
- package/lib/server/forge/core/validator/projectType.js.map +1 -1
- package/lib/server/forge/core/validator/runners/node.d.ts.map +1 -1
- package/lib/server/forge/core/validator/runners/node.js +6 -2
- package/lib/server/forge/core/validator/runners/node.js.map +1 -1
- package/lib/server/forge/core/validator.d.ts.map +1 -1
- package/lib/server/forge/core/validator.js +28 -22
- package/lib/server/forge/core/validator.js.map +1 -1
- package/lib/server/forge/core/workspaces.d.ts +70 -0
- package/lib/server/forge/core/workspaces.d.ts.map +1 -0
- package/lib/server/forge/core/workspaces.js +348 -0
- package/lib/server/forge/core/workspaces.js.map +1 -0
- package/lib/server/forge/db/database.d.ts +22 -0
- package/lib/server/forge/db/database.d.ts.map +1 -0
- package/lib/server/forge/db/database.js +71 -0
- package/lib/server/forge/db/database.js.map +1 -0
- package/lib/server/forge/db/migrations.d.ts +11 -0
- package/lib/server/forge/db/migrations.d.ts.map +1 -0
- package/lib/server/forge/db/migrations.js +87 -0
- package/lib/server/forge/db/migrations.js.map +1 -0
- package/lib/server/forge/routes.d.ts.map +1 -1
- package/lib/server/forge/routes.js +413 -163
- package/lib/server/forge/routes.js.map +1 -1
- package/lib/server/forge/sse.d.ts +10 -0
- package/lib/server/forge/sse.d.ts.map +1 -0
- package/lib/server/forge/sse.js +22 -0
- package/lib/server/forge/sse.js.map +1 -0
- package/lib/server/spa.d.ts +12 -0
- package/lib/server/spa.d.ts.map +1 -0
- package/lib/server/spa.js +23 -0
- package/lib/server/spa.js.map +1 -0
- package/lib/server.js +9 -24
- package/lib/server.js.map +1 -1
- package/lib/shared/events.d.ts +3 -0
- package/lib/shared/events.d.ts.map +1 -1
- package/lib/shared/events.js +3 -0
- package/lib/shared/events.js.map +1 -1
- package/lib/shared/packageRoot.d.ts +11 -0
- package/lib/shared/packageRoot.d.ts.map +1 -0
- package/lib/shared/packageRoot.js +23 -0
- package/lib/shared/packageRoot.js.map +1 -0
- package/lib/shared/project.d.ts +3 -0
- package/lib/shared/project.d.ts.map +1 -0
- package/lib/shared/project.js +3 -0
- package/lib/shared/project.js.map +1 -0
- package/package.json +23 -10
- package/lib/bin/cli/improve.d.ts +0 -2
- package/lib/bin/cli/improve.d.ts.map +0 -1
- package/lib/bin/cli/improve.js +0 -189
- package/lib/bin/cli/improve.js.map +0 -1
- package/lib/bin/cli/stats.d.ts +0 -2
- package/lib/bin/cli/stats.d.ts.map +0 -1
- package/lib/bin/cli/stats.js +0 -78
- package/lib/bin/cli/stats.js.map +0 -1
- package/lib/server/forge/core/k8s.d.ts +0 -18
- package/lib/server/forge/core/k8s.d.ts.map +0 -1
- package/lib/server/forge/core/k8s.js +0 -49
- package/lib/server/forge/core/k8s.js.map +0 -1
- package/lib/server/forge/core/openclawClient.d.ts +0 -61
- package/lib/server/forge/core/openclawClient.d.ts.map +0 -1
- package/lib/server/forge/core/openclawClient.js +0 -111
- package/lib/server/forge/core/openclawClient.js.map +0 -1
- package/lib/server/forge/core/quotaClient.d.ts +0 -48
- package/lib/server/forge/core/quotaClient.d.ts.map +0 -1
- package/lib/server/forge/core/quotaClient.js +0 -88
- package/lib/server/forge/core/quotaClient.js.map +0 -1
- package/lib/server/forge/core/selfImprove.d.ts +0 -43
- package/lib/server/forge/core/selfImprove.d.ts.map +0 -1
- package/lib/server/forge/core/selfImprove.js +0 -372
- package/lib/server/forge/core/selfImprove.js.map +0 -1
- package/lib/server/forge/core/telemetry.d.ts +0 -100
- package/lib/server/forge/core/telemetry.d.ts.map +0 -1
- package/lib/server/forge/core/telemetry.js +0 -273
- package/lib/server/forge/core/telemetry.js.map +0 -1
- package/scripts/fix-shebang.cjs +0 -11
- package/scripts/inject-readme.cjs +0 -23
- package/scripts/local-rollout.sh +0 -134
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { randomInt, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import express from "express";
|
|
3
|
+
import { API_TOKEN_PREFIX, createApiTokens } from "./apiTokens.js";
|
|
4
|
+
import { createAuditLog } from "./auditLog.js";
|
|
5
|
+
import { createOidcClient, randomToken } from "./oidc.js";
|
|
6
|
+
import { requireRole } from "./middleware.js";
|
|
7
|
+
import { isRole } from "./roles.js";
|
|
8
|
+
import { createSessions } from "./sessions.js";
|
|
9
|
+
import { createUsers } from "./users.js";
|
|
10
|
+
const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
|
|
11
|
+
export const CSRF_HEADER = "x-forge-request";
|
|
12
|
+
const OIDC_STATE_TTL_MS = 10 * 60_000;
|
|
13
|
+
function safeEqual(a, b) {
|
|
14
|
+
const x = Buffer.from(a);
|
|
15
|
+
const y = Buffer.from(b);
|
|
16
|
+
return x.length === y.length && timingSafeEqual(x, y);
|
|
17
|
+
}
|
|
18
|
+
const LOOPBACK = new Set(["127.0.0.1", "::1", "::ffff:127.0.0.1"]);
|
|
19
|
+
const LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
20
|
+
const FORWARDING_HEADERS = ["x-forwarded-for", "forwarded", "x-real-ip", "x-forwarded-host"];
|
|
21
|
+
/**
|
|
22
|
+
* True only for a browser on the server machine talking to Forge directly:
|
|
23
|
+
* loopback socket, no proxy headers (a reverse proxy on the same host would
|
|
24
|
+
* otherwise make every visitor look local), and a localhost Host header (so a
|
|
25
|
+
* DNS-rebinding page can't pose as local).
|
|
26
|
+
*/
|
|
27
|
+
export function isLocalRequest(req) {
|
|
28
|
+
if (!LOOPBACK.has(req.socket.remoteAddress ?? ""))
|
|
29
|
+
return false;
|
|
30
|
+
if (FORWARDING_HEADERS.some((h) => req.headers[h] !== undefined))
|
|
31
|
+
return false;
|
|
32
|
+
const host = (req.headers.host ?? "").replace(/:\d+$/, "").toLowerCase();
|
|
33
|
+
return LOCAL_HOSTNAMES.has(host);
|
|
34
|
+
}
|
|
35
|
+
const CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
36
|
+
function generateSetupCode() {
|
|
37
|
+
const chunk = () => Array.from({ length: 4 }, () => CODE_ALPHABET[randomInt(CODE_ALPHABET.length)]).join("");
|
|
38
|
+
return `${chunk()}-${chunk()}-${chunk()}`;
|
|
39
|
+
}
|
|
40
|
+
function parseCookies(header) {
|
|
41
|
+
const out = {};
|
|
42
|
+
for (const part of header?.split(";") ?? []) {
|
|
43
|
+
const i = part.indexOf("=");
|
|
44
|
+
if (i > 0)
|
|
45
|
+
out[part.slice(0, i).trim()] = decodeURIComponent(part.slice(i + 1).trim());
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** Only same-origin relative paths; anything else (incl. `//host`) → "/". */
|
|
50
|
+
export function safeReturnTo(value) {
|
|
51
|
+
return typeof value === "string" && value.startsWith("/") && !value.startsWith("//") && !value.startsWith("/\\")
|
|
52
|
+
? value
|
|
53
|
+
: "/";
|
|
54
|
+
}
|
|
55
|
+
const userPrincipal = (u, kind) => ({
|
|
56
|
+
id: u.id,
|
|
57
|
+
email: u.email,
|
|
58
|
+
name: u.name,
|
|
59
|
+
role: u.role,
|
|
60
|
+
kind,
|
|
61
|
+
});
|
|
62
|
+
const errorPage = (message) => `<!doctype html><meta charset="utf-8"><title>Sign-in failed</title><body style="font-family:system-ui;max-width:28rem;margin:15vh auto;padding:0 1rem"><h1 style="font-size:1.1rem">Sign-in failed</h1><p>${message.replace(/[<>&]/g, "")}</p><p><a href="/">Back to Forge</a></p></body>`;
|
|
63
|
+
export function createAuth(opts) {
|
|
64
|
+
const { db, clock = Date.now } = opts;
|
|
65
|
+
const users = createUsers({ db, clock });
|
|
66
|
+
const sessions = createSessions({ db, clock, idleMs: opts.sessionIdleMs, absoluteMs: opts.sessionAbsoluteMs });
|
|
67
|
+
const tokens = createApiTokens({ db, clock });
|
|
68
|
+
const audit = createAuditLog({ db, clock });
|
|
69
|
+
const oidc = opts.oidc ? createOidcClient(opts.oidc) : null;
|
|
70
|
+
let oidcRedirectUri = opts.oidc?.redirectUri ?? "";
|
|
71
|
+
// __Host- pins the cookie to this exact origin; it requires Secure.
|
|
72
|
+
const cookieName = opts.cookieSecure ? "__Host-forge_session" : "forge_session";
|
|
73
|
+
const setSessionCookie = (res, token) => {
|
|
74
|
+
const attrs = [`${cookieName}=${token}`, "Path=/", "HttpOnly", "SameSite=Lax", `Max-Age=${Math.floor(sessions.absoluteMs / 1000)}`];
|
|
75
|
+
if (opts.cookieSecure)
|
|
76
|
+
attrs.push("Secure");
|
|
77
|
+
res.append("Set-Cookie", attrs.join("; "));
|
|
78
|
+
};
|
|
79
|
+
const clearSessionCookie = (res) => res.append("Set-Cookie", `${cookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${opts.cookieSecure ? "; Secure" : ""}`);
|
|
80
|
+
const clientMeta = (req) => ({ ip: req.ip ?? "", userAgent: String(req.headers["user-agent"] ?? "") });
|
|
81
|
+
const record = (req, e) => audit.record({
|
|
82
|
+
actorId: e.actor?.id ?? null,
|
|
83
|
+
actorLabel: e.actor?.email ?? e.actorLabel ?? "anonymous",
|
|
84
|
+
action: e.action,
|
|
85
|
+
outcome: e.outcome,
|
|
86
|
+
target: e.target ?? null,
|
|
87
|
+
details: e.details ?? null,
|
|
88
|
+
...clientMeta(req),
|
|
89
|
+
});
|
|
90
|
+
async function startSession(req, res, user) {
|
|
91
|
+
const { token } = await sessions.create({ userId: user.id, ...clientMeta(req) });
|
|
92
|
+
setSessionCookie(res, token);
|
|
93
|
+
}
|
|
94
|
+
// ── Middleware ───────────────────────────────────────────────────────────
|
|
95
|
+
async function resolvePrincipal(req) {
|
|
96
|
+
const header = req.headers.authorization;
|
|
97
|
+
if (header?.startsWith("Bearer ")) {
|
|
98
|
+
req.authVia = "bearer";
|
|
99
|
+
const value = header.slice(7).trim();
|
|
100
|
+
if (value.startsWith(API_TOKEN_PREFIX)) {
|
|
101
|
+
const hit = await tokens.resolve(value);
|
|
102
|
+
const user = hit && (await users.findById(hit.userId));
|
|
103
|
+
return user && !user.disabled ? userPrincipal(user, "token") : "invalid";
|
|
104
|
+
}
|
|
105
|
+
if (opts.serviceToken && safeEqual(value, opts.serviceToken)) {
|
|
106
|
+
return { id: "service", email: "service-token", name: "Service token", role: "admin", kind: "service" };
|
|
107
|
+
}
|
|
108
|
+
return "invalid";
|
|
109
|
+
}
|
|
110
|
+
const sessionToken = parseCookies(req.headers.cookie)[cookieName];
|
|
111
|
+
if (!sessionToken)
|
|
112
|
+
return null;
|
|
113
|
+
req.authVia = "cookie";
|
|
114
|
+
const session = await sessions.resolve(sessionToken);
|
|
115
|
+
const user = session && (await users.findById(session.userId));
|
|
116
|
+
if (!user || user.disabled)
|
|
117
|
+
return null;
|
|
118
|
+
return userPrincipal(user, "user");
|
|
119
|
+
}
|
|
120
|
+
const authenticate = async (req, res, next) => {
|
|
121
|
+
try {
|
|
122
|
+
const principal = await resolvePrincipal(req);
|
|
123
|
+
if (principal === "invalid") {
|
|
124
|
+
res.status(401).json({ error: "Invalid or revoked token." });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
req.principal = principal ?? undefined;
|
|
128
|
+
next();
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
next(err);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const requireUser = (req, res, next) => {
|
|
135
|
+
if (!req.principal) {
|
|
136
|
+
res.status(401).json({ error: "Sign in required." });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
next();
|
|
140
|
+
};
|
|
141
|
+
/** Cookie-authenticated writes must carry a header a cross-site form can't set. */
|
|
142
|
+
const csrf = (req, res, next) => {
|
|
143
|
+
if (req.authVia === "cookie" && !SAFE_METHODS.has(req.method) && req.headers[CSRF_HEADER] !== "1") {
|
|
144
|
+
res.status(403).json({ error: "Missing request header." });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
next();
|
|
148
|
+
};
|
|
149
|
+
/** Logs every state-changing API call once the response is sent. */
|
|
150
|
+
const auditTrail = (req, res, next) => {
|
|
151
|
+
if (SAFE_METHODS.has(req.method))
|
|
152
|
+
return next();
|
|
153
|
+
res.on("finish", () => {
|
|
154
|
+
if (res.locals.audited)
|
|
155
|
+
return;
|
|
156
|
+
const params = req.params ?? {};
|
|
157
|
+
const target = params.mrIid ? `!${params.mrIid}` : params.iid ? `#${params.iid}` : params.jobId ? `job ${params.jobId}` : null;
|
|
158
|
+
const repo = (req.body && typeof req.body.repo === "string" ? req.body.repo : null) ?? (typeof req.query.repo === "string" ? req.query.repo : null);
|
|
159
|
+
const outcome = res.statusCode < 400 ? "success" : res.statusCode === 401 || res.statusCode === 403 ? "denied" : "failure";
|
|
160
|
+
void audit
|
|
161
|
+
.record({
|
|
162
|
+
actorId: req.principal?.id ?? null,
|
|
163
|
+
actorLabel: req.principal?.email ?? "anonymous",
|
|
164
|
+
action: `${req.method} ${req.route?.path ?? req.path}`,
|
|
165
|
+
target,
|
|
166
|
+
repo,
|
|
167
|
+
outcome,
|
|
168
|
+
details: { status: res.statusCode },
|
|
169
|
+
...clientMeta(req),
|
|
170
|
+
})
|
|
171
|
+
.catch((err) => console.error("[forge] audit write failed:", err));
|
|
172
|
+
});
|
|
173
|
+
next();
|
|
174
|
+
};
|
|
175
|
+
// ── /auth ────────────────────────────────────────────────────────────────
|
|
176
|
+
// Remote first-run setup needs proof of operating the server: FORGE_TOKEN
|
|
177
|
+
// when set, otherwise a one-time code printed to the server log.
|
|
178
|
+
const setupCode = opts.serviceToken ?? generateSetupCode();
|
|
179
|
+
let announced = false;
|
|
180
|
+
const router = express.Router();
|
|
181
|
+
router.use(express.json({ limit: "64kb" }));
|
|
182
|
+
router.get("/me", authenticate, async (req, res) => {
|
|
183
|
+
res.json({
|
|
184
|
+
user: req.principal ?? null,
|
|
185
|
+
needsSetup: (await users.count()) === 0,
|
|
186
|
+
setupRequiresToken: !isLocalRequest(req),
|
|
187
|
+
sso: oidc ? { name: opts.oidc?.name ?? "SSO" } : null,
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
router.post("/setup", async (req, res) => {
|
|
191
|
+
const { email, name, password, setupToken } = req.body ?? {};
|
|
192
|
+
if ((await users.count()) > 0)
|
|
193
|
+
return res.status(409).json({ error: "Forge is already set up." });
|
|
194
|
+
if (!isLocalRequest(req) && (typeof setupToken !== "string" || !safeEqual(setupToken.trim().toUpperCase(), setupCode.toUpperCase()))) {
|
|
195
|
+
return res.status(403).json({ error: "The setup code is incorrect." });
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
const user = await users.create({ email, name: name ?? "", role: "admin", password });
|
|
199
|
+
await startSession(req, res, user);
|
|
200
|
+
await record(req, { action: "auth.setup", outcome: "success", actor: userPrincipal(user, "user") });
|
|
201
|
+
res.status(201).json({ user: userPrincipal(user, "user") });
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
res.status(400).json({ error: err.message });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
router.post("/login", async (req, res) => {
|
|
208
|
+
const { email, password } = req.body ?? {};
|
|
209
|
+
const result = await users.authenticate(String(email ?? ""), String(password ?? ""));
|
|
210
|
+
if ("reason" in result) {
|
|
211
|
+
await record(req, { action: "auth.login", outcome: "failure", actorLabel: String(email ?? "").toLowerCase(), details: { reason: result.reason } });
|
|
212
|
+
const message = result.reason === "locked"
|
|
213
|
+
? "Too many failed attempts. Try again in 15 minutes."
|
|
214
|
+
: result.reason === "disabled"
|
|
215
|
+
? "This account is disabled."
|
|
216
|
+
: "Invalid email or password.";
|
|
217
|
+
return res.status(401).json({ error: message });
|
|
218
|
+
}
|
|
219
|
+
const { user } = result;
|
|
220
|
+
await startSession(req, res, user);
|
|
221
|
+
await record(req, { action: "auth.login", outcome: "success", actor: userPrincipal(user, "user") });
|
|
222
|
+
res.json({ user: userPrincipal(user, "user") });
|
|
223
|
+
});
|
|
224
|
+
router.post("/logout", authenticate, csrf, async (req, res) => {
|
|
225
|
+
const token = parseCookies(req.headers.cookie)[cookieName];
|
|
226
|
+
if (token)
|
|
227
|
+
await sessions.revoke(token);
|
|
228
|
+
clearSessionCookie(res);
|
|
229
|
+
if (req.principal)
|
|
230
|
+
await record(req, { action: "auth.logout", outcome: "success", actor: req.principal });
|
|
231
|
+
res.json({ ok: true });
|
|
232
|
+
});
|
|
233
|
+
router.get("/tokens", authenticate, requireUser, async (req, res) => {
|
|
234
|
+
res.json({ tokens: await tokens.list(req.principal.id) });
|
|
235
|
+
});
|
|
236
|
+
router.post("/tokens", authenticate, requireUser, csrf, async (req, res) => {
|
|
237
|
+
if (req.principal.kind === "service")
|
|
238
|
+
return res.status(400).json({ error: "Service tokens can't mint personal tokens." });
|
|
239
|
+
const created = await tokens.create({ userId: req.principal.id, name: String(req.body?.name ?? "") });
|
|
240
|
+
await record(req, { action: "token.create", outcome: "success", actor: req.principal, target: created.record.prefix });
|
|
241
|
+
res.status(201).json(created);
|
|
242
|
+
});
|
|
243
|
+
router.delete("/tokens/:id", authenticate, requireUser, csrf, async (req, res) => {
|
|
244
|
+
const id = String(req.params.id);
|
|
245
|
+
const ok = await tokens.revoke({ id, userId: req.principal.id });
|
|
246
|
+
await record(req, { action: "token.revoke", outcome: ok ? "success" : "failure", actor: req.principal, target: id });
|
|
247
|
+
res.status(ok ? 200 : 404).json({ ok });
|
|
248
|
+
});
|
|
249
|
+
router.get("/oidc/start", async (req, res, next) => {
|
|
250
|
+
if (!oidc)
|
|
251
|
+
return res.status(404).json({ error: "Single sign-on is not configured." });
|
|
252
|
+
try {
|
|
253
|
+
const state = randomToken();
|
|
254
|
+
const nonce = randomToken();
|
|
255
|
+
const codeVerifier = randomToken();
|
|
256
|
+
await db.run("DELETE FROM oidc_states WHERE created_at < ?", [clock() - OIDC_STATE_TTL_MS]);
|
|
257
|
+
await db.run("INSERT INTO oidc_states (state, code_verifier, nonce, return_to, created_at) VALUES (?, ?, ?, ?, ?)", [
|
|
258
|
+
state,
|
|
259
|
+
codeVerifier,
|
|
260
|
+
nonce,
|
|
261
|
+
safeReturnTo(req.query.returnTo),
|
|
262
|
+
clock(),
|
|
263
|
+
]);
|
|
264
|
+
res.redirect(302, await oidc.authorizationUrl({ state, nonce, codeVerifier, redirectUri: oidcRedirectUri }));
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
next(err);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
router.get("/oidc/callback", async (req, res) => {
|
|
271
|
+
if (!oidc || !opts.oidc)
|
|
272
|
+
return res.status(404).send(errorPage("Single sign-on is not configured."));
|
|
273
|
+
const state = String(req.query.state ?? "");
|
|
274
|
+
const row = await db.get("SELECT * FROM oidc_states WHERE state = ?", [state]);
|
|
275
|
+
await db.run("DELETE FROM oidc_states WHERE state = ?", [state]);
|
|
276
|
+
if (!row || clock() - row.created_at > OIDC_STATE_TTL_MS) {
|
|
277
|
+
return res.status(400).send(errorPage("This sign-in link expired. Start again."));
|
|
278
|
+
}
|
|
279
|
+
if (req.query.error)
|
|
280
|
+
return res.status(400).send(errorPage(String(req.query.error_description ?? req.query.error)));
|
|
281
|
+
const fail = async (status, message, label) => {
|
|
282
|
+
await record(req, { action: "auth.sso_login", outcome: "failure", actorLabel: label, details: { reason: message } });
|
|
283
|
+
res.status(status).send(errorPage(message));
|
|
284
|
+
};
|
|
285
|
+
try {
|
|
286
|
+
const claims = await oidc.exchange({ code: String(req.query.code ?? ""), codeVerifier: row.code_verifier, nonce: row.nonce, redirectUri: oidcRedirectUri });
|
|
287
|
+
const email = claims.email?.toLowerCase();
|
|
288
|
+
if (!email || claims.email_verified === false)
|
|
289
|
+
return fail(403, "Your identity provider did not supply a verified email.", email ?? "unknown");
|
|
290
|
+
const domain = email.split("@")[1];
|
|
291
|
+
if (opts.oidc.allowedDomains.length && !opts.oidc.allowedDomains.includes(domain)) {
|
|
292
|
+
return fail(403, "Your email domain is not allowed to sign in to this Forge workspace.", email);
|
|
293
|
+
}
|
|
294
|
+
const user = await users.upsertOidc({ subject: `${claims.iss}|${claims.sub}`, email, name: claims.name ?? "", defaultRole: opts.oidc.defaultRole });
|
|
295
|
+
if (user.disabled)
|
|
296
|
+
return fail(403, "This account is disabled.", email);
|
|
297
|
+
await startSession(req, res, user);
|
|
298
|
+
await record(req, { action: "auth.sso_login", outcome: "success", actor: userPrincipal(user, "user") });
|
|
299
|
+
res.redirect(302, row.return_to);
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
await fail(400, err.message, "unknown");
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
// ── /admin ───────────────────────────────────────────────────────────────
|
|
306
|
+
const adminRouter = express.Router();
|
|
307
|
+
adminRouter.use(requireRole("admin"));
|
|
308
|
+
adminRouter.use((_req, res, next) => {
|
|
309
|
+
res.locals.audited = true; // admin routes write explicit, descriptive events
|
|
310
|
+
next();
|
|
311
|
+
});
|
|
312
|
+
adminRouter.get("/users", async (_req, res) => {
|
|
313
|
+
res.json({ users: await users.list() });
|
|
314
|
+
});
|
|
315
|
+
adminRouter.post("/users", async (req, res) => {
|
|
316
|
+
const { email, name, role, password } = req.body ?? {};
|
|
317
|
+
if (!isRole(role))
|
|
318
|
+
return res.status(400).json({ error: "Choose a valid role." });
|
|
319
|
+
try {
|
|
320
|
+
const user = await users.create({ email: String(email ?? ""), name: String(name ?? ""), role, password: password || undefined });
|
|
321
|
+
await record(req, { action: "user.create", outcome: "success", actor: req.principal, target: user.email, details: { role } });
|
|
322
|
+
res.status(201).json({ user });
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
res.status(400).json({ error: err.message });
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
adminRouter.patch("/users/:id", async (req, res) => {
|
|
329
|
+
const target = await users.findById(String(req.params.id));
|
|
330
|
+
if (!target)
|
|
331
|
+
return res.status(404).json({ error: "User not found." });
|
|
332
|
+
const { role, disabled, name, password } = req.body ?? {};
|
|
333
|
+
if (role !== undefined && !isRole(role))
|
|
334
|
+
return res.status(400).json({ error: "Choose a valid role." });
|
|
335
|
+
const losesAdmin = target.role === "admin" && !target.disabled && ((role !== undefined && role !== "admin") || disabled === true);
|
|
336
|
+
if (losesAdmin && (await users.countActiveAdmins()) <= 1) {
|
|
337
|
+
return res.status(409).json({ error: "Forge needs at least one active admin." });
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
const user = await users.update(target.id, { role, disabled, name, password });
|
|
341
|
+
if (disabled === true || password !== undefined) {
|
|
342
|
+
await sessions.revokeAllForUser(target.id);
|
|
343
|
+
if (disabled === true)
|
|
344
|
+
await tokens.revokeAllForUser(target.id);
|
|
345
|
+
}
|
|
346
|
+
const changes = Object.fromEntries(Object.entries({ role, disabled, name, password: password !== undefined ? "reset" : undefined }).filter(([, v]) => v !== undefined));
|
|
347
|
+
await record(req, { action: "user.update", outcome: "success", actor: req.principal, target: target.email, details: changes });
|
|
348
|
+
res.json({ user });
|
|
349
|
+
}
|
|
350
|
+
catch (err) {
|
|
351
|
+
res.status(400).json({ error: err.message });
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
adminRouter.get("/audit", async (req, res) => {
|
|
355
|
+
const n = (v) => (typeof v === "string" && v ? Number(v) : undefined);
|
|
356
|
+
res.json(await audit.list({
|
|
357
|
+
action: typeof req.query.action === "string" && req.query.action ? req.query.action : undefined,
|
|
358
|
+
actorId: typeof req.query.actorId === "string" && req.query.actorId ? req.query.actorId : undefined,
|
|
359
|
+
limit: n(req.query.limit),
|
|
360
|
+
before: n(req.query.before),
|
|
361
|
+
}));
|
|
362
|
+
});
|
|
363
|
+
return {
|
|
364
|
+
users,
|
|
365
|
+
sessions,
|
|
366
|
+
tokens,
|
|
367
|
+
audit,
|
|
368
|
+
router,
|
|
369
|
+
adminRouter,
|
|
370
|
+
authenticate,
|
|
371
|
+
requireUser,
|
|
372
|
+
requireRole,
|
|
373
|
+
csrf,
|
|
374
|
+
auditTrail,
|
|
375
|
+
/** Logs the setup code while no admin exists (only when it was generated). */
|
|
376
|
+
announceSetup: async () => {
|
|
377
|
+
if (opts.serviceToken || announced || (await users.count()) > 0)
|
|
378
|
+
return;
|
|
379
|
+
announced = true;
|
|
380
|
+
(opts.onSetupCode ?? ((code) => console.log(`[forge] First-run setup code (needed when not on localhost): ${code}`)))(setupCode);
|
|
381
|
+
},
|
|
382
|
+
setOidcRedirectUri: (uri) => {
|
|
383
|
+
oidcRedirectUri = uri;
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
//# sourceMappingURL=createAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAuth.js","sourceRoot":"","sources":["../../../../server/forge/auth/createAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from "express";
|
|
2
|
+
import { type Role } from "./roles.ts";
|
|
3
|
+
export type RoleGuard = ((req: Request, res: Response, next: NextFunction) => void) & {
|
|
4
|
+
requiredRole: Role;
|
|
5
|
+
};
|
|
6
|
+
/** Stateless: only reads `req.principal`, so routers can use it without the auth instance. */
|
|
7
|
+
export declare function requireRole(role: Role): RoleGuard;
|
|
8
|
+
/** Fail closed if a router is mounted without the auth stack in front of it. */
|
|
9
|
+
export declare function requirePrincipal(req: Request, res: Response, next: NextFunction): void;
|
|
10
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../../server/forge/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,EAAW,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG;IAAE,YAAY,EAAE,IAAI,CAAA;CAAE,CAAC;AAE7G,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAUjD;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAM/E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { hasRole } from "./roles.js";
|
|
2
|
+
/** Stateless: only reads `req.principal`, so routers can use it without the auth instance. */
|
|
3
|
+
export function requireRole(role) {
|
|
4
|
+
const guard = ((req, res, next) => {
|
|
5
|
+
if (!req.principal || !hasRole(req.principal.role, role)) {
|
|
6
|
+
res.status(req.principal ? 403 : 401).json({ error: req.principal ? `Requires the ${role} role.` : "Sign in required." });
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
next();
|
|
10
|
+
});
|
|
11
|
+
guard.requiredRole = role;
|
|
12
|
+
return guard;
|
|
13
|
+
}
|
|
14
|
+
/** Fail closed if a router is mounted without the auth stack in front of it. */
|
|
15
|
+
export function requirePrincipal(req, res, next) {
|
|
16
|
+
if (!req.principal) {
|
|
17
|
+
res.status(401).json({ error: "Sign in required." });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
next();
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../../server/forge/auth/middleware.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAa,MAAM,YAAY,CAAC;AAIhD,8FAA8F;AAC9F,MAAM,UAAU,WAAW,CAAC,IAAU;IACrC,MAAM,KAAK,GAAG,CAAC,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QAClE,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YAC1D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,gBAAgB,IAAI,QAAQ,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC;YAC1H,OAAO;QACR,CAAC;QACD,IAAI,EAAE,CAAC;IACR,CAAC,CAAc,CAAC;IAChB,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;IAC1B,OAAO,KAAK,CAAC;AACd,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,gBAAgB,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC/E,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;QACrD,OAAO;IACR,CAAC;IACD,IAAI,EAAE,CAAC;AACR,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type JWTPayload } from "jose";
|
|
2
|
+
import type { Role } from "./roles.ts";
|
|
3
|
+
export interface OidcOptions {
|
|
4
|
+
issuer: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
clientSecret: string;
|
|
7
|
+
redirectUri: string;
|
|
8
|
+
defaultRole: Role;
|
|
9
|
+
/** Empty = any domain the IdP vouches for. */
|
|
10
|
+
allowedDomains: string[];
|
|
11
|
+
/** Button label, e.g. "Okta". */
|
|
12
|
+
name?: string;
|
|
13
|
+
scopes?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface OidcClaims extends JWTPayload {
|
|
16
|
+
email?: string;
|
|
17
|
+
email_verified?: boolean;
|
|
18
|
+
name?: string;
|
|
19
|
+
nonce?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const randomToken: () => string;
|
|
22
|
+
export declare const pkceChallenge: (verifier: string) => string;
|
|
23
|
+
/** Authorization-code flow with PKCE; id_token verified against the IdP's JWKS. */
|
|
24
|
+
export declare function createOidcClient(opts: OidcOptions): {
|
|
25
|
+
authorizationUrl({ state, nonce, codeVerifier, redirectUri }: {
|
|
26
|
+
state: string;
|
|
27
|
+
nonce: string;
|
|
28
|
+
codeVerifier: string;
|
|
29
|
+
redirectUri: string;
|
|
30
|
+
}): Promise<string>;
|
|
31
|
+
exchange({ code, codeVerifier, nonce, redirectUri }: {
|
|
32
|
+
code: string;
|
|
33
|
+
codeVerifier: string;
|
|
34
|
+
nonce: string;
|
|
35
|
+
redirectUri: string;
|
|
36
|
+
}): Promise<OidcClaims & JWTPayload>;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=oidc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc.d.ts","sourceRoot":"","sources":["../../../../server/forge/auth/oidc.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AAEtE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,IAAI,CAAC;IAClB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AASD,MAAM,WAAW,UAAW,SAAQ,UAAU;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAGD,eAAO,MAAM,WAAW,cAAgC,CAAC;AACzD,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,WAA2D,CAAC;AAE1G,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW;kEAgBoB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;yDAgBpF;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE;EAuBtI"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
3
|
+
const b64url = (buf) => buf.toString("base64url");
|
|
4
|
+
export const randomToken = () => b64url(randomBytes(32));
|
|
5
|
+
export const pkceChallenge = (verifier) => b64url(createHash("sha256").update(verifier).digest());
|
|
6
|
+
/** Authorization-code flow with PKCE; id_token verified against the IdP's JWKS. */
|
|
7
|
+
export function createOidcClient(opts) {
|
|
8
|
+
let discovery = null;
|
|
9
|
+
let jwks = null;
|
|
10
|
+
const discover = () => {
|
|
11
|
+
discovery ??= fetch(`${opts.issuer.replace(/\/$/, "")}/.well-known/openid-configuration`).then(async (res) => {
|
|
12
|
+
if (!res.ok) {
|
|
13
|
+
discovery = null;
|
|
14
|
+
throw new Error(`OIDC discovery failed (${res.status}).`);
|
|
15
|
+
}
|
|
16
|
+
return (await res.json());
|
|
17
|
+
});
|
|
18
|
+
return discovery;
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
async authorizationUrl({ state, nonce, codeVerifier, redirectUri }) {
|
|
22
|
+
const d = await discover();
|
|
23
|
+
const url = new URL(d.authorization_endpoint);
|
|
24
|
+
url.search = new URLSearchParams({
|
|
25
|
+
response_type: "code",
|
|
26
|
+
client_id: opts.clientId,
|
|
27
|
+
redirect_uri: redirectUri,
|
|
28
|
+
scope: opts.scopes ?? "openid email profile",
|
|
29
|
+
state,
|
|
30
|
+
nonce,
|
|
31
|
+
code_challenge: pkceChallenge(codeVerifier),
|
|
32
|
+
code_challenge_method: "S256",
|
|
33
|
+
}).toString();
|
|
34
|
+
return url.toString();
|
|
35
|
+
},
|
|
36
|
+
async exchange({ code, codeVerifier, nonce, redirectUri }) {
|
|
37
|
+
const d = await discover();
|
|
38
|
+
const res = await fetch(d.token_endpoint, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" },
|
|
41
|
+
body: new URLSearchParams({
|
|
42
|
+
grant_type: "authorization_code",
|
|
43
|
+
code,
|
|
44
|
+
redirect_uri: redirectUri,
|
|
45
|
+
client_id: opts.clientId,
|
|
46
|
+
client_secret: opts.clientSecret,
|
|
47
|
+
code_verifier: codeVerifier,
|
|
48
|
+
}),
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok)
|
|
51
|
+
throw new Error(`OIDC token exchange failed (${res.status}).`);
|
|
52
|
+
const { id_token: idToken } = (await res.json());
|
|
53
|
+
if (!idToken)
|
|
54
|
+
throw new Error("OIDC provider returned no id_token.");
|
|
55
|
+
jwks ??= createRemoteJWKSet(new URL(d.jwks_uri));
|
|
56
|
+
const { payload } = await jwtVerify(idToken, jwks, { issuer: d.issuer, audience: opts.clientId });
|
|
57
|
+
if (payload.nonce !== nonce)
|
|
58
|
+
throw new Error("OIDC nonce mismatch.");
|
|
59
|
+
return payload;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=oidc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../../../server/forge/auth/oidc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const PASSWORD_MIN_LENGTH = 12;
|
|
2
|
+
/** `scrypt$<N>$<salt b64>$<hash b64>` */
|
|
3
|
+
export declare function hashPassword(password: string): Promise<string>;
|
|
4
|
+
export declare function verifyPassword(password: string, stored: string): Promise<boolean>;
|
|
5
|
+
/** Returns an error message, or null when the password is acceptable. */
|
|
6
|
+
export declare function validatePassword(password: string): string | null;
|
|
7
|
+
//# sourceMappingURL=passwords.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passwords.d.ts","sourceRoot":"","sources":["../../../../server/forge/auth/passwords.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAItC,yCAAyC;AACzC,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIpE;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMvF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKhE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { randomBytes, scrypt as scryptCb, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const scrypt = promisify(scryptCb);
|
|
4
|
+
export const PASSWORD_MIN_LENGTH = 12;
|
|
5
|
+
const PARAMS = { N: 2 ** 15, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
|
|
6
|
+
const KEY_LEN = 32;
|
|
7
|
+
/** `scrypt$<N>$<salt b64>$<hash b64>` */
|
|
8
|
+
export async function hashPassword(password) {
|
|
9
|
+
const salt = randomBytes(16);
|
|
10
|
+
const hash = await scrypt(password, salt, KEY_LEN, PARAMS);
|
|
11
|
+
return `scrypt$${PARAMS.N}$${salt.toString("base64")}$${hash.toString("base64")}`;
|
|
12
|
+
}
|
|
13
|
+
export async function verifyPassword(password, stored) {
|
|
14
|
+
const [scheme, n, saltB64, hashB64] = stored.split("$");
|
|
15
|
+
if (scheme !== "scrypt" || !n || !saltB64 || !hashB64)
|
|
16
|
+
return false;
|
|
17
|
+
const expected = Buffer.from(hashB64, "base64");
|
|
18
|
+
const actual = await scrypt(password, Buffer.from(saltB64, "base64"), expected.length, { ...PARAMS, N: Number(n) });
|
|
19
|
+
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
20
|
+
}
|
|
21
|
+
/** Returns an error message, or null when the password is acceptable. */
|
|
22
|
+
export function validatePassword(password) {
|
|
23
|
+
if (typeof password !== "string" || password.length < PASSWORD_MIN_LENGTH) {
|
|
24
|
+
return `Password must be at least ${PASSWORD_MIN_LENGTH} characters.`;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=passwords.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passwords.js","sourceRoot":"","sources":["../../../../server/forge/auth/passwords.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,IAAI,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAA6E,CAAC;AAE/G,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;AACpE,MAAM,OAAO,GAAG,EAAE,CAAC;AAEnB,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IAClD,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3D,OAAO,UAAU,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,MAAc;IACpE,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxD,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IACpE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpH,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAC/E,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAChD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC3E,OAAO,6BAA6B,mBAAmB,cAAc,CAAC;IACvE,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
|