hazo_auth 5.1.13 → 5.1.14

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.
@@ -59,6 +59,11 @@ const ROUTES: RouteDefinition[] = [
59
59
  { name: "user_management_users_roles", path: "api/hazo_auth/user_management/users/roles", method: "GET", export_name: "userManagementUsersRolesGET" },
60
60
  { name: "user_management_users_roles_post", path: "api/hazo_auth/user_management/users/roles", method: "POST", export_name: "userManagementUsersRolesPOST" },
61
61
  { name: "user_management_users_roles_put", path: "api/hazo_auth/user_management/users/roles", method: "PUT", export_name: "userManagementUsersRolesPUT" },
62
+ // OAuth routes
63
+ { name: "nextauth", path: "api/auth/[...nextauth]", method: "GET", export_name: "nextauthGET" },
64
+ { name: "nextauth_post", path: "api/auth/[...nextauth]", method: "POST", export_name: "nextauthPOST" },
65
+ { name: "oauth_google_callback", path: "api/hazo_auth/oauth/google/callback", method: "GET", export_name: "oauthGoogleCallbackGET" },
66
+ { name: "set_password", path: "api/hazo_auth/set_password", method: "POST", export_name: "setPasswordPOST" },
62
67
  ];
63
68
 
64
69
  const PAGES: PageDefinition[] = [
@@ -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;AA4LF,wBAAgB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,IAAI,CA8DnE"}
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;AAiMF,wBAAgB,eAAe,CAAC,OAAO,GAAE,eAAoB,GAAG,IAAI,CA8DnE"}
@@ -36,6 +36,11 @@ const ROUTES = [
36
36
  { name: "user_management_users_roles", path: "api/hazo_auth/user_management/users/roles", method: "GET", export_name: "userManagementUsersRolesGET" },
37
37
  { name: "user_management_users_roles_post", path: "api/hazo_auth/user_management/users/roles", method: "POST", export_name: "userManagementUsersRolesPOST" },
38
38
  { name: "user_management_users_roles_put", path: "api/hazo_auth/user_management/users/roles", method: "PUT", export_name: "userManagementUsersRolesPUT" },
39
+ // OAuth routes
40
+ { name: "nextauth", path: "api/auth/[...nextauth]", method: "GET", export_name: "nextauthGET" },
41
+ { name: "nextauth_post", path: "api/auth/[...nextauth]", method: "POST", export_name: "nextauthPOST" },
42
+ { name: "oauth_google_callback", path: "api/hazo_auth/oauth/google/callback", method: "GET", export_name: "oauthGoogleCallbackGET" },
43
+ { name: "set_password", path: "api/hazo_auth/set_password", method: "POST", export_name: "setPasswordPOST" },
39
44
  ];
40
45
  const PAGES = [
41
46
  { name: "login", path: "hazo_auth/login", component_name: "LoginPage", import_path: "hazo_auth/pages/login" },
@@ -24,4 +24,7 @@ export { GET as appUserDataGET, PATCH as appUserDataPATCH, PUT as appUserDataPUT
24
24
  export { GET as appUserDataSchemaGET } from "./app_user_data_schema.js";
25
25
  export { GET as invitationsGET, POST as invitationsPOST, PATCH as invitationsPATCH, DELETE as invitationsDELETE } from "./invitations.js";
26
26
  export { POST as createFirmPOST } from "./create_firm.js";
27
+ export { GET as nextauthGET, POST as nextauthPOST } from "./nextauth.js";
28
+ export { GET as oauthGoogleCallbackGET } from "./oauth_google_callback.js";
29
+ export { POST as setPasswordPOST } from "./set_password.js";
27
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/routes/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,GAAG,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAE,IAAI,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,IAAI,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,IAAI,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,GAAG,IAAI,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGzE,OAAO,EAAE,GAAG,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,IAAI,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAG1E,OAAO,EAAE,KAAK,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,IAAI,IAAI,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAE,MAAM,IAAI,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,GAAG,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,GAAG,IAAI,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,GAAG,IAAI,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAGjF,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,IAAI,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAGpE,OAAO,EAAE,GAAG,IAAI,sBAAsB,EAAE,KAAK,IAAI,wBAAwB,EAAE,IAAI,IAAI,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC/I,OAAO,EAAE,GAAG,IAAI,4BAA4B,EAAE,IAAI,IAAI,6BAA6B,EAAE,GAAG,IAAI,4BAA4B,EAAE,MAAM,IAAI,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AAC9M,OAAO,EAAE,GAAG,IAAI,sBAAsB,EAAE,IAAI,IAAI,uBAAuB,EAAE,GAAG,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC3I,OAAO,EAAE,GAAG,IAAI,2BAA2B,EAAE,IAAI,IAAI,4BAA4B,EAAE,GAAG,IAAI,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAGhK,OAAO,EAAE,GAAG,IAAI,cAAc,EAAE,KAAK,IAAI,gBAAgB,EAAE,GAAG,IAAI,cAAc,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1I,OAAO,EAAE,GAAG,IAAI,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGxE,OAAO,EAAE,GAAG,IAAI,cAAc,EAAE,IAAI,IAAI,eAAe,EAAE,KAAK,IAAI,gBAAgB,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1I,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/routes/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,GAAG,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAE,IAAI,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,IAAI,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,IAAI,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,GAAG,IAAI,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGzE,OAAO,EAAE,GAAG,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,IAAI,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAG1E,OAAO,EAAE,KAAK,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,IAAI,IAAI,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAE,MAAM,IAAI,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,GAAG,IAAI,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,GAAG,IAAI,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,GAAG,IAAI,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAGjF,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,IAAI,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAGpE,OAAO,EAAE,GAAG,IAAI,sBAAsB,EAAE,KAAK,IAAI,wBAAwB,EAAE,IAAI,IAAI,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC/I,OAAO,EAAE,GAAG,IAAI,4BAA4B,EAAE,IAAI,IAAI,6BAA6B,EAAE,GAAG,IAAI,4BAA4B,EAAE,MAAM,IAAI,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AAC9M,OAAO,EAAE,GAAG,IAAI,sBAAsB,EAAE,IAAI,IAAI,uBAAuB,EAAE,GAAG,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC3I,OAAO,EAAE,GAAG,IAAI,2BAA2B,EAAE,IAAI,IAAI,4BAA4B,EAAE,GAAG,IAAI,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAGhK,OAAO,EAAE,GAAG,IAAI,cAAc,EAAE,KAAK,IAAI,gBAAgB,EAAE,GAAG,IAAI,cAAc,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1I,OAAO,EAAE,GAAG,IAAI,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGxE,OAAO,EAAE,GAAG,IAAI,cAAc,EAAE,IAAI,IAAI,eAAe,EAAE,KAAK,IAAI,gBAAgB,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1I,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EAAE,GAAG,IAAI,WAAW,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,GAAG,IAAI,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
@@ -35,3 +35,7 @@ export { GET as appUserDataSchemaGET } from "./app_user_data_schema.js";
35
35
  export { GET as invitationsGET, POST as invitationsPOST, PATCH as invitationsPATCH, DELETE as invitationsDELETE } from "./invitations.js";
36
36
  // Create firm routes (for new users creating their firm)
37
37
  export { POST as createFirmPOST } from "./create_firm.js";
38
+ // OAuth routes
39
+ export { GET as nextauthGET, POST as nextauthPOST } from "./nextauth.js";
40
+ export { GET as oauthGoogleCallbackGET } from "./oauth_google_callback.js";
41
+ export { POST as setPasswordPOST } from "./set_password.js";
@@ -0,0 +1,9 @@
1
+ type NextAuthContext = {
2
+ params: Promise<{
3
+ nextauth: string[];
4
+ }>;
5
+ };
6
+ export declare function GET(request: Request, context: NextAuthContext): Promise<any>;
7
+ export declare function POST(request: Request, context: NextAuthContext): Promise<any>;
8
+ export {};
9
+ //# sourceMappingURL=nextauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nextauth.d.ts","sourceRoot":"","sources":["../../../src/server/routes/nextauth.ts"],"names":[],"mappings":"AAMA,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACzC,CAAC;AA8BF,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,gBAGnE;AAED,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,gBAGpE"}
@@ -0,0 +1,36 @@
1
+ // file_description: NextAuth.js route handler for OAuth authentication (for re-export by consuming apps)
2
+ // section: imports
3
+ import NextAuth from "next-auth";
4
+ import { get_nextauth_config } from "../../lib/auth/nextauth_config.js";
5
+ // section: handler
6
+ // Get config lazily to ensure environment variables are available
7
+ function getHandler() {
8
+ var _a;
9
+ const config = get_nextauth_config();
10
+ // Debug logging (remove in production)
11
+ if (process.env.NODE_ENV === "development") {
12
+ console.log("[NextAuth] Creating handler with providers:", ((_a = config.providers) === null || _a === void 0 ? void 0 : _a.length) || 0);
13
+ console.log("[NextAuth] Google Client ID set:", !!process.env.HAZO_AUTH_GOOGLE_CLIENT_ID);
14
+ console.log("[NextAuth] Google Client Secret set:", !!process.env.HAZO_AUTH_GOOGLE_CLIENT_SECRET);
15
+ console.log("[NextAuth] NEXTAUTH_SECRET set:", !!process.env.NEXTAUTH_SECRET);
16
+ console.log("[NextAuth] NEXTAUTH_URL:", process.env.NEXTAUTH_URL);
17
+ }
18
+ return NextAuth(config);
19
+ }
20
+ // Create handler lazily
21
+ let cachedHandler = null;
22
+ function getOrCreateHandler() {
23
+ if (!cachedHandler) {
24
+ cachedHandler = getHandler();
25
+ }
26
+ return cachedHandler;
27
+ }
28
+ // section: exports
29
+ export async function GET(request, context) {
30
+ const handler = getOrCreateHandler();
31
+ return handler(request, context);
32
+ }
33
+ export async function POST(request, context) {
34
+ const handler = getOrCreateHandler();
35
+ return handler(request, context);
36
+ }
@@ -0,0 +1,8 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ /**
3
+ * Handles the OAuth callback after Google sign-in
4
+ * The user creation/linking is done in NextAuth signIn callback
5
+ * This route just sets the hazo_auth session cookies
6
+ */
7
+ export declare function GET(request: NextRequest): Promise<NextResponse<unknown>>;
8
+ //# sourceMappingURL=oauth_google_callback.d.ts.map
@@ -0,0 +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;AAsBxD;;;;GAIG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW,kCA8H7C"}
@@ -0,0 +1,122 @@
1
+ // file_description: Custom OAuth callback handler that creates hazo_auth session after Google sign-in
2
+ // section: imports
3
+ import { NextResponse } from "next/server";
4
+ import { getToken } from "next-auth/jwt";
5
+ import { create_app_logger } from "../../lib/app_logger.js";
6
+ import { create_session_token } from "../../lib/services/session_token_service.js";
7
+ import { get_filename, get_line_number } from "../../lib/utils/api_route_helpers.js";
8
+ import { get_login_config } from "../../lib/login_config.server.js";
9
+ import { get_cookie_name, get_cookie_options, BASE_COOKIE_NAMES } from "../../lib/cookies_config.server.js";
10
+ import { get_hazo_connect_instance } from "../../lib/hazo_connect_instance.server.js";
11
+ import { get_post_login_redirect } from "../../lib/services/post_verification_service.js";
12
+ // section: api_handler
13
+ /**
14
+ * Handles the OAuth callback after Google sign-in
15
+ * The user creation/linking is done in NextAuth signIn callback
16
+ * This route just sets the hazo_auth session cookies
17
+ */
18
+ export async function GET(request) {
19
+ const logger = create_app_logger();
20
+ try {
21
+ // Get the NextAuth token from the session
22
+ const token = (await getToken({ req: request }));
23
+ logger.debug("google_callback_token_received", {
24
+ filename: get_filename(),
25
+ line_number: get_line_number(),
26
+ has_token: !!token,
27
+ has_email: !!(token === null || token === void 0 ? void 0 : token.email),
28
+ has_hazo_user_id: !!(token === null || token === void 0 ? void 0 : token.hazo_user_id),
29
+ has_google_id: !!(token === null || token === void 0 ? void 0 : token.google_id),
30
+ });
31
+ if (!token) {
32
+ logger.warn("google_callback_no_token", {
33
+ filename: get_filename(),
34
+ line_number: get_line_number(),
35
+ note: "No NextAuth token found - user may not have completed Google sign-in",
36
+ });
37
+ // Redirect to login with error
38
+ const login_url = new URL("/hazo_auth/login", request.url);
39
+ login_url.searchParams.set("error", "oauth_failed");
40
+ return NextResponse.redirect(login_url);
41
+ }
42
+ // Validate we have the required data
43
+ if (!token.email || !token.hazo_user_id) {
44
+ logger.warn("google_callback_missing_data", {
45
+ filename: get_filename(),
46
+ line_number: get_line_number(),
47
+ has_email: !!token.email,
48
+ has_hazo_user_id: !!token.hazo_user_id,
49
+ has_google_id: !!token.google_id,
50
+ });
51
+ const login_url = new URL("/hazo_auth/login", request.url);
52
+ login_url.searchParams.set("error", "oauth_incomplete");
53
+ return NextResponse.redirect(login_url);
54
+ }
55
+ const user_id = token.hazo_user_id;
56
+ const email = token.email;
57
+ logger.info("google_callback_success", {
58
+ filename: get_filename(),
59
+ line_number: get_line_number(),
60
+ user_id,
61
+ email,
62
+ });
63
+ // Get redirect URL based on user's scope/invitation status
64
+ const loginConfig = get_login_config();
65
+ const default_redirect = loginConfig.redirectRoute || "/";
66
+ // Check if user needs onboarding (no scope, no invitation = create firm)
67
+ const hazoConnect = get_hazo_connect_instance();
68
+ const { redirect_url: determined_redirect, needs_onboarding } = await get_post_login_redirect(hazoConnect, user_id, email, default_redirect);
69
+ logger.info("google_callback_post_login_redirect", {
70
+ filename: get_filename(),
71
+ line_number: get_line_number(),
72
+ user_id,
73
+ email,
74
+ redirect_url: determined_redirect,
75
+ needs_onboarding,
76
+ });
77
+ // Create redirect response
78
+ const redirect_url = new URL(determined_redirect, request.url);
79
+ const response = NextResponse.redirect(redirect_url);
80
+ // Set authentication cookies (same as login route, with configurable prefix and domain)
81
+ const base_cookie_options = {
82
+ httpOnly: true,
83
+ secure: process.env.NODE_ENV === "production",
84
+ sameSite: "lax",
85
+ path: "/",
86
+ maxAge: 60 * 60 * 24 * 30, // 30 days
87
+ };
88
+ const cookie_options = get_cookie_options(base_cookie_options);
89
+ response.cookies.set(get_cookie_name(BASE_COOKIE_NAMES.USER_ID), user_id, cookie_options);
90
+ response.cookies.set(get_cookie_name(BASE_COOKIE_NAMES.USER_EMAIL), email, cookie_options);
91
+ // Create and set JWT session token
92
+ try {
93
+ const session_token = await create_session_token(user_id, email);
94
+ response.cookies.set(get_cookie_name(BASE_COOKIE_NAMES.SESSION), session_token, cookie_options);
95
+ }
96
+ catch (token_error) {
97
+ const token_error_message = token_error instanceof Error ? token_error.message : "Unknown error";
98
+ logger.warn("google_callback_session_token_creation_failed", {
99
+ filename: get_filename(),
100
+ line_number: get_line_number(),
101
+ user_id,
102
+ email,
103
+ error: token_error_message,
104
+ note: "OAuth login succeeded but session token creation failed - using legacy cookies",
105
+ });
106
+ }
107
+ return response;
108
+ }
109
+ catch (error) {
110
+ const error_message = error instanceof Error ? error.message : "Unknown error";
111
+ const error_stack = error instanceof Error ? error.stack : undefined;
112
+ logger.error("google_callback_error", {
113
+ filename: get_filename(),
114
+ line_number: get_line_number(),
115
+ error_message,
116
+ error_stack,
117
+ });
118
+ const login_url = new URL("/hazo_auth/login", request.url);
119
+ login_url.searchParams.set("error", "oauth_error");
120
+ return NextResponse.redirect(login_url);
121
+ }
122
+ }
@@ -0,0 +1,13 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ /**
3
+ * POST /api/hazo_auth/set_password
4
+ * Allows OAuth-only users (e.g., Google sign-in users) to set a password
5
+ * This enables them to use email/password login in addition to OAuth
6
+ */
7
+ export declare function POST(request: NextRequest): Promise<NextResponse<{
8
+ error: string;
9
+ }> | NextResponse<{
10
+ success: boolean;
11
+ message: string;
12
+ }>>;
13
+ //# sourceMappingURL=set_password.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set_password.d.ts","sourceRoot":"","sources":["../../../src/server/routes/set_password.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAYxD;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;IA4G9C"}
@@ -0,0 +1,88 @@
1
+ // file_description: API route for setting password for OAuth-only users
2
+ // section: imports
3
+ import { NextResponse } from "next/server";
4
+ import argon2 from "argon2";
5
+ import { get_hazo_connect_instance } from "../../lib/hazo_connect_instance.server.js";
6
+ import { create_app_logger } from "../../lib/app_logger.js";
7
+ import { hazo_get_auth } from "../../lib/auth/hazo_get_auth.server.js";
8
+ import { user_has_password, set_user_password } from "../../lib/services/oauth_service.js";
9
+ import { get_password_requirements_config } from "../../lib/password_requirements_config.server.js";
10
+ import { validate_password } from "../../lib/utils/password_validator.js";
11
+ import { get_filename, get_line_number } from "../../lib/utils/api_route_helpers.js";
12
+ import { get_auth_cache } from "../../lib/auth/auth_cache.js";
13
+ // section: api_handler
14
+ /**
15
+ * POST /api/hazo_auth/set_password
16
+ * Allows OAuth-only users (e.g., Google sign-in users) to set a password
17
+ * This enables them to use email/password login in addition to OAuth
18
+ */
19
+ export async function POST(request) {
20
+ const logger = create_app_logger();
21
+ try {
22
+ // Require authentication using hazo_get_auth
23
+ const auth_result = await hazo_get_auth(request);
24
+ if (!auth_result.authenticated) {
25
+ return NextResponse.json({ error: "Authentication required" }, { status: 401 });
26
+ }
27
+ const user_id = auth_result.user.id;
28
+ // Parse request body
29
+ const body = await request.json();
30
+ const { new_password } = body;
31
+ // Validate input
32
+ if (!new_password) {
33
+ return NextResponse.json({ error: "New password is required" }, { status: 400 });
34
+ }
35
+ // Get hazo_connect instance
36
+ const hazoConnect = get_hazo_connect_instance();
37
+ // Check if user already has a password set
38
+ const has_password = await user_has_password(hazoConnect, user_id);
39
+ if (has_password) {
40
+ return NextResponse.json({
41
+ error: "Password already set. Use Change Password instead.",
42
+ has_password: true,
43
+ }, { status: 400 });
44
+ }
45
+ // Validate password against requirements
46
+ const password_config = get_password_requirements_config();
47
+ const validation_result = validate_password(new_password, password_config);
48
+ if (!validation_result.valid) {
49
+ return NextResponse.json({ error: validation_result.errors.join(". ") }, { status: 400 });
50
+ }
51
+ // Hash the new password
52
+ const password_hash = await argon2.hash(new_password);
53
+ // Set the password
54
+ const result = await set_user_password(hazoConnect, user_id, password_hash);
55
+ if (!result.success) {
56
+ logger.error("set_password_failed", {
57
+ filename: get_filename(),
58
+ line_number: get_line_number(),
59
+ user_id,
60
+ error: result.error,
61
+ });
62
+ return NextResponse.json({ error: result.error || "Failed to set password" }, { status: 500 });
63
+ }
64
+ // Invalidate auth cache for this user
65
+ const auth_cache = get_auth_cache();
66
+ auth_cache.invalidate_user(user_id);
67
+ logger.info("set_password_success", {
68
+ filename: get_filename(),
69
+ line_number: get_line_number(),
70
+ user_id,
71
+ });
72
+ return NextResponse.json({
73
+ success: true,
74
+ message: "Password set successfully. You can now sign in with email and password.",
75
+ }, { status: 200 });
76
+ }
77
+ catch (error) {
78
+ const error_message = error instanceof Error ? error.message : "Unknown error";
79
+ const error_stack = error instanceof Error ? error.stack : undefined;
80
+ logger.error("set_password_error", {
81
+ filename: get_filename(),
82
+ line_number: get_line_number(),
83
+ error_message,
84
+ error_stack,
85
+ });
86
+ return NextResponse.json({ error: "Failed to set password. Please try again." }, { status: 500 });
87
+ }
88
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_auth",
3
- "version": "5.1.13",
3
+ "version": "5.1.14",
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",