hazo_auth 5.1.38 → 5.2.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 +10 -0
- package/cli-src/lib/auth/ensure_anon_id.server.ts +88 -0
- package/cli-src/lib/auth/index.ts +3 -0
- package/cli-src/lib/cookies_config.edge.ts +1 -0
- package/cli-src/lib/cookies_config.server.ts +1 -0
- package/cli-src/lib/hazo_connect_setup.server.ts +0 -8
- package/cli-src/lib/schema/sqlite_schema.ts +12 -6
- package/cli-src/lib/services/session_token_service.ts +2 -2
- package/cli-src/lib/ui_shell_config.server.ts +6 -2
- package/dist/components/layouts/login/index.d.ts +16 -3
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +49 -5
- package/dist/components/layouts/register/index.d.ts +7 -3
- package/dist/components/layouts/register/index.d.ts.map +1 -1
- package/dist/components/layouts/register/index.js +36 -3
- package/dist/components/layouts/shared/components/floating_home_link.d.ts +20 -0
- package/dist/components/layouts/shared/components/floating_home_link.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/floating_home_link.js +29 -0
- package/dist/components/layouts/shared/components/profile_pic_menu.d.ts +8 -0
- package/dist/components/layouts/shared/components/profile_pic_menu.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/profile_pic_menu.js +18 -8
- package/dist/components/layouts/shared/index.d.ts +2 -0
- package/dist/components/layouts/shared/index.d.ts.map +1 -1
- package/dist/components/layouts/shared/index.js +1 -0
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/lib/auth/ensure_anon_id.server.d.ts +21 -0
- package/dist/lib/auth/ensure_anon_id.server.d.ts.map +1 -0
- package/dist/lib/auth/ensure_anon_id.server.js +73 -0
- package/dist/lib/auth/index.d.ts +1 -0
- package/dist/lib/auth/index.d.ts.map +1 -1
- package/dist/lib/auth/index.js +2 -0
- package/dist/lib/cookies_config.edge.d.ts +1 -0
- package/dist/lib/cookies_config.edge.d.ts.map +1 -1
- package/dist/lib/cookies_config.edge.js +1 -0
- package/dist/lib/cookies_config.server.d.ts +1 -0
- package/dist/lib/cookies_config.server.d.ts.map +1 -1
- package/dist/lib/cookies_config.server.js +1 -0
- package/dist/lib/hazo_connect_setup.server.d.ts.map +1 -1
- package/dist/lib/hazo_connect_setup.server.js +0 -8
- package/dist/lib/schema/sqlite_schema.d.ts +1 -1
- package/dist/lib/schema/sqlite_schema.d.ts.map +1 -1
- package/dist/lib/schema/sqlite_schema.js +12 -6
- package/dist/lib/services/session_token_service.js +2 -2
- package/dist/lib/ui_shell_config.server.d.ts.map +1 -1
- package/dist/lib/ui_shell_config.server.js +6 -2
- package/dist/server/routes/oauth_google_callback.d.ts.map +1 -1
- package/dist/server/routes/oauth_google_callback.js +33 -6
- package/dist/server_pages/login.d.ts +13 -1
- package/dist/server_pages/login.d.ts.map +1 -1
- package/dist/server_pages/login.js +25 -9
- package/dist/server_pages/login_client_wrapper.d.ts +6 -4
- package/dist/server_pages/login_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/login_client_wrapper.js +2 -2
- package/dist/server_pages/register.d.ts +7 -1
- package/dist/server_pages/register.d.ts.map +1 -1
- package/dist/server_pages/register.js +18 -9
- package/dist/server_pages/register_client_wrapper.d.ts +6 -4
- package/dist/server_pages/register_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/register_client_wrapper.js +2 -2
- package/package.json +40 -34
|
@@ -34,8 +34,11 @@ export function ProfilePicMenu({ show_single_button = false, sign_up_label = "Si
|
|
|
34
34
|
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
|
35
35
|
const [shiftKeyHeld, setShiftKeyHeld] = useState(false);
|
|
36
36
|
const [showPermissionsDialog, setShowPermissionsDialog] = useState(false);
|
|
37
|
-
//
|
|
38
|
-
|
|
37
|
+
// The logout API endpoint is always derived from apiBasePath. `logout_path`
|
|
38
|
+
// is a navigation destination for AFTER the logout completes (not the API
|
|
39
|
+
// URL — earlier versions conflated these and broke when consumers passed
|
|
40
|
+
// a non-API path like "/").
|
|
41
|
+
const logoutApiPath = `${apiBasePath}/logout`;
|
|
39
42
|
// Get initials from name or email
|
|
40
43
|
const getInitials = () => {
|
|
41
44
|
var _a, _b;
|
|
@@ -55,7 +58,7 @@ export function ProfilePicMenu({ show_single_button = false, sign_up_label = "Si
|
|
|
55
58
|
const handleLogout = async () => {
|
|
56
59
|
setIsLoggingOut(true);
|
|
57
60
|
try {
|
|
58
|
-
const response = await fetch(
|
|
61
|
+
const response = await fetch(logoutApiPath, {
|
|
59
62
|
method: "POST",
|
|
60
63
|
headers: {
|
|
61
64
|
"Content-Type": "application/json",
|
|
@@ -68,8 +71,13 @@ export function ProfilePicMenu({ show_single_button = false, sign_up_label = "Si
|
|
|
68
71
|
toast.success("Logged out successfully");
|
|
69
72
|
// Trigger auth status refresh in all components
|
|
70
73
|
trigger_auth_status_refresh();
|
|
71
|
-
//
|
|
72
|
-
|
|
74
|
+
// Navigate to the configured post-logout destination, or refresh in place.
|
|
75
|
+
if (logout_path) {
|
|
76
|
+
router.push(logout_path);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
router.refresh();
|
|
80
|
+
}
|
|
73
81
|
}
|
|
74
82
|
catch (error) {
|
|
75
83
|
const errorMessage = error instanceof Error ? error.message : "Logout failed. Please try again.";
|
|
@@ -116,11 +124,13 @@ export function ProfilePicMenu({ show_single_button = false, sign_up_label = "Si
|
|
|
116
124
|
order: 1,
|
|
117
125
|
id: "default_settings",
|
|
118
126
|
});
|
|
119
|
-
// Logout (link, order: 2)
|
|
127
|
+
// Logout (link, order: 2). The href is a fallback for accessibility —
|
|
128
|
+
// `default_logout` items are click-intercepted by `handleLogout` which
|
|
129
|
+
// POSTs to the API path and then navigates per `logout_path`.
|
|
120
130
|
items.push({
|
|
121
131
|
type: "link",
|
|
122
132
|
label: "Logout",
|
|
123
|
-
href:
|
|
133
|
+
href: logoutApiPath,
|
|
124
134
|
order: 2,
|
|
125
135
|
id: "default_logout",
|
|
126
136
|
});
|
|
@@ -141,7 +151,7 @@ export function ProfilePicMenu({ show_single_button = false, sign_up_label = "Si
|
|
|
141
151
|
return a.order - b.order;
|
|
142
152
|
});
|
|
143
153
|
return items;
|
|
144
|
-
}, [authStatus.authenticated, authStatus.name, authStatus.email, settings_path,
|
|
154
|
+
}, [authStatus.authenticated, authStatus.name, authStatus.email, settings_path, logoutApiPath, custom_menu_items]);
|
|
145
155
|
// Avatar size classes
|
|
146
156
|
const avatarSizeClasses = {
|
|
147
157
|
sm: "h-8 w-8",
|
|
@@ -2,6 +2,8 @@ export { AlreadyLoggedInGuard } from "./components/already_logged_in_guard.js";
|
|
|
2
2
|
export { AuthNavbar } from "./components/auth_navbar.js";
|
|
3
3
|
export type { AuthNavbarProps } from "./components/auth_navbar";
|
|
4
4
|
export { FieldErrorMessage } from "./components/field_error_message.js";
|
|
5
|
+
export { FloatingHomeLink } from "./components/floating_home_link.js";
|
|
6
|
+
export type { FloatingHomeLinkProps } from "./components/floating_home_link";
|
|
5
7
|
export { FormActionButtons } from "./components/form_action_buttons.js";
|
|
6
8
|
export { FormFieldWrapper } from "./components/form_field_wrapper.js";
|
|
7
9
|
export { FormHeader } from "./components/form_header.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/shared/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAE7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACjF,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,cAAc,+BAA+B,CAAC;AAG9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/shared/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,YAAY,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAE7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACjF,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,cAAc,+BAA+B,CAAC;AAG9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,cAAc,oBAAoB,CAAC"}
|
|
@@ -4,6 +4,7 @@ export { AlreadyLoggedInGuard } from "./components/already_logged_in_guard.js";
|
|
|
4
4
|
export { AuthNavbar } from "./components/auth_navbar.js";
|
|
5
5
|
// AuthPageShell - NOT exported (test workspace component only)
|
|
6
6
|
export { FieldErrorMessage } from "./components/field_error_message.js";
|
|
7
|
+
export { FloatingHomeLink } from "./components/floating_home_link.js";
|
|
7
8
|
export { FormActionButtons } from "./components/form_action_buttons.js";
|
|
8
9
|
export { FormFieldWrapper } from "./components/form_field_wrapper.js";
|
|
9
10
|
export { FormHeader } from "./components/form_header.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" |
|
|
5
|
-
size?: "default" | "
|
|
4
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
5
|
+
size?: "default" | "icon" | "sm" | "lg" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
8
8
|
asChild?: boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { NextRequest } from "next/server";
|
|
3
|
+
/**
|
|
4
|
+
* Returns a stable opaque anonymous visitor ID for the caller, reading an
|
|
5
|
+
* existing httpOnly cookie or issuing a fresh UUID v4 if absent.
|
|
6
|
+
*
|
|
7
|
+
* - Cookie name: `hazo_auth_anon_id` (prefixed via `[hazo_auth__cookies] cookie_prefix`)
|
|
8
|
+
* - Cookie attributes: httpOnly, sameSite=lax, path=/, secure in production,
|
|
9
|
+
* maxAge = 2 years.
|
|
10
|
+
* - Idempotent per-request: calling twice with the same NextRequest returns
|
|
11
|
+
* the same id and only queues one Set-Cookie.
|
|
12
|
+
*
|
|
13
|
+
* Independent from authenticated identity: an authenticated user can still
|
|
14
|
+
* have an anon id cookie. Whether to consult it for a logged-in caller is up
|
|
15
|
+
* to the caller (hazo_feedback, for example, doesn't bother).
|
|
16
|
+
*
|
|
17
|
+
* @param request - The incoming NextRequest.
|
|
18
|
+
* @returns The existing or freshly-issued anonymous visitor id (UUID v4 string).
|
|
19
|
+
*/
|
|
20
|
+
export declare function ensure_anon_id(request: NextRequest): Promise<string>;
|
|
21
|
+
//# sourceMappingURL=ensure_anon_id.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensure_anon_id.server.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/ensure_anon_id.server.ts"],"names":[],"mappings":"AAqBA,OAAO,aAAa,CAAC;AAGrB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgB1C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B1E"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// file_description: server-only helper to issue/read a stable opaque anonymous visitor ID via httpOnly cookie
|
|
2
|
+
//
|
|
3
|
+
// Used by hazo_feedback (and any future caller) to group submissions from the
|
|
4
|
+
// same anonymous visitor across page loads / weeks without requiring login.
|
|
5
|
+
//
|
|
6
|
+
// Signature choice (v5.2.0): async, request-only — Option A in the design doc.
|
|
7
|
+
// - Reads the existing cookie from the incoming `request.cookies`.
|
|
8
|
+
// - Writes a freshly-issued cookie via the async `cookies()` API from
|
|
9
|
+
// `next/headers`, which is the supported write surface in Server Components,
|
|
10
|
+
// Server Actions, and Route Handlers in Next.js 15+.
|
|
11
|
+
// - Tradeoff: NOT valid in Edge middleware (where `next/headers` throws). If a
|
|
12
|
+
// middleware caller ever needs this, add a sibling `ensure_anon_id_edge`
|
|
13
|
+
// that takes (request, response) and writes via `response.cookies.set`.
|
|
14
|
+
//
|
|
15
|
+
// Idempotent per-request: a per-request WeakMap remembers which id was issued
|
|
16
|
+
// for a given NextRequest instance, so calling `ensure_anon_id(request)` twice
|
|
17
|
+
// in the same request returns the same id and queues only one Set-Cookie. This
|
|
18
|
+
// matters because `request.cookies` reflects the *incoming* state and won't
|
|
19
|
+
// see an id that the same request just issued.
|
|
20
|
+
// section: server-only-guard
|
|
21
|
+
import "server-only";
|
|
22
|
+
import { cookies } from "next/headers";
|
|
23
|
+
import { BASE_COOKIE_NAMES, get_cookie_name, get_cookie_options, } from "../cookies_config.server.js";
|
|
24
|
+
// section: constants
|
|
25
|
+
const TWO_YEARS_SECONDS = 60 * 60 * 24 * 365 * 2;
|
|
26
|
+
// section: state
|
|
27
|
+
// WeakMap keyed by NextRequest so entries are GC'd with the request.
|
|
28
|
+
const issued_per_request = new WeakMap();
|
|
29
|
+
// section: main_function
|
|
30
|
+
/**
|
|
31
|
+
* Returns a stable opaque anonymous visitor ID for the caller, reading an
|
|
32
|
+
* existing httpOnly cookie or issuing a fresh UUID v4 if absent.
|
|
33
|
+
*
|
|
34
|
+
* - Cookie name: `hazo_auth_anon_id` (prefixed via `[hazo_auth__cookies] cookie_prefix`)
|
|
35
|
+
* - Cookie attributes: httpOnly, sameSite=lax, path=/, secure in production,
|
|
36
|
+
* maxAge = 2 years.
|
|
37
|
+
* - Idempotent per-request: calling twice with the same NextRequest returns
|
|
38
|
+
* the same id and only queues one Set-Cookie.
|
|
39
|
+
*
|
|
40
|
+
* Independent from authenticated identity: an authenticated user can still
|
|
41
|
+
* have an anon id cookie. Whether to consult it for a logged-in caller is up
|
|
42
|
+
* to the caller (hazo_feedback, for example, doesn't bother).
|
|
43
|
+
*
|
|
44
|
+
* @param request - The incoming NextRequest.
|
|
45
|
+
* @returns The existing or freshly-issued anonymous visitor id (UUID v4 string).
|
|
46
|
+
*/
|
|
47
|
+
export async function ensure_anon_id(request) {
|
|
48
|
+
var _a;
|
|
49
|
+
const cookie_name = get_cookie_name(BASE_COOKIE_NAMES.ANON_ID);
|
|
50
|
+
// Per-request idempotency: if we already issued for this NextRequest, reuse it.
|
|
51
|
+
const already_issued = issued_per_request.get(request);
|
|
52
|
+
if (already_issued) {
|
|
53
|
+
return already_issued;
|
|
54
|
+
}
|
|
55
|
+
// Read the existing cookie from the incoming request.
|
|
56
|
+
const existing = (_a = request.cookies.get(cookie_name)) === null || _a === void 0 ? void 0 : _a.value;
|
|
57
|
+
if (existing) {
|
|
58
|
+
return existing;
|
|
59
|
+
}
|
|
60
|
+
// Issue a new id and queue the Set-Cookie via the next/headers cookie store.
|
|
61
|
+
const new_id = crypto.randomUUID();
|
|
62
|
+
const cookie_options = get_cookie_options({
|
|
63
|
+
httpOnly: true,
|
|
64
|
+
secure: process.env.NODE_ENV === "production",
|
|
65
|
+
sameSite: "lax",
|
|
66
|
+
path: "/",
|
|
67
|
+
maxAge: TWO_YEARS_SECONDS,
|
|
68
|
+
});
|
|
69
|
+
const cookie_store = await cookies();
|
|
70
|
+
cookie_store.set(cookie_name, new_id, cookie_options);
|
|
71
|
+
issued_per_request.set(request, new_id);
|
|
72
|
+
return new_id;
|
|
73
|
+
}
|
package/dist/lib/auth/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from "./auth_types.js";
|
|
|
2
2
|
export { hazo_get_auth } from "./hazo_get_auth.server.js";
|
|
3
3
|
export { get_authenticated_user, require_auth, is_authenticated, } from "./auth_utils.server.js";
|
|
4
4
|
export type { AuthResult, AuthUser } from "./auth_utils.server";
|
|
5
|
+
export { ensure_anon_id } from "./ensure_anon_id.server.js";
|
|
5
6
|
export { hazo_get_tenant_auth, require_tenant_auth, extract_scope_id_from_request, } from "./hazo_get_tenant_auth.server.js";
|
|
6
7
|
export type { ScopeDetails, TenantOrganization, TenantAuthOptions, TenantAuthResult, RequiredTenantAuthResult, } from "./auth_types";
|
|
7
8
|
export { HazoAuthError, AuthenticationRequiredError, TenantRequiredError, TenantAccessDeniedError, } from "./auth_types.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/index.ts"],"names":[],"mappings":"AAEA,cAAc,cAAc,CAAC;AAG7B,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGhE,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/index.ts"],"names":[],"mappings":"AAEA,cAAc,cAAc,CAAC;AAG7B,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGhE,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,aAAa,EACb,2BAA2B,EAC3B,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/lib/auth/index.js
CHANGED
|
@@ -4,6 +4,8 @@ export * from "./auth_types.js";
|
|
|
4
4
|
// section: server_exports
|
|
5
5
|
export { hazo_get_auth } from "./hazo_get_auth.server.js";
|
|
6
6
|
export { get_authenticated_user, require_auth, is_authenticated, } from "./auth_utils.server.js";
|
|
7
|
+
// section: anon_id_exports (v5.2)
|
|
8
|
+
export { ensure_anon_id } from "./ensure_anon_id.server.js";
|
|
7
9
|
// section: tenant_auth_exports
|
|
8
10
|
export { hazo_get_tenant_auth, require_tenant_auth, extract_scope_id_from_request, } from "./hazo_get_tenant_auth.server.js";
|
|
9
11
|
export { HazoAuthError, AuthenticationRequiredError, TenantRequiredError, TenantAccessDeniedError, } from "./auth_types.js";
|
|
@@ -3,6 +3,7 @@ export declare const BASE_COOKIE_NAMES: {
|
|
|
3
3
|
readonly USER_EMAIL: "hazo_auth_user_email";
|
|
4
4
|
readonly SESSION: "hazo_auth_session";
|
|
5
5
|
readonly DEV_LOCK: "hazo_auth_dev_lock";
|
|
6
|
+
readonly ANON_ID: "hazo_auth_anon_id";
|
|
6
7
|
};
|
|
7
8
|
/**
|
|
8
9
|
* Gets the cookie prefix from environment variable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies_config.edge.d.ts","sourceRoot":"","sources":["../../src/lib/cookies_config.edge.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"cookies_config.edge.d.ts","sourceRoot":"","sources":["../../src/lib/cookies_config.edge.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,iBAAiB;;;;;;CAMpB,CAAC;AAGX;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAc/C;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAQ9G"}
|
|
@@ -7,6 +7,7 @@ export const BASE_COOKIE_NAMES = {
|
|
|
7
7
|
USER_EMAIL: "hazo_auth_user_email",
|
|
8
8
|
SESSION: "hazo_auth_session",
|
|
9
9
|
DEV_LOCK: "hazo_auth_dev_lock",
|
|
10
|
+
ANON_ID: "hazo_auth_anon_id", // v5.2: Stable opaque per-visitor ID for anonymous flows (e.g. hazo_feedback). Kept in sync with the server constant.
|
|
10
11
|
};
|
|
11
12
|
// section: main_functions
|
|
12
13
|
/**
|
|
@@ -11,6 +11,7 @@ export declare const BASE_COOKIE_NAMES: {
|
|
|
11
11
|
readonly SESSION: "hazo_auth_session";
|
|
12
12
|
readonly DEV_LOCK: "hazo_auth_dev_lock";
|
|
13
13
|
readonly SCOPE_ID: "hazo_auth_scope_id";
|
|
14
|
+
readonly ANON_ID: "hazo_auth_anon_id";
|
|
14
15
|
};
|
|
15
16
|
/**
|
|
16
17
|
* Reads cookie configuration from hazo_auth_config.ini file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies_config.server.d.ts","sourceRoot":"","sources":["../../src/lib/cookies_config.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAMrB,MAAM,MAAM,aAAa,GAAG;IAC1B,6FAA6F;IAC7F,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAYF,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"cookies_config.server.d.ts","sourceRoot":"","sources":["../../src/lib/cookies_config.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAMrB,MAAM,MAAM,aAAa,GAAG;IAC1B,6FAA6F;IAC7F,aAAa,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAYF,eAAO,MAAM,iBAAiB;;;;;;;CAOpB,CAAC;AAGX;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,aAAa,CAoBlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAQzG;AAKD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,aAAa,CAKzD;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
|
|
@@ -16,6 +16,7 @@ export const BASE_COOKIE_NAMES = {
|
|
|
16
16
|
SESSION: "hazo_auth_session",
|
|
17
17
|
DEV_LOCK: "hazo_auth_dev_lock",
|
|
18
18
|
SCOPE_ID: "hazo_auth_scope_id", // v5.2: Tenant context cookie for multi-tenancy
|
|
19
|
+
ANON_ID: "hazo_auth_anon_id", // v5.2: Stable opaque per-visitor ID for anonymous flows (e.g. hazo_feedback)
|
|
19
20
|
};
|
|
20
21
|
// section: main_function
|
|
21
22
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hazo_connect_setup.server.d.ts","sourceRoot":"","sources":["../../src/lib/hazo_connect_setup.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"hazo_connect_setup.server.d.ts","sourceRoot":"","sources":["../../src/lib/hazo_connect_setup.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAsJrB;;;;GAIG;AACH,wBAAgB,iCAAiC,8CAuBhD;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,IAAI;IACjD,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAsBA"}
|
|
@@ -78,14 +78,6 @@ function get_hazo_connect_config() {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
// Log the resolved path so consumers can see which DB file is being used
|
|
82
|
-
logger.info("hazo_connect_sqlite_path_resolved", {
|
|
83
|
-
filename: "hazo_connect_setup.server.ts",
|
|
84
|
-
line_number: 0,
|
|
85
|
-
sqlite_path,
|
|
86
|
-
process_cwd: process.cwd(),
|
|
87
|
-
config_source: hazo_connect_section ? "hazo_auth_config.ini" : "environment variables",
|
|
88
|
-
});
|
|
89
81
|
return {
|
|
90
82
|
type: "sqlite",
|
|
91
83
|
sqlitePath: sqlite_path,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const SQLITE_SCHEMA = "\n-- hazo_auth canonical SQLite schema\n-- This schema creates all tables required by hazo_auth\n\n-- Users table (from migration 011)\nCREATE TABLE IF NOT EXISTS hazo_users (\n id TEXT PRIMARY KEY,\n email_address TEXT NOT NULL UNIQUE,\n password_hash TEXT,\n name TEXT,\n email_verified
|
|
1
|
+
export declare const SQLITE_SCHEMA = "\n-- hazo_auth canonical SQLite schema\n-- This schema creates all tables required by hazo_auth\n\n-- Users table (from migration 011)\nCREATE TABLE IF NOT EXISTS hazo_users (\n id TEXT PRIMARY KEY,\n email_address TEXT NOT NULL UNIQUE,\n password_hash TEXT,\n name TEXT,\n email_verified BOOLEAN DEFAULT false,\n login_attempts INTEGER DEFAULT 0,\n last_logon TEXT,\n profile_picture_url TEXT,\n profile_source TEXT CHECK(profile_source IN ('gravatar', 'custom', 'predefined')),\n mfa_secret TEXT,\n url_on_logon TEXT,\n google_id TEXT UNIQUE,\n auth_providers TEXT DEFAULT 'email',\n user_type TEXT,\n app_user_data TEXT,\n status TEXT DEFAULT 'ACTIVE' CHECK(status IN ('PENDING', 'ACTIVE', 'BLOCKED')),\n managed_by_user_id TEXT REFERENCES hazo_users(id) ON DELETE SET NULL,\n pin_hash TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n changed_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_hazo_users_email ON hazo_users(email_address);\nCREATE INDEX IF NOT EXISTS idx_hazo_users_google_id ON hazo_users(google_id);\nCREATE INDEX IF NOT EXISTS idx_hazo_users_status ON hazo_users(status);\n\n-- Refresh tokens table\n-- Note: runtime (token_service.ts) writes token_hash (argon2-hashed value) on\n-- insert and verifies via argon2.verify(token_hash, plaintext_token) on read.\n-- The plaintext \"token\" column is retained nullable for legacy compatibility\n-- but new writes only set token_hash.\nCREATE TABLE IF NOT EXISTS hazo_refresh_tokens (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,\n token TEXT UNIQUE,\n token_hash TEXT,\n token_type TEXT DEFAULT 'refresh',\n expires_at TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_hazo_refresh_tokens_user ON hazo_refresh_tokens(user_id);\nCREATE INDEX IF NOT EXISTS idx_hazo_refresh_tokens_token ON hazo_refresh_tokens(token);\nCREATE INDEX IF NOT EXISTS idx_hazo_refresh_tokens_token_hash ON hazo_refresh_tokens(token_hash);\n\n-- Roles table\nCREATE TABLE IF NOT EXISTS hazo_roles (\n id TEXT PRIMARY KEY,\n role_name TEXT NOT NULL UNIQUE,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n changed_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Permissions table\nCREATE TABLE IF NOT EXISTS hazo_permissions (\n id TEXT PRIMARY KEY,\n permission_name TEXT NOT NULL UNIQUE,\n description TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n changed_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Role-permission assignments (composite PK, no id column)\nCREATE TABLE IF NOT EXISTS hazo_role_permissions (\n role_id TEXT NOT NULL REFERENCES hazo_roles(id) ON DELETE CASCADE,\n permission_id TEXT NOT NULL REFERENCES hazo_permissions(id) ON DELETE CASCADE,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (role_id, permission_id)\n);\n\n-- Unified scopes table (hierarchical multi-tenancy)\nCREATE TABLE IF NOT EXISTS hazo_scopes (\n id TEXT PRIMARY KEY,\n parent_id TEXT REFERENCES hazo_scopes(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n level TEXT NOT NULL,\n logo_url TEXT,\n primary_color TEXT,\n secondary_color TEXT,\n tagline TEXT,\n slug TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n changed_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_hazo_scopes_parent ON hazo_scopes(parent_id);\nCREATE INDEX IF NOT EXISTS idx_hazo_scopes_level ON hazo_scopes(level);\nCREATE INDEX IF NOT EXISTS idx_hazo_scopes_slug ON hazo_scopes(slug);\n\n-- Super admin scope\nINSERT OR IGNORE INTO hazo_scopes (id, parent_id, name, level, created_at, changed_at)\nVALUES ('00000000-0000-0000-0000-000000000000', NULL, 'Super Admin', 'system', datetime('now'), datetime('now'));\n\n-- Default system scope (for non-multi-tenancy mode)\nINSERT OR IGNORE INTO hazo_scopes (id, parent_id, name, level, created_at, changed_at)\nVALUES ('00000000-0000-0000-0000-000000000001', NULL, 'System', 'default', datetime('now'), datetime('now'));\n\n-- User-scope assignments (membership model with scope-specific roles)\nCREATE TABLE IF NOT EXISTS hazo_user_scopes (\n user_id TEXT NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,\n scope_id TEXT NOT NULL REFERENCES hazo_scopes(id) ON DELETE CASCADE,\n root_scope_id TEXT NOT NULL REFERENCES hazo_scopes(id) ON DELETE CASCADE,\n role_id TEXT NOT NULL REFERENCES hazo_roles(id) ON DELETE CASCADE,\n status TEXT DEFAULT 'ACTIVE' CHECK (status IN ('INVITED', 'ACTIVE', 'SUSPENDED', 'DEPARTED')),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n changed_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (user_id, scope_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_hazo_user_scopes_scope ON hazo_user_scopes(scope_id);\nCREATE INDEX IF NOT EXISTS idx_hazo_user_scopes_root ON hazo_user_scopes(root_scope_id);\nCREATE INDEX IF NOT EXISTS idx_hazo_user_scopes_role ON hazo_user_scopes(role_id);\n\n-- Invitations table\nCREATE TABLE IF NOT EXISTS hazo_invitations (\n id TEXT PRIMARY KEY,\n email_address TEXT NOT NULL,\n token TEXT NOT NULL UNIQUE,\n scope_id TEXT NOT NULL REFERENCES hazo_scopes(id) ON DELETE CASCADE,\n root_scope_id TEXT NOT NULL REFERENCES hazo_scopes(id) ON DELETE CASCADE,\n role_id TEXT NOT NULL REFERENCES hazo_roles(id) ON DELETE CASCADE,\n invited_by TEXT REFERENCES hazo_users(id) ON DELETE SET NULL,\n status TEXT NOT NULL DEFAULT 'PENDING' CHECK(status IN ('PENDING', 'ACCEPTED', 'EXPIRED', 'REVOKED')),\n expires_at TEXT NOT NULL,\n accepted_at TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n changed_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_hazo_invitations_email ON hazo_invitations(email_address);\nCREATE INDEX IF NOT EXISTS idx_hazo_invitations_token ON hazo_invitations(token);\nCREATE INDEX IF NOT EXISTS idx_hazo_invitations_scope ON hazo_invitations(scope_id);\nCREATE INDEX IF NOT EXISTS idx_hazo_invitations_status ON hazo_invitations(status);\nCREATE INDEX IF NOT EXISTS idx_hazo_invitations_expires ON hazo_invitations(expires_at);\n\nCREATE INDEX IF NOT EXISTS idx_hazo_users_managed_by ON hazo_users(managed_by_user_id);\n\n-- Relationships table\nCREATE TABLE IF NOT EXISTS hazo_user_relationships (\n id TEXT PRIMARY KEY,\n parent_user_id TEXT NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,\n child_user_id TEXT NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,\n relationship_type TEXT NOT NULL DEFAULT 'parent',\n can_view_progress BOOLEAN DEFAULT true,\n can_edit_profile BOOLEAN DEFAULT true,\n can_delete BOOLEAN DEFAULT false,\n is_self BOOLEAN DEFAULT false,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(parent_user_id, child_user_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_hazo_user_relationships_parent ON hazo_user_relationships(parent_user_id);\nCREATE INDEX IF NOT EXISTS idx_hazo_user_relationships_child ON hazo_user_relationships(child_user_id);\n\n-- Firm admin role (for firm creators)\nINSERT OR IGNORE INTO hazo_roles (id, role_name, created_at, changed_at)\nVALUES (lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab',abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))), 'firm_admin', datetime('now'), datetime('now'));\n";
|
|
2
2
|
//# sourceMappingURL=sqlite_schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite_schema.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/sqlite_schema.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"sqlite_schema.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/sqlite_schema.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa,uuOAmKzB,CAAC"}
|
|
@@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS hazo_users (
|
|
|
11
11
|
email_address TEXT NOT NULL UNIQUE,
|
|
12
12
|
password_hash TEXT,
|
|
13
13
|
name TEXT,
|
|
14
|
-
email_verified
|
|
14
|
+
email_verified BOOLEAN DEFAULT false,
|
|
15
15
|
login_attempts INTEGER DEFAULT 0,
|
|
16
16
|
last_logon TEXT,
|
|
17
17
|
profile_picture_url TEXT,
|
|
@@ -34,10 +34,15 @@ CREATE INDEX IF NOT EXISTS idx_hazo_users_google_id ON hazo_users(google_id);
|
|
|
34
34
|
CREATE INDEX IF NOT EXISTS idx_hazo_users_status ON hazo_users(status);
|
|
35
35
|
|
|
36
36
|
-- Refresh tokens table
|
|
37
|
+
-- Note: runtime (token_service.ts) writes token_hash (argon2-hashed value) on
|
|
38
|
+
-- insert and verifies via argon2.verify(token_hash, plaintext_token) on read.
|
|
39
|
+
-- The plaintext "token" column is retained nullable for legacy compatibility
|
|
40
|
+
-- but new writes only set token_hash.
|
|
37
41
|
CREATE TABLE IF NOT EXISTS hazo_refresh_tokens (
|
|
38
42
|
id TEXT PRIMARY KEY,
|
|
39
43
|
user_id TEXT NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,
|
|
40
|
-
token TEXT
|
|
44
|
+
token TEXT UNIQUE,
|
|
45
|
+
token_hash TEXT,
|
|
41
46
|
token_type TEXT DEFAULT 'refresh',
|
|
42
47
|
expires_at TEXT NOT NULL,
|
|
43
48
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
@@ -45,6 +50,7 @@ CREATE TABLE IF NOT EXISTS hazo_refresh_tokens (
|
|
|
45
50
|
|
|
46
51
|
CREATE INDEX IF NOT EXISTS idx_hazo_refresh_tokens_user ON hazo_refresh_tokens(user_id);
|
|
47
52
|
CREATE INDEX IF NOT EXISTS idx_hazo_refresh_tokens_token ON hazo_refresh_tokens(token);
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_hazo_refresh_tokens_token_hash ON hazo_refresh_tokens(token_hash);
|
|
48
54
|
|
|
49
55
|
-- Roles table
|
|
50
56
|
CREATE TABLE IF NOT EXISTS hazo_roles (
|
|
@@ -144,10 +150,10 @@ CREATE TABLE IF NOT EXISTS hazo_user_relationships (
|
|
|
144
150
|
parent_user_id TEXT NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,
|
|
145
151
|
child_user_id TEXT NOT NULL REFERENCES hazo_users(id) ON DELETE CASCADE,
|
|
146
152
|
relationship_type TEXT NOT NULL DEFAULT 'parent',
|
|
147
|
-
can_view_progress
|
|
148
|
-
can_edit_profile
|
|
149
|
-
can_delete
|
|
150
|
-
is_self
|
|
153
|
+
can_view_progress BOOLEAN DEFAULT true,
|
|
154
|
+
can_edit_profile BOOLEAN DEFAULT true,
|
|
155
|
+
can_delete BOOLEAN DEFAULT false,
|
|
156
|
+
is_self BOOLEAN DEFAULT false,
|
|
151
157
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
152
158
|
UNIQUE(parent_user_id, child_user_id)
|
|
153
159
|
);
|
|
@@ -59,7 +59,7 @@ export async function create_session_token(user_id, email, managed_by_user_id) {
|
|
|
59
59
|
.setIssuedAt(now)
|
|
60
60
|
.setExpirationTime(exp)
|
|
61
61
|
.sign(secret);
|
|
62
|
-
logger.
|
|
62
|
+
logger.debug("session_token_created", {
|
|
63
63
|
filename: get_filename(),
|
|
64
64
|
line_number: get_line_number(),
|
|
65
65
|
user_id,
|
|
@@ -107,7 +107,7 @@ export async function validate_session_token(token) {
|
|
|
107
107
|
});
|
|
108
108
|
return { valid: false };
|
|
109
109
|
}
|
|
110
|
-
logger.
|
|
110
|
+
logger.debug("session_token_validated", {
|
|
111
111
|
filename: get_filename(),
|
|
112
112
|
line_number: get_line_number(),
|
|
113
113
|
user_id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui_shell_config.server.d.ts","sourceRoot":"","sources":["../../src/lib/ui_shell_config.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAIrB,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG9E,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,YAAY,CAAC;AAE9D,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,wBAAwB,EAAE,MAAM,CAAC;IACjC,uBAAuB,EAAE,OAAO,CAAC;IACjC,2BAA2B,EAAE,OAAO,CAAC;IACrC,+CAA+C;IAC/C,MAAM,EAAE,YAAY,CAAC;IACrB,mDAAmD;IACnD,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAGF;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,
|
|
1
|
+
{"version":3,"file":"ui_shell_config.server.d.ts","sourceRoot":"","sources":["../../src/lib/ui_shell_config.server.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAIrB,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG9E,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,YAAY,CAAC;AAE9D,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,wBAAwB,EAAE,MAAM,CAAC;IACjC,uBAAuB,EAAE,OAAO,CAAC;IACjC,2BAA2B,EAAE,OAAO,CAAC;IACrC,+CAA+C;IAC/C,MAAM,EAAE,YAAY,CAAC;IACrB,mDAAmD;IACnD,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAGF;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,CA6DnD"}
|
|
@@ -17,8 +17,12 @@ export function get_ui_shell_config() {
|
|
|
17
17
|
const standalone_description = get_config_value(section, "standalone_description", "Reuse the packaged authentication flows while inheriting your existing app shell styles.");
|
|
18
18
|
const standalone_wrapper_class = get_config_value(section, "standalone_wrapper_class", "cls_standalone_shell flex min-h-screen w-full items-center justify-center bg-background px-4 py-10");
|
|
19
19
|
const standalone_content_class = get_config_value(section, "standalone_content_class", "cls_standalone_shell_content w-full max-w-5xl shadow-xl rounded-2xl border bg-card");
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
// Default to false: the package-default copy ("Welcome to hazo auth" /
|
|
21
|
+
// "Reuse the packaged authentication flows...") is dev/test placeholder
|
|
22
|
+
// text, not production-suitable. Consumers who want their own heading set
|
|
23
|
+
// standalone_heading + standalone_show_heading=true explicitly.
|
|
24
|
+
const standalone_show_heading = get_config_value(section, "standalone_show_heading", "false").toLowerCase() === "true";
|
|
25
|
+
const standalone_show_description = get_config_value(section, "standalone_show_description", "false").toLowerCase() === "true";
|
|
22
26
|
const vertical_center = get_config_value(section, "vertical_center", "true").toLowerCase() === "true";
|
|
23
27
|
const navbar = get_navbar_config();
|
|
24
28
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth_google_callback.d.ts","sourceRoot":"","sources":["../../../src/server/routes/oauth_google_callback.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAwBxD;;;;GAIG;AACH,wBAAsB,GAAG,CAAC,gBAAgB,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"oauth_google_callback.d.ts","sourceRoot":"","sources":["../../../src/server/routes/oauth_google_callback.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAwBxD;;;;GAIG;AACH,wBAAsB,GAAG,CAAC,gBAAgB,EAAE,WAAW,kCA2LtD"}
|
|
@@ -25,10 +25,18 @@ export async function GET(original_request) {
|
|
|
25
25
|
// Detect if request came through HTTPS proxy (Cloudflare tunnel, etc.)
|
|
26
26
|
const is_secure = original_request.headers.get("x-forwarded-proto") === "https" ||
|
|
27
27
|
request.url.startsWith("https://");
|
|
28
|
+
// Resolve the configured sign-in page up-front so the early error redirects
|
|
29
|
+
// (no token / missing data) honour [hazo_auth__oauth] sign_in_page just like
|
|
30
|
+
// the success path. Defaults to `/hazo_auth/login` for back-compat.
|
|
31
|
+
const sign_in_page = get_oauth_config().sign_in_page;
|
|
28
32
|
try {
|
|
29
33
|
// Get the NextAuth token from the session
|
|
30
34
|
// When behind HTTPS proxy, next-auth uses __Secure- cookie prefix
|
|
31
35
|
const token = (await getToken({
|
|
36
|
+
// next-auth@4 was typed against an older next/server NextRequest that
|
|
37
|
+
// still carried `geo`, `ip`, and the `[INTERNALS]` symbol. Next 16
|
|
38
|
+
// dropped those, leaving the two NextRequest types structurally
|
|
39
|
+
// incompatible at the type level even though the runtime shape is fine.
|
|
32
40
|
req: request,
|
|
33
41
|
secureCookie: is_secure,
|
|
34
42
|
}));
|
|
@@ -47,7 +55,7 @@ export async function GET(original_request) {
|
|
|
47
55
|
note: "No NextAuth token found - user may not have completed Google sign-in",
|
|
48
56
|
});
|
|
49
57
|
// Redirect to login with error — use .toString() to ensure absolute URL
|
|
50
|
-
const login_url = new URL(
|
|
58
|
+
const login_url = new URL(sign_in_page, request.url);
|
|
51
59
|
login_url.searchParams.set("error", "oauth_failed");
|
|
52
60
|
return NextResponse.redirect(login_url.toString());
|
|
53
61
|
}
|
|
@@ -60,13 +68,13 @@ export async function GET(original_request) {
|
|
|
60
68
|
has_hazo_user_id: !!token.hazo_user_id,
|
|
61
69
|
has_google_id: !!token.google_id,
|
|
62
70
|
});
|
|
63
|
-
const login_url = new URL(
|
|
71
|
+
const login_url = new URL(sign_in_page, request.url);
|
|
64
72
|
login_url.searchParams.set("error", "oauth_incomplete");
|
|
65
73
|
return NextResponse.redirect(login_url.toString());
|
|
66
74
|
}
|
|
67
75
|
const user_id = token.hazo_user_id;
|
|
68
76
|
const email = token.email;
|
|
69
|
-
logger.
|
|
77
|
+
logger.debug("google_callback_success", {
|
|
70
78
|
filename: get_filename(),
|
|
71
79
|
line_number: get_line_number(),
|
|
72
80
|
user_id,
|
|
@@ -75,13 +83,32 @@ export async function GET(original_request) {
|
|
|
75
83
|
// Get redirect URL based on user's scope/invitation status
|
|
76
84
|
const loginConfig = get_login_config();
|
|
77
85
|
const oauthConfig = get_oauth_config();
|
|
86
|
+
// Per-request override: the login/register layouts can encode a
|
|
87
|
+
// `?next=<path>` query param into the GoogleSignInButton callback URL,
|
|
88
|
+
// letting consumer flows (e.g. invite-link landings) keep the user on
|
|
89
|
+
// their original target after the OAuth round-trip rather than
|
|
90
|
+
// bouncing through the static `oauthConfig.default_redirect`.
|
|
91
|
+
//
|
|
92
|
+
// Validation: must be a same-origin path (starts with `/`, not `//`,
|
|
93
|
+
// no protocol). Anything else is dropped — prevents an open-redirect
|
|
94
|
+
// attack where a hostile referrer crafts a `next=//evil.com` URL.
|
|
95
|
+
const raw_next = request.nextUrl.searchParams.get("next");
|
|
96
|
+
const safe_next = raw_next &&
|
|
97
|
+
raw_next.startsWith("/") &&
|
|
98
|
+
!raw_next.startsWith("//") &&
|
|
99
|
+
!/^[a-z][a-z0-9+.\-]*:/i.test(raw_next)
|
|
100
|
+
? raw_next
|
|
101
|
+
: null;
|
|
78
102
|
// Check if user needs onboarding (no scope, no invitation = create firm)
|
|
79
103
|
const hazoConnect = get_hazo_connect_instance();
|
|
80
104
|
const { redirect_url: determined_redirect, needs_onboarding, invitation_check_skipped, invitation_table_error, } = await get_post_login_redirect(hazoConnect, user_id, email, {
|
|
81
|
-
default_redirect: oauthConfig.default_redirect || loginConfig.redirectRoute || "/",
|
|
105
|
+
default_redirect: safe_next || oauthConfig.default_redirect || loginConfig.redirectRoute || "/",
|
|
82
106
|
create_firm_url: oauthConfig.create_firm_url,
|
|
83
107
|
skip_invitation_check: oauthConfig.skip_invitation_check,
|
|
84
|
-
no_scope_redirect
|
|
108
|
+
// When a per-request next= is set, override no_scope_redirect too.
|
|
109
|
+
// Otherwise an invite recipient (no scope yet) would still get
|
|
110
|
+
// bounced to /onboarding instead of back to the invite landing.
|
|
111
|
+
no_scope_redirect: safe_next || oauthConfig.no_scope_redirect,
|
|
85
112
|
});
|
|
86
113
|
// Log warning if invitation table is missing
|
|
87
114
|
if (invitation_table_error) {
|
|
@@ -93,7 +120,7 @@ export async function GET(original_request) {
|
|
|
93
120
|
note: "hazo_invitations table does not exist - run migration or set skip_invitation_check=true in [hazo_auth__oauth]",
|
|
94
121
|
});
|
|
95
122
|
}
|
|
96
|
-
logger.
|
|
123
|
+
logger.debug("google_callback_post_login_redirect", {
|
|
97
124
|
filename: get_filename(),
|
|
98
125
|
line_number: get_line_number(),
|
|
99
126
|
user_id,
|
|
@@ -16,6 +16,18 @@ export type LoginPageProps = {
|
|
|
16
16
|
* Defaults to "#f1f5f9"
|
|
17
17
|
*/
|
|
18
18
|
image_background_color?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Layout mode (default: `"two_column"`).
|
|
21
|
+
* - `"two_column"` — full server-rendered page with the package's
|
|
22
|
+
* TwoColumnAuthLayout (image on the left, form on the right) wrapped in
|
|
23
|
+
* the standalone AuthPageShell. Backwards-compatible.
|
|
24
|
+
* - `"form_only"` — emits just the form content (no AuthPageShell, no
|
|
25
|
+
* TwoColumnAuthLayout). Use when wrapping the form in your own brand
|
|
26
|
+
* chrome (e.g. a custom split-panel layout or an existing app shell).
|
|
27
|
+
* `image_src` / `image_alt` / `image_background_color` are ignored in
|
|
28
|
+
* this mode.
|
|
29
|
+
*/
|
|
30
|
+
layout?: "two_column" | "form_only";
|
|
19
31
|
};
|
|
20
32
|
/**
|
|
21
33
|
* Zero-config LoginPage server component
|
|
@@ -40,6 +52,6 @@ export type LoginPageProps = {
|
|
|
40
52
|
* @param props - Optional visual customization props
|
|
41
53
|
* @returns Server-rendered login page
|
|
42
54
|
*/
|
|
43
|
-
export default function LoginPage({ image_src, image_alt, image_background_color, }?: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
55
|
+
export default function LoginPage({ image_src, image_alt, image_background_color, layout, }?: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
|
44
56
|
export { LoginPage };
|
|
45
57
|
//# sourceMappingURL=login.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/server_pages/login.tsx"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/server_pages/login.tsx"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AASrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC;IAErC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,YAAY,GAAG,WAAW,CAAC;CACrC,CAAC;AAGF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,SAAS,EACT,SAAS,EACT,sBAAsB,EACtB,MAAqB,GACtB,GAAE,cAAmB,2CA2DrB;AAGD,OAAO,EAAE,SAAS,EAAE,CAAC"}
|