hazo_auth 10.4.10 → 10.4.11

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.
@@ -310,6 +310,43 @@ function check_env_vars(): CheckResult[] {
310
310
  const GOOGLE_NEXTAUTH_CALLBACK_PATH = "/api/auth/callback/google";
311
311
  const FACEBOOK_NEXTAUTH_CALLBACK_PATH = "/api/auth/callback/facebook";
312
312
 
313
+ /**
314
+ * The edge proxy (Edge Runtime, no filesystem) reads the cookie prefix from the
315
+ * HAZO_AUTH_COOKIE_PREFIX env var, while the server reads cookie_prefix from the
316
+ * ini. If they differ — or the env var is unset — the edge gate can't find the
317
+ * session cookie and silently bounces authenticated users back to /login. This
318
+ * check catches that split before it wastes an afternoon.
319
+ */
320
+ function check_cookie_prefix(project_root: string): CheckResult[] {
321
+ const hazo_config = read_ini_file(path.join(project_root, "config", "hazo_auth_config.ini"));
322
+ const ini_prefix = (hazo_config?.["hazo_auth__cookies"]?.["cookie_prefix"] ?? "").trim();
323
+ const env_prefix = (process.env.HAZO_AUTH_COOKIE_PREFIX ?? "").trim();
324
+
325
+ if (!ini_prefix) {
326
+ // Already reported as a fail by check_config_values; nothing to compare.
327
+ return [];
328
+ }
329
+ if (!env_prefix) {
330
+ return [
331
+ {
332
+ name: "Cookie prefix (env ↔ ini)",
333
+ status: "fail",
334
+ message: `HAZO_AUTH_COOKIE_PREFIX env var not set. The edge proxy throws without it and bounces authenticated users to /login. Set HAZO_AUTH_COOKIE_PREFIX=${ini_prefix} to match cookie_prefix in hazo_auth_config.ini.`,
335
+ },
336
+ ];
337
+ }
338
+ if (env_prefix !== ini_prefix) {
339
+ return [
340
+ {
341
+ name: "Cookie prefix (env ↔ ini)",
342
+ status: "fail",
343
+ message: `Mismatch: HAZO_AUTH_COOKIE_PREFIX="${env_prefix}" but ini cookie_prefix="${ini_prefix}". They must be identical or the edge gate can't find the session cookie. Make them equal.`,
344
+ },
345
+ ];
346
+ }
347
+ return [{ name: "Cookie prefix (env ↔ ini)", status: "pass", message: `"${env_prefix}" (matches)` }];
348
+ }
349
+
313
350
  function read_oauth_origin(): { origin: string; resolved: boolean } {
314
351
  const nextauth_url = process.env.NEXTAUTH_URL;
315
352
  if (nextauth_url && nextauth_url.trim().length > 0) {
@@ -850,6 +887,14 @@ export function run_validation(): ValidationSummary {
850
887
  all_results.push(...env_results);
851
888
  console.log();
852
889
 
890
+ const cookie_prefix_results = check_cookie_prefix(project_root);
891
+ if (cookie_prefix_results.length > 0) {
892
+ console.log("\x1b[1m🍪 Cookie Prefix\x1b[0m");
893
+ cookie_prefix_results.forEach(print_result);
894
+ all_results.push(...cookie_prefix_results);
895
+ console.log();
896
+ }
897
+
853
898
  console.log("\x1b[1m🔑 OAuth Providers\x1b[0m");
854
899
  const oauth_results = check_oauth(project_root);
855
900
  oauth_results.forEach(print_result);
@@ -3,6 +3,7 @@
3
3
  import "server-only";
4
4
 
5
5
  import { get_oauth_config } from "./oauth_config.server.js";
6
+ import { get_cookies_config } from "./cookies_config.server.js";
6
7
  import { googleRedirectUri, facebookRedirectUri } from "./oauth_routes.js";
7
8
  import { create_app_logger } from "./app_logger.js";
8
9
 
@@ -49,6 +50,30 @@ export function getOAuthPreflightReport(): OAuthPreflightReport {
49
50
  const oauth = get_oauth_config();
50
51
  const redirectUris: OAuthPreflightReport["redirectUris"] = [];
51
52
 
53
+ // Cookie prefix: env (read by the edge proxy) must match the ini (read by the
54
+ // server). A mismatch — or an unset env var — makes the edge gate throw and
55
+ // silently bounce authenticated users to /login.
56
+ try {
57
+ const ini_prefix = (get_cookies_config().cookie_prefix ?? "").trim();
58
+ const env_prefix = (process.env.HAZO_AUTH_COOKIE_PREFIX ?? "").trim();
59
+ if (ini_prefix && !env_prefix) {
60
+ checks.push({
61
+ name: "Cookie prefix",
62
+ status: "fail",
63
+ message: `HAZO_AUTH_COOKIE_PREFIX env var not set. The edge proxy throws without it and bounces authenticated users to /login. Set HAZO_AUTH_COOKIE_PREFIX=${ini_prefix} (matching cookie_prefix in hazo_auth_config.ini).`,
64
+ });
65
+ } else if (ini_prefix && env_prefix && env_prefix !== ini_prefix) {
66
+ checks.push({
67
+ name: "Cookie prefix",
68
+ status: "fail",
69
+ message: `Mismatch: HAZO_AUTH_COOKIE_PREFIX="${env_prefix}" vs ini cookie_prefix="${ini_prefix}". They must be identical or the edge gate can't find the session cookie.`,
70
+ });
71
+ }
72
+ } catch {
73
+ // get_cookies_config throws when cookie_prefix is absent from the ini; that
74
+ // missing-config case is reported elsewhere (validate CLI / server boot).
75
+ }
76
+
52
77
  // NEXTAUTH_URL — the source of the redirect URI.
53
78
  if (resolved) {
54
79
  checks.push({ name: "NEXTAUTH_URL", status: "pass", message: origin });
@@ -152,10 +177,10 @@ export function logOAuthPreflight(): OAuthPreflightReport | null {
152
177
  _logged_once = true;
153
178
 
154
179
  const report = getOAuthPreflightReport();
155
- if (report.redirectUris.length === 0) return report; // No OAuth providers enabled.
156
-
157
180
  const logger = create_app_logger();
158
181
 
182
+ // Always surface failing/warning checks (e.g. cookie-prefix mismatch), even
183
+ // when no OAuth provider is enabled and there are no redirect URIs to print.
159
184
  for (const check of report.checks) {
160
185
  if (check.status === "fail") {
161
186
  logger.error("oauth_preflight_check_failed", { check: check.name, detail: check.message });
@@ -164,6 +189,8 @@ export function logOAuthPreflight(): OAuthPreflightReport | null {
164
189
  }
165
190
  }
166
191
 
192
+ if (report.redirectUris.length === 0) return report; // No OAuth providers — nothing to register.
193
+
167
194
  // Highlighted, copy-pasteable block — the single thing that prevents
168
195
  // redirect_uri_mismatch. console (not logger) so it renders verbatim.
169
196
  const lines: string[] = [
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAOA,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AAsyBF,wBAAgB,cAAc,IAAI,iBAAiB,CAsFlD"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/cli/validate.ts"],"names":[],"mappings":"AAOA,KAAK,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AA20BF,wBAAgB,cAAc,IAAI,iBAAiB,CA8FlD"}
@@ -272,6 +272,42 @@ function check_env_vars() {
272
272
  // compiles as an isolated tree and must not import the server-only lib.
273
273
  const GOOGLE_NEXTAUTH_CALLBACK_PATH = "/api/auth/callback/google";
274
274
  const FACEBOOK_NEXTAUTH_CALLBACK_PATH = "/api/auth/callback/facebook";
275
+ /**
276
+ * The edge proxy (Edge Runtime, no filesystem) reads the cookie prefix from the
277
+ * HAZO_AUTH_COOKIE_PREFIX env var, while the server reads cookie_prefix from the
278
+ * ini. If they differ — or the env var is unset — the edge gate can't find the
279
+ * session cookie and silently bounces authenticated users back to /login. This
280
+ * check catches that split before it wastes an afternoon.
281
+ */
282
+ function check_cookie_prefix(project_root) {
283
+ var _a, _b, _c;
284
+ const hazo_config = read_ini_file(path.join(project_root, "config", "hazo_auth_config.ini"));
285
+ const ini_prefix = ((_b = (_a = hazo_config === null || hazo_config === void 0 ? void 0 : hazo_config["hazo_auth__cookies"]) === null || _a === void 0 ? void 0 : _a["cookie_prefix"]) !== null && _b !== void 0 ? _b : "").trim();
286
+ const env_prefix = ((_c = process.env.HAZO_AUTH_COOKIE_PREFIX) !== null && _c !== void 0 ? _c : "").trim();
287
+ if (!ini_prefix) {
288
+ // Already reported as a fail by check_config_values; nothing to compare.
289
+ return [];
290
+ }
291
+ if (!env_prefix) {
292
+ return [
293
+ {
294
+ name: "Cookie prefix (env ↔ ini)",
295
+ status: "fail",
296
+ message: `HAZO_AUTH_COOKIE_PREFIX env var not set. The edge proxy throws without it and bounces authenticated users to /login. Set HAZO_AUTH_COOKIE_PREFIX=${ini_prefix} to match cookie_prefix in hazo_auth_config.ini.`,
297
+ },
298
+ ];
299
+ }
300
+ if (env_prefix !== ini_prefix) {
301
+ return [
302
+ {
303
+ name: "Cookie prefix (env ↔ ini)",
304
+ status: "fail",
305
+ message: `Mismatch: HAZO_AUTH_COOKIE_PREFIX="${env_prefix}" but ini cookie_prefix="${ini_prefix}". They must be identical or the edge gate can't find the session cookie. Make them equal.`,
306
+ },
307
+ ];
308
+ }
309
+ return [{ name: "Cookie prefix (env ↔ ini)", status: "pass", message: `"${env_prefix}" (matches)` }];
310
+ }
275
311
  function read_oauth_origin() {
276
312
  var _a;
277
313
  const nextauth_url = process.env.NEXTAUTH_URL;
@@ -781,6 +817,13 @@ export function run_validation() {
781
817
  env_results.forEach(print_result);
782
818
  all_results.push(...env_results);
783
819
  console.log();
820
+ const cookie_prefix_results = check_cookie_prefix(project_root);
821
+ if (cookie_prefix_results.length > 0) {
822
+ console.log("\x1b[1m🍪 Cookie Prefix\x1b[0m");
823
+ cookie_prefix_results.forEach(print_result);
824
+ all_results.push(...cookie_prefix_results);
825
+ console.log();
826
+ }
784
827
  console.log("\x1b[1m🔑 OAuth Providers\x1b[0m");
785
828
  const oauth_results = check_oauth(project_root);
786
829
  oauth_results.forEach(print_result);
@@ -1 +1 @@
1
- {"version":3,"file":"oauth_preflight.server.d.ts","sourceRoot":"","sources":["../../src/lib/oauth_preflight.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAOrB,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,cAAc,EAAE,OAAO,CAAC;IACxB,uFAAuF;IACvF,YAAY,EAAE;QAAE,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjE,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B,CAAC;AAeF;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,CA2F9D;AAKD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,oBAAoB,GAAG,IAAI,CAsC/D"}
1
+ {"version":3,"file":"oauth_preflight.server.d.ts","sourceRoot":"","sources":["../../src/lib/oauth_preflight.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAQrB,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,cAAc,EAAE,OAAO,CAAC;IACxB,uFAAuF;IACvF,YAAY,EAAE;QAAE,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjE,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B,CAAC;AAeF;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,CAmH9D;AAKD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,oBAAoB,GAAG,IAAI,CAwC/D"}
@@ -2,6 +2,7 @@
2
2
  // redirect URIs to register, killing the recurring `redirect_uri_mismatch` for new apps.
3
3
  import "server-only";
4
4
  import { get_oauth_config } from "./oauth_config.server.js";
5
+ import { get_cookies_config } from "./cookies_config.server.js";
5
6
  import { googleRedirectUri, facebookRedirectUri } from "./oauth_routes.js";
6
7
  import { create_app_logger } from "./app_logger.js";
7
8
  // section: helpers
@@ -22,10 +23,36 @@ function resolve_origin() {
22
23
  * no DB or network access.
23
24
  */
24
25
  export function getOAuthPreflightReport() {
26
+ var _a, _b;
25
27
  const checks = [];
26
28
  const { origin, resolved } = resolve_origin();
27
29
  const oauth = get_oauth_config();
28
30
  const redirectUris = [];
31
+ // Cookie prefix: env (read by the edge proxy) must match the ini (read by the
32
+ // server). A mismatch — or an unset env var — makes the edge gate throw and
33
+ // silently bounce authenticated users to /login.
34
+ try {
35
+ const ini_prefix = ((_a = get_cookies_config().cookie_prefix) !== null && _a !== void 0 ? _a : "").trim();
36
+ const env_prefix = ((_b = process.env.HAZO_AUTH_COOKIE_PREFIX) !== null && _b !== void 0 ? _b : "").trim();
37
+ if (ini_prefix && !env_prefix) {
38
+ checks.push({
39
+ name: "Cookie prefix",
40
+ status: "fail",
41
+ message: `HAZO_AUTH_COOKIE_PREFIX env var not set. The edge proxy throws without it and bounces authenticated users to /login. Set HAZO_AUTH_COOKIE_PREFIX=${ini_prefix} (matching cookie_prefix in hazo_auth_config.ini).`,
42
+ });
43
+ }
44
+ else if (ini_prefix && env_prefix && env_prefix !== ini_prefix) {
45
+ checks.push({
46
+ name: "Cookie prefix",
47
+ status: "fail",
48
+ message: `Mismatch: HAZO_AUTH_COOKIE_PREFIX="${env_prefix}" vs ini cookie_prefix="${ini_prefix}". They must be identical or the edge gate can't find the session cookie.`,
49
+ });
50
+ }
51
+ }
52
+ catch (_c) {
53
+ // get_cookies_config throws when cookie_prefix is absent from the ini; that
54
+ // missing-config case is reported elsewhere (validate CLI / server boot).
55
+ }
29
56
  // NEXTAUTH_URL — the source of the redirect URI.
30
57
  if (resolved) {
31
58
  checks.push({ name: "NEXTAUTH_URL", status: "pass", message: origin });
@@ -45,7 +72,7 @@ export function getOAuthPreflightReport() {
45
72
  const u = new URL(origin);
46
73
  url_port = u.port || (u.protocol === "https:" ? "443" : "80");
47
74
  }
48
- catch (_a) {
75
+ catch (_d) {
49
76
  url_port = null;
50
77
  }
51
78
  if (url_port && url_port !== declared_port) {
@@ -128,9 +155,9 @@ export function logOAuthPreflight() {
128
155
  return null;
129
156
  _logged_once = true;
130
157
  const report = getOAuthPreflightReport();
131
- if (report.redirectUris.length === 0)
132
- return report; // No OAuth providers enabled.
133
158
  const logger = create_app_logger();
159
+ // Always surface failing/warning checks (e.g. cookie-prefix mismatch), even
160
+ // when no OAuth provider is enabled and there are no redirect URIs to print.
134
161
  for (const check of report.checks) {
135
162
  if (check.status === "fail") {
136
163
  logger.error("oauth_preflight_check_failed", { check: check.name, detail: check.message });
@@ -139,6 +166,8 @@ export function logOAuthPreflight() {
139
166
  logger.warn("oauth_preflight_check_warning", { check: check.name, detail: check.message });
140
167
  }
141
168
  }
169
+ if (report.redirectUris.length === 0)
170
+ return report; // No OAuth providers — nothing to register.
142
171
  // Highlighted, copy-pasteable block — the single thing that prevents
143
172
  // redirect_uri_mismatch. console (not logger) so it renders verbatim.
144
173
  const lines = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_auth",
3
- "version": "10.4.10",
3
+ "version": "10.4.11",
4
4
  "description": "Zero-config authentication UI components for Next.js with RBAC, OAuth, scope-based multi-tenancy, and invitations",
5
5
  "keywords": [
6
6
  "authentication",