§8 review convergence (todo §8); re-ran the architecture + product reviewers to convergence — 5 rounds, until both returned zero new actionable findings. Fixed across rounds 1-4 (tests-first): bounded every outbound Ory fetch with a timeout (src/fetch-timeout.ts withTimeout + ORY_TIMEOUT_SEC default 5, incl. the http JWKS fetch) so a hung Ory can't park a request handler; anonymous on a permission-gated plugin route now 303→/login (was a dead-end 403; signed-in-without-role still 403); an already-signed-in user is sent home from /login + /registration; the onRequest hook short-circuit now sets the fresh CSRF cookie; admin-users malformed :id → 404 (was 500) via safeDecode; parseJwks validates key element shape (fails loud at load); removed the dead COOKIE_SECRET (loaded + enforced + documented but never read); documented HYDRA_ADMIN_URL; admin recovery shows the code + links to the public /recovery instead of the browser-unreachable admin-API link; reference-plugin breadcrumb-label + pagination/datetime README notes; corrected the contract doc to not over-promise a post-login "retry". Declined: unconditional base-ctx chrome (would build the menu per request, regressing the lazy hot path). Deferred → §9: return_to-preservation for deep-link login. Stability-reviewer on the cumulative diff: APPROVE, no Critical/High (addressed its Low nits). typecheck + 310 units + the full scripts/ci.sh gate (visual 9 · auth 1 · oauth 2 · full 6) green.
This commit is contained in:
+10
-2
@@ -10,7 +10,6 @@
|
||||
|
||||
export interface Config {
|
||||
cacheTemplates: boolean;
|
||||
cookieSecret: string;
|
||||
csrfSecret: string;
|
||||
hydraAdminUrl: string;
|
||||
jwksUrl: string;
|
||||
@@ -21,6 +20,7 @@ export interface Config {
|
||||
ketoWriteUrl: string;
|
||||
kratosAdminUrl: string;
|
||||
kratosPublicUrl: string;
|
||||
oryTimeoutSec: number; // per-call timeout for outbound Kratos/Keto/Hydra fetches (bounds a hung Ory)
|
||||
port: number;
|
||||
secureCookies: boolean;
|
||||
}
|
||||
@@ -82,11 +82,18 @@ function readNonNegInt(env: Env, key: string, devDefault: number): number {
|
||||
return n;
|
||||
}
|
||||
|
||||
function readPosInt(env: Env, key: string, devDefault: number): number {
|
||||
const raw = env[key];
|
||||
if (raw === undefined) return devDefault;
|
||||
const n = Number(raw);
|
||||
if (!Number.isInteger(n) || n < 1) throw new Error(`config: ${key} must be a positive integer, got "${raw}"`);
|
||||
return n;
|
||||
}
|
||||
|
||||
export function loadConfig(env: Env = process.env): Config {
|
||||
const requireSecure = readBool(env, "REQUIRE_SECURE_SECRETS", false);
|
||||
return {
|
||||
cacheTemplates: readBool(env, "CACHE_TEMPLATES", false),
|
||||
cookieSecret: readSecret(env, "COOKIE_SECRET", "dev-insecure-cookie-secret", requireSecure),
|
||||
csrfSecret: readSecret(env, "CSRF_SECRET", "dev-insecure-csrf-secret", requireSecure),
|
||||
// Hydra admin API — the OAuth2 login/consent challenge handshake (§6); not on the first-party path.
|
||||
hydraAdminUrl: readUrl(env, "HYDRA_ADMIN_URL", "http://hydra:4445"),
|
||||
@@ -103,6 +110,7 @@ export function loadConfig(env: Env = process.env): Config {
|
||||
ketoWriteUrl: readUrl(env, "KETO_WRITE_URL", "http://keto:4467"),
|
||||
kratosAdminUrl: readUrl(env, "KRATOS_ADMIN_URL", "http://kratos:4434"),
|
||||
kratosPublicUrl: readUrl(env, "KRATOS_PUBLIC_URL", "http://kratos:4433"),
|
||||
oryTimeoutSec: readPosInt(env, "ORY_TIMEOUT_SEC", 5),
|
||||
port: readPort(env),
|
||||
// Set Secure on our session/CSRF cookies. Off by default (dev runs http); prod (https) sets it.
|
||||
secureCookies: readBool(env, "SECURE_COOKIES", false),
|
||||
|
||||
Reference in New Issue
Block a user