hazo_auth 10.4.11 → 10.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/SETUP_CHECKLIST.md +2 -1
- package/cli-src/cli/generate.ts +4 -0
- package/cli-src/lib/validate_session_secret.server.ts +83 -0
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +4 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -0
- package/dist/components/user_picker_select.d.ts +58 -0
- package/dist/components/user_picker_select.d.ts.map +1 -0
- package/dist/components/user_picker_select.js +76 -0
- package/dist/lib/validate_session_secret.server.d.ts +20 -0
- package/dist/lib/validate_session_secret.server.d.ts.map +1 -0
- package/dist/lib/validate_session_secret.server.js +72 -0
- package/dist/server/routes/user_management_users.d.ts +1 -1
- package/dist/server-lib.d.ts +1 -0
- package/dist/server-lib.d.ts.map +1 -1
- package/dist/server-lib.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
A reusable authentication UI component package powered by Next.js, TailwindCSS, and shadcn. It integrates `hazo_config` for configuration management and `hazo_connect` for data access, enabling future components to stay aligned with platform conventions.
|
|
4
4
|
|
|
5
|
+
### What's New in v10.4.13
|
|
6
|
+
|
|
7
|
+
- **`validate_session_secret_config()`** exported from `hazo_auth/server-lib` — hard startup validator that throws `HazoConfigError` (code `HAZO_AUTH_CONFIG`) when `JWT_SECRET` is unset or shorter than 32 chars. Call it from a Next.js `instrumentation.ts` `register()` hook to fail boot loudly instead of hitting the silent split-brain a bad/missing secret otherwise causes (edge proxy bounces to `/login` while server-side auth falls back to unsigned cookies and still considers the user authenticated).
|
|
8
|
+
|
|
5
9
|
### What's New in v10.4.5
|
|
6
10
|
|
|
7
11
|
- **Pre-fill login credentials** — `LoginPage`, `LoginClientWrapper`, and `LoginLayout` now accept a `default_values` / `defaultValues` prop (`{ email?: string; password?: string }`) to pre-populate the login form. Useful for demo and test environments (gate with `process.env.DEMO_EMAIL` etc.).
|
package/SETUP_CHECKLIST.md
CHANGED
|
@@ -424,9 +424,10 @@ from_name = Your App Name
|
|
|
424
424
|
**Checklist:**
|
|
425
425
|
- [ ] `.env.local` file created
|
|
426
426
|
- [ ] `HAZO_AUTH_COOKIE_PREFIX` set (REQUIRED - must match config file)
|
|
427
|
-
- [ ] `JWT_SECRET` set (required for JWT session tokens)
|
|
427
|
+
- [ ] `JWT_SECRET` set (required for JWT session tokens, >= 32 chars)
|
|
428
428
|
- [ ] `ZEPTOMAIL_API_KEY` set (or email will not work)
|
|
429
429
|
- [ ] `from_email` configured in `hazo_notify_config.ini`
|
|
430
|
+
- [ ] Call `validate_session_secret_config()` (from `hazo_auth/server-lib`) in your `instrumentation.ts` `register()` hook — hard-fails boot with a descriptive banner if `JWT_SECRET` is missing or too short, instead of the silent split-brain (logged-in on `/login` but bounced on every protected route) that a bad secret otherwise causes.
|
|
430
431
|
|
|
431
432
|
---
|
|
432
433
|
|
package/cli-src/cli/generate.ts
CHANGED
|
@@ -64,6 +64,10 @@ const ROUTES: RouteDefinition[] = [
|
|
|
64
64
|
{ name: "nextauth_post", path: "api/auth/[...nextauth]", method: "POST", export_name: "nextauthPOST" },
|
|
65
65
|
{ name: "oauth_google_callback", path: "api/hazo_auth/oauth/google/callback", method: "GET", export_name: "oauthGoogleCallbackGET" },
|
|
66
66
|
{ name: "set_password", path: "api/hazo_auth/set_password", method: "POST", export_name: "setPasswordPOST" },
|
|
67
|
+
// Static assets (default auth-page images served from the package's dist/assets/images).
|
|
68
|
+
// Without this route the default image_src (/api/hazo_auth/assets/login_default.jpg) 404s
|
|
69
|
+
// and login/register/etc. render with an empty image panel.
|
|
70
|
+
{ name: "assets", path: "api/hazo_auth/assets/[name]", method: "GET", export_name: "assetGET" },
|
|
67
71
|
];
|
|
68
72
|
|
|
69
73
|
const PAGES: PageDefinition[] = [
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// file_description: startup validator for the JWT session signing secret — hard-fails boot with a descriptive banner if JWT_SECRET is missing or too short
|
|
2
|
+
// section: server-only-guard
|
|
3
|
+
import "server-only";
|
|
4
|
+
|
|
5
|
+
// section: imports
|
|
6
|
+
import { HazoConfigError } from "hazo_core";
|
|
7
|
+
|
|
8
|
+
// section: constants
|
|
9
|
+
/**
|
|
10
|
+
* Minimum acceptable length for JWT_SECRET. jose/HS256 accepts shorter keys,
|
|
11
|
+
* but anything under 32 chars is trivially brute-forceable for a session
|
|
12
|
+
* signing secret. Matches the threshold the validate CLI enforces.
|
|
13
|
+
*/
|
|
14
|
+
const MIN_JWT_SECRET_LEN = 32;
|
|
15
|
+
|
|
16
|
+
// section: validate
|
|
17
|
+
/**
|
|
18
|
+
* Validates the JWT session signing secret at startup.
|
|
19
|
+
*
|
|
20
|
+
* Throws HazoConfigError with a descriptive banner if JWT_SECRET is unset or
|
|
21
|
+
* shorter than 32 chars. Call this in your instrumentation `register()` hook to
|
|
22
|
+
* hard-fail boot instead of the silent failure mode it otherwise causes:
|
|
23
|
+
*
|
|
24
|
+
* - The edge proxy validator (`validate_session_cookie`) returns
|
|
25
|
+
* `{ valid: false }` when JWT_SECRET is missing → logged-in users get
|
|
26
|
+
* bounced to /login.
|
|
27
|
+
* - The server resolver (`hazo_get_auth`) falls back to unsigned simple
|
|
28
|
+
* cookies and considers the same user authenticated.
|
|
29
|
+
*
|
|
30
|
+
* The two disagree, so the app looks "logged in" on the login page yet bounces
|
|
31
|
+
* on every protected route. Failing at boot turns that silent split-brain into
|
|
32
|
+
* an obvious, actionable error.
|
|
33
|
+
*/
|
|
34
|
+
export function validate_session_secret_config(): void {
|
|
35
|
+
const jwt_secret = process.env.JWT_SECRET;
|
|
36
|
+
|
|
37
|
+
if (!jwt_secret || jwt_secret.trim().length === 0) {
|
|
38
|
+
console.error("=".repeat(60));
|
|
39
|
+
console.error("FATAL: JWT_SECRET is not set");
|
|
40
|
+
console.error("=".repeat(60));
|
|
41
|
+
console.error("hazo_auth signs session cookies with JWT_SECRET. Without it:");
|
|
42
|
+
console.error(" - the edge proxy rejects every session and bounces");
|
|
43
|
+
console.error(" logged-in users to /login;");
|
|
44
|
+
console.error(" - server-side auth silently falls back to unsigned");
|
|
45
|
+
console.error(" cookies, so the app looks logged in but every");
|
|
46
|
+
console.error(" protected route bounces.");
|
|
47
|
+
console.error("");
|
|
48
|
+
console.error("Fix — add to .env.local:");
|
|
49
|
+
console.error(" JWT_SECRET=<random string, at least 32 chars>");
|
|
50
|
+
console.error("Generate one:");
|
|
51
|
+
console.error(" node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
|
|
52
|
+
console.error("=".repeat(60));
|
|
53
|
+
|
|
54
|
+
throw new HazoConfigError({
|
|
55
|
+
code: "HAZO_AUTH_CONFIG",
|
|
56
|
+
pkg: "hazo_auth",
|
|
57
|
+
message:
|
|
58
|
+
"JWT_SECRET environment variable is required to sign session cookies. " +
|
|
59
|
+
"Without it the edge proxy bounces authenticated users to /login while " +
|
|
60
|
+
"server-side auth falls back to unsigned cookies. Set JWT_SECRET (>= 32 chars) in .env.local.",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (jwt_secret.trim().length < MIN_JWT_SECRET_LEN) {
|
|
65
|
+
console.error("=".repeat(60));
|
|
66
|
+
console.error("FATAL: JWT_SECRET is too short");
|
|
67
|
+
console.error("=".repeat(60));
|
|
68
|
+
console.error(
|
|
69
|
+
`JWT_SECRET is ${jwt_secret.trim().length} chars; at least ${MIN_JWT_SECRET_LEN} are required.`,
|
|
70
|
+
);
|
|
71
|
+
console.error("Generate a strong one:");
|
|
72
|
+
console.error(" node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
|
|
73
|
+
console.error("=".repeat(60));
|
|
74
|
+
|
|
75
|
+
throw new HazoConfigError({
|
|
76
|
+
code: "HAZO_AUTH_CONFIG",
|
|
77
|
+
pkg: "hazo_auth",
|
|
78
|
+
message:
|
|
79
|
+
`JWT_SECRET is too short (${jwt_secret.trim().length} chars). ` +
|
|
80
|
+
`Use at least ${MIN_JWT_SECRET_LEN} characters for the session signing secret.`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAqMF,wBAAgB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,IAAI,CA8DnE"}
|
package/dist/cli/generate.js
CHANGED
|
@@ -41,6 +41,10 @@ const ROUTES = [
|
|
|
41
41
|
{ name: "nextauth_post", path: "api/auth/[...nextauth]", method: "POST", export_name: "nextauthPOST" },
|
|
42
42
|
{ name: "oauth_google_callback", path: "api/hazo_auth/oauth/google/callback", method: "GET", export_name: "oauthGoogleCallbackGET" },
|
|
43
43
|
{ name: "set_password", path: "api/hazo_auth/set_password", method: "POST", export_name: "setPasswordPOST" },
|
|
44
|
+
// Static assets (default auth-page images served from the package's dist/assets/images).
|
|
45
|
+
// Without this route the default image_src (/api/hazo_auth/assets/login_default.jpg) 404s
|
|
46
|
+
// and login/register/etc. render with an empty image panel.
|
|
47
|
+
{ name: "assets", path: "api/hazo_auth/assets/[name]", method: "GET", export_name: "assetGET" },
|
|
44
48
|
];
|
|
45
49
|
const PAGES = [
|
|
46
50
|
{ name: "login", path: "hazo_auth/login", component_name: "LoginPage", import_path: "hazo_auth/pages/login" },
|
package/dist/client.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * from "./components/layouts/shared/utils/validation.js";
|
|
|
12
12
|
export { requestGoogleScopes } from "./lib/auth/request_google_scopes.js";
|
|
13
13
|
export { PermissionDeniedDialog } from "./components/permission_denied_dialog.client.js";
|
|
14
14
|
export type { PermissionDeniedDialogProps, PermissionDeniedDetails } from "./components/permission_denied_dialog.client";
|
|
15
|
+
export { UserPickerSelect, UserAvatar, userDisplayName } from "./components/user_picker_select.js";
|
|
16
|
+
export type { UserPickerSelectProps, UserAvatarProps, PickableUser, } from "./components/user_picker_select";
|
|
15
17
|
export { AuthIssueCard, registerAuthIssueCardRenderer } from './admin-issues/plugin.client.js';
|
|
16
18
|
export { PermissionDeniedProvider, showPermissionDeniedDialog, handlePermissionDenied, fetchWithPermissionCapture, } from './admin-issues/permission_denied_provider.client.js';
|
|
17
19
|
export type { PermissionDeniedProviderProps } from './admin-issues/permission_denied_provider.client';
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,cAAc,oBAAoB,CAAC;AAInC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAInI,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIpD,cAAc,uBAAuB,CAAC;AAItC,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,mDAAmD,CAAC;AACjH,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAC3G,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,qDAAqD,CAAC;AACnH,YAAY,EAAE,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qDAAqD,CAAC;AAGvI,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAIxG,cAAc,8CAA8C,CAAC;AAG7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAGvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AACtF,YAAY,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAYA,cAAc,oBAAoB,CAAC;AAInC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAInI,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIpD,cAAc,uBAAuB,CAAC;AAItC,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,mDAAmD,CAAC;AACjH,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAC3G,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,qDAAqD,CAAC;AACnH,YAAY,EAAE,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qDAAqD,CAAC;AAGvI,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAIxG,cAAc,8CAA8C,CAAC;AAG7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAGvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AACtF,YAAY,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAKzH,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAChG,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,YAAY,GACb,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,aAAa,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAG5F,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,kDAAkD,CAAC;AAC1D,YAAY,EAAE,6BAA6B,EAAE,MAAM,kDAAkD,CAAC"}
|
package/dist/client.js
CHANGED
|
@@ -33,6 +33,10 @@ export * from "./components/layouts/shared/utils/validation.js";
|
|
|
33
33
|
export { requestGoogleScopes } from "./lib/auth/request_google_scopes.js";
|
|
34
34
|
// section: permission_denied_dialog_exports
|
|
35
35
|
export { PermissionDeniedDialog } from "./components/permission_denied_dialog.client.js";
|
|
36
|
+
// section: user_picker_exports
|
|
37
|
+
// Reusable avatar + username dropdown for choosing a user (issue owner,
|
|
38
|
+
// assignee, mention, etc.). Data-agnostic — caller supplies the candidate list.
|
|
39
|
+
export { UserPickerSelect, UserAvatar, userDisplayName } from "./components/user_picker_select.js";
|
|
36
40
|
// admin-issues plugin (client card renderer)
|
|
37
41
|
export { AuthIssueCard, registerAuthIssueCardRenderer } from './admin-issues/plugin.client.js';
|
|
38
42
|
// permission-denied provider + fetch helper
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface PickableUser {
|
|
3
|
+
/** Stable id — the value the picker commits. */
|
|
4
|
+
user_id: string;
|
|
5
|
+
/** Display name; falls back to email, then the id. */
|
|
6
|
+
name?: string | null;
|
|
7
|
+
email?: string | null;
|
|
8
|
+
/** Profile picture URL; absent/failed → tinted initials. */
|
|
9
|
+
profile_picture_url?: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface UserPickerSelectProps {
|
|
12
|
+
/** Candidate users to choose from. */
|
|
13
|
+
users: PickableUser[];
|
|
14
|
+
/** Selected user_id; null (or "") means unassigned. */
|
|
15
|
+
value: string | null;
|
|
16
|
+
/** Fired with the new user_id, or null when the unassigned row is picked. */
|
|
17
|
+
onChange: (userId: string | null) => void;
|
|
18
|
+
/** Prepend an "Unassigned" row. Default true. */
|
|
19
|
+
includeUnassigned?: boolean;
|
|
20
|
+
/** Label for the unassigned row. Default "Unassigned". */
|
|
21
|
+
unassignedLabel?: string;
|
|
22
|
+
/** Placeholder shown when nothing is selected. Default "Select user". */
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
/** Control + avatar sizing. Default "default". */
|
|
26
|
+
size?: "sm" | "default";
|
|
27
|
+
/** Extra classes for the trigger. */
|
|
28
|
+
className?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Classes for the portalled dropdown panel. The list renders in a body-level
|
|
31
|
+
* portal, so it escapes any theme tokens scoped to an ancestor — pass classes
|
|
32
|
+
* here when the surrounding surface pins its own tokens.
|
|
33
|
+
*/
|
|
34
|
+
contentClassName?: string;
|
|
35
|
+
/** Inline styles for the portalled dropdown panel (e.g. pinned CSS token vars). */
|
|
36
|
+
contentStyle?: React.CSSProperties;
|
|
37
|
+
"aria-label"?: string;
|
|
38
|
+
}
|
|
39
|
+
/** name → email → id, so a row is never blank. */
|
|
40
|
+
export declare function userDisplayName(u: PickableUser): string;
|
|
41
|
+
export interface UserAvatarProps {
|
|
42
|
+
user?: PickableUser | null;
|
|
43
|
+
size?: "sm" | "default";
|
|
44
|
+
className?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Standalone avatar: the profile picture when present, else tinted initials,
|
|
48
|
+
* else a muted person glyph for the empty/unassigned slot. Exported so callers
|
|
49
|
+
* can reuse the exact same avatar outside the picker (cards, headers, etc.).
|
|
50
|
+
*/
|
|
51
|
+
export declare function UserAvatar({ user, size, className }: UserAvatarProps): React.JSX.Element;
|
|
52
|
+
/**
|
|
53
|
+
* A Select whose rows show a profile picture + username. The trigger mirrors the
|
|
54
|
+
* selected row (Radix `SelectValue` copies the item's children), so the chosen
|
|
55
|
+
* user's avatar + name show in the closed control automatically.
|
|
56
|
+
*/
|
|
57
|
+
export declare function UserPickerSelect({ users, value, onChange, includeUnassigned, unassignedLabel, placeholder, disabled, size, className, contentClassName, contentStyle, "aria-label": ariaLabel, }: UserPickerSelectProps): React.JSX.Element;
|
|
58
|
+
//# sourceMappingURL=user_picker_select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user_picker_select.d.ts","sourceRoot":"","sources":["../../src/components/user_picker_select.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,sCAAsC;IACtC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,uDAAuD;IACvD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1C,iDAAiD;IACjD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kDAAkD;IAClD,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACxB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,YAAY,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAOD,kDAAkD;AAClD,wBAAgB,eAAe,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAEvD;AA+BD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,IAAgB,EAAE,SAAS,EAAE,EAAE,eAAe,qBAsBhF;AAGD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,QAAQ,EACR,iBAAwB,EACxB,eAA8B,EAC9B,WAA2B,EAC3B,QAAQ,EACR,IAAgB,EAChB,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,YAAY,EAAE,SAAS,GACxB,EAAE,qBAAqB,qBAqCvB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// file_description: reusable user-picker dropdown — a Select whose rows render a
|
|
2
|
+
// profile picture (or tinted initials) beside the user's name. Client-safe;
|
|
3
|
+
// built on the shadcn Avatar + Select primitives. Data-agnostic: the caller
|
|
4
|
+
// supplies the candidate users and owns fetching them (e.g. via
|
|
5
|
+
// hazo_get_user_profiles server-side).
|
|
6
|
+
// section: client_directive
|
|
7
|
+
"use client";
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { UserRound } from "lucide-react";
|
|
10
|
+
import { Avatar, AvatarImage, AvatarFallback } from "./ui/avatar.js";
|
|
11
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "./ui/select.js";
|
|
12
|
+
import { cn } from "../lib/utils.js";
|
|
13
|
+
// Radix Select forbids an empty-string item value, so the unassigned row uses a
|
|
14
|
+
// sentinel that is mapped back to null across the component boundary.
|
|
15
|
+
const UNASSIGNED = "__hazo_unassigned__";
|
|
16
|
+
// section: helpers
|
|
17
|
+
/** name → email → id, so a row is never blank. */
|
|
18
|
+
export function userDisplayName(u) {
|
|
19
|
+
return (u.name && u.name.trim()) || (u.email && u.email.trim()) || u.user_id;
|
|
20
|
+
}
|
|
21
|
+
/** Up-to-two-letter initials from a label. */
|
|
22
|
+
function initialsOf(label) {
|
|
23
|
+
const s = label.trim();
|
|
24
|
+
if (!s)
|
|
25
|
+
return "?";
|
|
26
|
+
const parts = s.split(/[\s@._-]+/).filter(Boolean);
|
|
27
|
+
if (parts.length === 0)
|
|
28
|
+
return s.slice(0, 2).toUpperCase();
|
|
29
|
+
if (parts.length === 1)
|
|
30
|
+
return parts[0].slice(0, 2).toUpperCase();
|
|
31
|
+
return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
32
|
+
}
|
|
33
|
+
// Deterministic fallback tint from a key, so the same user keeps one colour.
|
|
34
|
+
const TINTS = [
|
|
35
|
+
"bg-indigo-500 text-white",
|
|
36
|
+
"bg-emerald-500 text-white",
|
|
37
|
+
"bg-violet-500 text-white",
|
|
38
|
+
"bg-amber-500 text-white",
|
|
39
|
+
"bg-rose-500 text-white",
|
|
40
|
+
"bg-cyan-500 text-white",
|
|
41
|
+
"bg-sky-500 text-white",
|
|
42
|
+
"bg-fuchsia-500 text-white",
|
|
43
|
+
];
|
|
44
|
+
function tintOf(key) {
|
|
45
|
+
let hash = 0;
|
|
46
|
+
for (let i = 0; i < key.length; i++)
|
|
47
|
+
hash = (hash * 31 + key.charCodeAt(i)) | 0;
|
|
48
|
+
return TINTS[Math.abs(hash) % TINTS.length];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Standalone avatar: the profile picture when present, else tinted initials,
|
|
52
|
+
* else a muted person glyph for the empty/unassigned slot. Exported so callers
|
|
53
|
+
* can reuse the exact same avatar outside the picker (cards, headers, etc.).
|
|
54
|
+
*/
|
|
55
|
+
export function UserAvatar({ user, size = "default", className }) {
|
|
56
|
+
const dim = size === "sm" ? "h-5 w-5" : "h-6 w-6";
|
|
57
|
+
if (!user) {
|
|
58
|
+
return (_jsx(Avatar, { className: cn(dim, "bg-muted text-muted-foreground", className), children: _jsx(AvatarFallback, { className: "bg-transparent", children: _jsx(UserRound, { className: size === "sm" ? "h-3 w-3" : "h-3.5 w-3.5" }) }) }));
|
|
59
|
+
}
|
|
60
|
+
const label = userDisplayName(user);
|
|
61
|
+
return (_jsxs(Avatar, { className: cn(dim, className), children: [user.profile_picture_url ? (_jsx(AvatarImage, { src: user.profile_picture_url, alt: "" })) : null, _jsx(AvatarFallback, { className: cn("text-[9px] font-semibold", tintOf(user.user_id)), children: initialsOf(label) })] }));
|
|
62
|
+
}
|
|
63
|
+
// section: user_picker_select
|
|
64
|
+
/**
|
|
65
|
+
* A Select whose rows show a profile picture + username. The trigger mirrors the
|
|
66
|
+
* selected row (Radix `SelectValue` copies the item's children), so the chosen
|
|
67
|
+
* user's avatar + name show in the closed control automatically.
|
|
68
|
+
*/
|
|
69
|
+
export function UserPickerSelect({ users, value, onChange, includeUnassigned = true, unassignedLabel = "Unassigned", placeholder = "Select user", disabled, size = "default", className, contentClassName, contentStyle, "aria-label": ariaLabel, }) {
|
|
70
|
+
const current = value ? value : includeUnassigned ? UNASSIGNED : "";
|
|
71
|
+
const handleChange = (next) => {
|
|
72
|
+
onChange(next === UNASSIGNED ? null : next);
|
|
73
|
+
};
|
|
74
|
+
const triggerSize = size === "sm" ? "h-7 gap-1.5 text-xs" : "h-9 gap-2 text-sm";
|
|
75
|
+
return (_jsxs(Select, { value: current || undefined, onValueChange: handleChange, disabled: disabled, children: [_jsx(SelectTrigger, { "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : "Select user", className: cn(triggerSize, "min-w-0 [&>span]:truncate", className), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsxs(SelectContent, { className: contentClassName, style: contentStyle, children: [includeUnassigned ? (_jsx(SelectItem, { value: UNASSIGNED, children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(UserAvatar, { user: null, size: size }), _jsx("span", { className: "truncate", children: unassignedLabel })] }) })) : null, users.map((u) => (_jsx(SelectItem, { value: u.user_id, children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(UserAvatar, { user: u, size: size }), _jsx("span", { className: "truncate", children: userDisplayName(u) })] }) }, u.user_id)))] })] }));
|
|
76
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
/**
|
|
3
|
+
* Validates the JWT session signing secret at startup.
|
|
4
|
+
*
|
|
5
|
+
* Throws HazoConfigError with a descriptive banner if JWT_SECRET is unset or
|
|
6
|
+
* shorter than 32 chars. Call this in your instrumentation `register()` hook to
|
|
7
|
+
* hard-fail boot instead of the silent failure mode it otherwise causes:
|
|
8
|
+
*
|
|
9
|
+
* - The edge proxy validator (`validate_session_cookie`) returns
|
|
10
|
+
* `{ valid: false }` when JWT_SECRET is missing → logged-in users get
|
|
11
|
+
* bounced to /login.
|
|
12
|
+
* - The server resolver (`hazo_get_auth`) falls back to unsigned simple
|
|
13
|
+
* cookies and considers the same user authenticated.
|
|
14
|
+
*
|
|
15
|
+
* The two disagree, so the app looks "logged in" on the login page yet bounces
|
|
16
|
+
* on every protected route. Failing at boot turns that silent split-brain into
|
|
17
|
+
* an obvious, actionable error.
|
|
18
|
+
*/
|
|
19
|
+
export declare function validate_session_secret_config(): void;
|
|
20
|
+
//# sourceMappingURL=validate_session_secret.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate_session_secret.server.d.ts","sourceRoot":"","sources":["../../src/lib/validate_session_secret.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAcrB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAiDrD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// file_description: startup validator for the JWT session signing secret — hard-fails boot with a descriptive banner if JWT_SECRET is missing or too short
|
|
2
|
+
// section: server-only-guard
|
|
3
|
+
import "server-only";
|
|
4
|
+
// section: imports
|
|
5
|
+
import { HazoConfigError } from "hazo_core";
|
|
6
|
+
// section: constants
|
|
7
|
+
/**
|
|
8
|
+
* Minimum acceptable length for JWT_SECRET. jose/HS256 accepts shorter keys,
|
|
9
|
+
* but anything under 32 chars is trivially brute-forceable for a session
|
|
10
|
+
* signing secret. Matches the threshold the validate CLI enforces.
|
|
11
|
+
*/
|
|
12
|
+
const MIN_JWT_SECRET_LEN = 32;
|
|
13
|
+
// section: validate
|
|
14
|
+
/**
|
|
15
|
+
* Validates the JWT session signing secret at startup.
|
|
16
|
+
*
|
|
17
|
+
* Throws HazoConfigError with a descriptive banner if JWT_SECRET is unset or
|
|
18
|
+
* shorter than 32 chars. Call this in your instrumentation `register()` hook to
|
|
19
|
+
* hard-fail boot instead of the silent failure mode it otherwise causes:
|
|
20
|
+
*
|
|
21
|
+
* - The edge proxy validator (`validate_session_cookie`) returns
|
|
22
|
+
* `{ valid: false }` when JWT_SECRET is missing → logged-in users get
|
|
23
|
+
* bounced to /login.
|
|
24
|
+
* - The server resolver (`hazo_get_auth`) falls back to unsigned simple
|
|
25
|
+
* cookies and considers the same user authenticated.
|
|
26
|
+
*
|
|
27
|
+
* The two disagree, so the app looks "logged in" on the login page yet bounces
|
|
28
|
+
* on every protected route. Failing at boot turns that silent split-brain into
|
|
29
|
+
* an obvious, actionable error.
|
|
30
|
+
*/
|
|
31
|
+
export function validate_session_secret_config() {
|
|
32
|
+
const jwt_secret = process.env.JWT_SECRET;
|
|
33
|
+
if (!jwt_secret || jwt_secret.trim().length === 0) {
|
|
34
|
+
console.error("=".repeat(60));
|
|
35
|
+
console.error("FATAL: JWT_SECRET is not set");
|
|
36
|
+
console.error("=".repeat(60));
|
|
37
|
+
console.error("hazo_auth signs session cookies with JWT_SECRET. Without it:");
|
|
38
|
+
console.error(" - the edge proxy rejects every session and bounces");
|
|
39
|
+
console.error(" logged-in users to /login;");
|
|
40
|
+
console.error(" - server-side auth silently falls back to unsigned");
|
|
41
|
+
console.error(" cookies, so the app looks logged in but every");
|
|
42
|
+
console.error(" protected route bounces.");
|
|
43
|
+
console.error("");
|
|
44
|
+
console.error("Fix — add to .env.local:");
|
|
45
|
+
console.error(" JWT_SECRET=<random string, at least 32 chars>");
|
|
46
|
+
console.error("Generate one:");
|
|
47
|
+
console.error(" node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
|
|
48
|
+
console.error("=".repeat(60));
|
|
49
|
+
throw new HazoConfigError({
|
|
50
|
+
code: "HAZO_AUTH_CONFIG",
|
|
51
|
+
pkg: "hazo_auth",
|
|
52
|
+
message: "JWT_SECRET environment variable is required to sign session cookies. " +
|
|
53
|
+
"Without it the edge proxy bounces authenticated users to /login while " +
|
|
54
|
+
"server-side auth falls back to unsigned cookies. Set JWT_SECRET (>= 32 chars) in .env.local.",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (jwt_secret.trim().length < MIN_JWT_SECRET_LEN) {
|
|
58
|
+
console.error("=".repeat(60));
|
|
59
|
+
console.error("FATAL: JWT_SECRET is too short");
|
|
60
|
+
console.error("=".repeat(60));
|
|
61
|
+
console.error(`JWT_SECRET is ${jwt_secret.trim().length} chars; at least ${MIN_JWT_SECRET_LEN} are required.`);
|
|
62
|
+
console.error("Generate a strong one:");
|
|
63
|
+
console.error(" node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
|
|
64
|
+
console.error("=".repeat(60));
|
|
65
|
+
throw new HazoConfigError({
|
|
66
|
+
code: "HAZO_AUTH_CONFIG",
|
|
67
|
+
pkg: "hazo_auth",
|
|
68
|
+
message: `JWT_SECRET is too short (${jwt_secret.trim().length} chars). ` +
|
|
69
|
+
`Use at least ${MIN_JWT_SECRET_LEN} characters for the session signing secret.`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -26,7 +26,7 @@ export declare function GET(request: NextRequest): Promise<NextResponse<{
|
|
|
26
26
|
profile_source: {} | null;
|
|
27
27
|
user_type: string | null;
|
|
28
28
|
app_user_data: Record<string, unknown> | null;
|
|
29
|
-
legal_acceptance_status: "current" | "
|
|
29
|
+
legal_acceptance_status: "current" | "outdated" | "none";
|
|
30
30
|
}[];
|
|
31
31
|
}>>;
|
|
32
32
|
/**
|
package/dist/server-lib.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export { get_branding_config, is_branding_enabled, is_allowed_logo_format, get_m
|
|
|
26
26
|
export type { FirmBrandingConfig } from "./lib/branding_config.server";
|
|
27
27
|
export { create_sqlite_hazo_connect } from "./lib/hazo_connect_setup.js";
|
|
28
28
|
export { validate_hazo_connect_config } from "./lib/validate_hazo_connect_config.server.js";
|
|
29
|
+
export { validate_session_secret_config } from "./lib/validate_session_secret.server.js";
|
|
29
30
|
export { get_hazo_connect_instance, set_hazo_connect_instance, reset_hazo_connect_instance, } from "./lib/hazo_connect_instance.server.js";
|
|
30
31
|
export { create_app_logger } from "./lib/app_logger.js";
|
|
31
32
|
export { sanitize_error_for_user } from "./lib/utils/error_sanitizer.js";
|
package/dist/server-lib.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-lib.d.ts","sourceRoot":"","sources":["../src/server-lib.ts"],"names":[],"mappings":"AAYA,OAAO,aAAa,CAAC;AAGrB,cAAc,kBAAkB,CAAC;AAGjC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAGrF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,gCAAgC,EAAE,MAAM,2CAA2C,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,cAAc,+BAA+B,CAAC;AAG9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AACvF,YAAY,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAGpF,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,YAAY,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAG/E,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAK7E,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,YAAY,EACV,oBAAoB,EACpB,cAAc,EACd,eAAe,GAChB,MAAM,8BAA8B,CAAC"}
|
|
1
|
+
{"version":3,"file":"server-lib.d.ts","sourceRoot":"","sources":["../src/server-lib.ts"],"names":[],"mappings":"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"}
|
package/dist/server-lib.js
CHANGED
|
@@ -40,6 +40,7 @@ export { get_branding_config, is_branding_enabled, is_allowed_logo_format, get_m
|
|
|
40
40
|
// section: hazo_connect_exports
|
|
41
41
|
export { create_sqlite_hazo_connect } from "./lib/hazo_connect_setup.js";
|
|
42
42
|
export { validate_hazo_connect_config } from "./lib/validate_hazo_connect_config.server.js";
|
|
43
|
+
export { validate_session_secret_config } from "./lib/validate_session_secret.server.js";
|
|
43
44
|
export { get_hazo_connect_instance, set_hazo_connect_instance, reset_hazo_connect_instance, } from "./lib/hazo_connect_instance.server.js";
|
|
44
45
|
// section: logger_exports
|
|
45
46
|
export { create_app_logger } from "./lib/app_logger.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_auth",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.5.0",
|
|
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",
|
|
@@ -408,7 +408,7 @@
|
|
|
408
408
|
"hazo_core": "^1.2.1",
|
|
409
409
|
"hazo_logs": "^2.1.1",
|
|
410
410
|
"hazo_notify": "^6.1.4",
|
|
411
|
-
"hazo_ui": "^4.
|
|
411
|
+
"hazo_ui": "^4.7.0",
|
|
412
412
|
"input-otp": "^1.4.0",
|
|
413
413
|
"jest": "^30.2.0",
|
|
414
414
|
"jest-environment-jsdom": "^30.0.0",
|