@artatol-acp/auth-nextjs 0.3.8 → 0.4.1

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.
@@ -3,17 +3,29 @@ import { type ReactNode } from 'react';
3
3
  export type ACPAuthContextValue = {
4
4
  user: User | null;
5
5
  isLoading: boolean;
6
- login: (email: string, password: string) => Promise<void>;
6
+ login: (email: string, password: string) => Promise<{
7
+ requiresTwoFactor?: boolean;
8
+ tempToken?: string;
9
+ }>;
10
+ verify2FA: (tempToken: string, code: string) => Promise<void>;
7
11
  logout: () => Promise<void>;
8
12
  refresh: () => Promise<boolean>;
9
13
  resendVerification: (email: string) => Promise<void>;
10
14
  };
11
15
  export type ACPAuthProviderProps = {
12
16
  children: ReactNode;
13
- baseUrl: string;
17
+ /**
18
+ * Base path for auth API routes in your app.
19
+ * @default "/api/auth"
20
+ */
21
+ apiBasePath?: string;
22
+ /**
23
+ * Initial user data from server-side rendering.
24
+ * If provided, skips initial client-side session fetch.
25
+ */
26
+ initialUser?: User | null;
14
27
  };
15
- export declare function ACPAuthProvider({ children, baseUrl }: ACPAuthProviderProps): import("react/jsx-runtime").JSX.Element;
28
+ export declare function ACPAuthProvider({ children, apiBasePath, initialUser, }: ACPAuthProviderProps): import("react/jsx-runtime").JSX.Element;
16
29
  export declare function useAuth(): ACPAuthContextValue;
17
- export { ACPAuthClient } from '@artatol-acp/auth-js';
18
- export type * from '@artatol-acp/auth-js';
30
+ export type { User } from '@artatol-acp/auth-js';
19
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAiB,KAAK,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAuE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5G,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD,CAAC;AAIF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,oBAAoB,2CAuF1E;AAED,wBAAgB,OAAO,IAAI,mBAAmB,CAM7C;AAGD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,mBAAmB,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAuE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5G,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzG,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD,CAAC;AAIF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B,CAAC;AA6BF,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,WAAyB,EACzB,WAAW,GACZ,EAAE,oBAAoB,2CAmHtB;AAED,wBAAgB,OAAO,IAAI,mBAAmB,CAM7C;AAGD,YAAY,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,19 +1,28 @@
1
1
  /** @jsxImportSource react */
2
2
  'use client';
3
3
  import { jsx as _jsx } from "react/jsx-runtime";
4
- import { ACPAuthClient } from '@artatol-acp/auth-js';
5
4
  import { createContext, useContext, useState, useEffect, useRef, useCallback } from 'react';
6
5
  const ACPAuthContext = createContext(null);
7
- export function ACPAuthProvider({ children, baseUrl }) {
8
- const [user, setUser] = useState(null);
9
- const [isLoading, setIsLoading] = useState(true);
10
- const clientRef = useRef(new ACPAuthClient({
11
- baseUrl,
12
- apiKey: '',
13
- autoRefresh: true,
14
- refreshThresholdSeconds: 60
15
- }));
6
+ async function authFetch(path, options = {}) {
7
+ const response = await fetch(path, {
8
+ ...options,
9
+ headers: {
10
+ 'Content-Type': 'application/json',
11
+ ...options.headers,
12
+ },
13
+ credentials: 'include',
14
+ });
15
+ const data = await response.json();
16
+ if (!data.success) {
17
+ throw new Error(data.error.message);
18
+ }
19
+ return data.data;
20
+ }
21
+ export function ACPAuthProvider({ children, apiBasePath = '/api/auth', initialUser, }) {
22
+ const [user, setUser] = useState(initialUser ?? null);
23
+ const [isLoading, setIsLoading] = useState(initialUser === undefined);
16
24
  const intervalRef = useRef(null);
25
+ const apiBase = apiBasePath.replace(/\/$/, '');
17
26
  const clearRefreshInterval = useCallback(() => {
18
27
  if (intervalRef.current) {
19
28
  clearInterval(intervalRef.current);
@@ -22,43 +31,60 @@ export function ACPAuthProvider({ children, baseUrl }) {
22
31
  }, []);
23
32
  const refresh = useCallback(async () => {
24
33
  try {
25
- await clientRef.current.refresh();
26
- const userData = await clientRef.current.me();
27
- setUser(userData);
34
+ const result = await authFetch(`${apiBase}/session`, {
35
+ method: 'POST',
36
+ });
37
+ setUser(result.user);
28
38
  return true;
29
39
  }
30
40
  catch {
41
+ setUser(null);
31
42
  return false;
32
43
  }
33
- }, []);
44
+ }, [apiBase]);
34
45
  const login = useCallback(async (email, password) => {
35
- const result = await clientRef.current.login({ email, password });
36
- if ('requiresTwoFactor' in result) {
37
- throw new Error('2FA_REQUIRED');
46
+ const result = await authFetch(`${apiBase}/login`, {
47
+ method: 'POST',
48
+ body: JSON.stringify({ email, password }),
49
+ });
50
+ if (result.requiresTwoFactor) {
51
+ return { requiresTwoFactor: true, tempToken: result.tempToken };
52
+ }
53
+ if (result.user) {
54
+ setUser(result.user);
38
55
  }
39
- // User logged in successfully, token is automatically set in client
56
+ return {};
57
+ }, [apiBase]);
58
+ const verify2FA = useCallback(async (tempToken, code) => {
59
+ const result = await authFetch(`${apiBase}/verify-2fa`, {
60
+ method: 'POST',
61
+ body: JSON.stringify({ tempToken, code }),
62
+ });
40
63
  setUser(result.user);
41
- }, []);
64
+ }, [apiBase]);
42
65
  const logout = useCallback(async () => {
43
- // First clear the interval to prevent race conditions
44
66
  clearRefreshInterval();
45
- // Then clear user state
46
67
  setUser(null);
47
- // Finally call logout API
48
68
  try {
49
- await clientRef.current.logout();
69
+ await authFetch(`${apiBase}/logout`, { method: 'POST' });
50
70
  }
51
71
  catch {
52
- // Ignore logout errors, user is already logged out locally
72
+ // Ignore logout errors
53
73
  }
54
- }, [clearRefreshInterval]);
74
+ }, [apiBase, clearRefreshInterval]);
55
75
  const resendVerification = useCallback(async (email) => {
56
- await clientRef.current.resendVerificationEmail({ email });
57
- }, []);
58
- // Initial session restore
76
+ await authFetch(`${apiBase}/resend-verification`, {
77
+ method: 'POST',
78
+ body: JSON.stringify({ email }),
79
+ });
80
+ }, [apiBase]);
81
+ // Initial session restore (skip if initialUser was provided)
59
82
  useEffect(() => {
83
+ if (initialUser !== undefined) {
84
+ return;
85
+ }
60
86
  refresh().finally(() => setIsLoading(false));
61
- }, [refresh]);
87
+ }, [refresh, initialUser]);
62
88
  // Auto-refresh interval
63
89
  useEffect(() => {
64
90
  if (!user) {
@@ -76,7 +102,15 @@ export function ACPAuthProvider({ children, baseUrl }) {
76
102
  }, REFRESH_INTERVAL);
77
103
  return clearRefreshInterval;
78
104
  }, [user, refresh, clearRefreshInterval]);
79
- return (_jsx(ACPAuthContext.Provider, { value: { user, isLoading, login, logout, refresh, resendVerification }, children: children }));
105
+ return (_jsx(ACPAuthContext.Provider, { value: {
106
+ user,
107
+ isLoading,
108
+ login,
109
+ verify2FA,
110
+ logout,
111
+ refresh,
112
+ resendVerification,
113
+ }, children: children }));
80
114
  }
81
115
  export function useAuth() {
82
116
  const context = useContext(ACPAuthContext);
@@ -85,6 +119,4 @@ export function useAuth() {
85
119
  }
86
120
  return context;
87
121
  }
88
- // Re-export client for direct use
89
- export { ACPAuthClient } from '@artatol-acp/auth-js';
90
122
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { ACPAuthClient, type User, type FetchFunction } from '@artatol-acp/auth-js';
3
+ export type ACPAuthHandlerOptions = {
4
+ baseUrl: string;
5
+ apiKey?: string;
6
+ /**
7
+ * Cookie configuration
8
+ */
9
+ cookies?: {
10
+ /**
11
+ * Cookie path
12
+ * @default "/"
13
+ */
14
+ path?: string;
15
+ /**
16
+ * Whether to use secure cookies (HTTPS only)
17
+ * @default process.env.NODE_ENV === "production"
18
+ */
19
+ secure?: boolean;
20
+ /**
21
+ * SameSite cookie attribute
22
+ * @default "lax"
23
+ */
24
+ sameSite?: 'strict' | 'lax' | 'none';
25
+ };
26
+ };
27
+ type ApiSuccess<T> = {
28
+ success: true;
29
+ data: T;
30
+ };
31
+ type ApiError = {
32
+ success: false;
33
+ error: {
34
+ message: string;
35
+ code?: string;
36
+ };
37
+ };
38
+ export declare function createAuthHandlers(options: ACPAuthHandlerOptions): {
39
+ authHandler: (request: NextRequest, context: {
40
+ params: Promise<{
41
+ action: string;
42
+ }>;
43
+ }) => Promise<NextResponse<ApiError> | NextResponse<ApiSuccess<{
44
+ requiresTwoFactor: boolean;
45
+ tempToken: string;
46
+ }>> | NextResponse<ApiSuccess<{
47
+ user: User;
48
+ }>> | NextResponse<ApiSuccess<{
49
+ message: string;
50
+ }>>>;
51
+ loginHandler: (request: NextRequest) => Promise<NextResponse<ApiError> | NextResponse<ApiSuccess<{
52
+ requiresTwoFactor: boolean;
53
+ tempToken: string;
54
+ }>> | NextResponse<ApiSuccess<{
55
+ user: User;
56
+ }>>>;
57
+ verify2FAHandler: (request: NextRequest) => Promise<NextResponse<ApiError> | NextResponse<ApiSuccess<{
58
+ user: User;
59
+ }>>>;
60
+ logoutHandler: (_request: NextRequest) => Promise<NextResponse<ApiSuccess<{
61
+ message: string;
62
+ }>>>;
63
+ sessionHandler: (_request: NextRequest) => Promise<NextResponse<ApiError> | NextResponse<ApiSuccess<{
64
+ user: User;
65
+ }>>>;
66
+ registerHandler: (request: NextRequest) => Promise<NextResponse<ApiError> | NextResponse<ApiSuccess<{
67
+ user: User;
68
+ }>>>;
69
+ resendVerificationHandler: (request: NextRequest) => Promise<NextResponse<ApiError> | NextResponse<ApiSuccess<{
70
+ message: string;
71
+ }>>>;
72
+ forgotPasswordHandler: (request: NextRequest) => Promise<NextResponse<ApiError> | NextResponse<ApiSuccess<{
73
+ message: string;
74
+ }>>>;
75
+ resetPasswordHandler: (request: NextRequest) => Promise<NextResponse<ApiError> | NextResponse<ApiSuccess<{
76
+ message: string;
77
+ }>>>;
78
+ setAuthCookies: (accessToken: string, refreshToken?: string) => Promise<void>;
79
+ clearAuthCookies: () => Promise<void>;
80
+ getStoredTokens: () => Promise<{
81
+ accessToken: string | undefined;
82
+ refreshToken: string | undefined;
83
+ }>;
84
+ createClient: (customFetch?: FetchFunction) => ACPAuthClient;
85
+ };
86
+ export type AuthHandlers = ReturnType<typeof createAuthHandlers>;
87
+ export {};
88
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,aAAa,EAAgB,KAAK,IAAI,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAElG,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE;QACR;;;WAGG;QACH,IAAI,CAAC,EAAE,MAAM,CAAC;QACd;;;WAGG;QACH,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB;;;WAGG;QACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;KACtC,CAAC;CACH,CAAC;AAEF,KAAK,UAAU,CAAC,CAAC,IAAI;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC;AAChD,KAAK,QAAQ,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAc9E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB;2BAwWpD,WAAW,WACX;QAAE,MAAM,EAAE,OAAO,CAAC;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE;;;;;;;;4BApQb,WAAW;;;;;;gCAsCP,WAAW;;;8BA8Bb,WAAW;;;+BAqCV,WAAW;;;+BAsDX,WAAW;;;yCAyBD,WAAW;;;qCAyBf,WAAW;;;oCAyBZ,WAAW;;;kCAlRb,MAAM,iBAAiB,MAAM;;;;;;iCAhDpC,aAAa;EAwYlD;AAGD,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -0,0 +1,359 @@
1
+ import { cookies } from 'next/headers';
2
+ import { NextResponse } from 'next/server';
3
+ import { ACPAuthClient, ACPAuthError } from '@artatol-acp/auth-js';
4
+ function jsonResponse(data, status = 200) {
5
+ return NextResponse.json({ success: true, data }, { status });
6
+ }
7
+ function errorResponse(message, status = 400, code) {
8
+ return NextResponse.json({ success: false, error: { message, code } }, { status });
9
+ }
10
+ export function createAuthHandlers(options) {
11
+ const { baseUrl, apiKey = '', cookies: cookieConfig = {}, } = options;
12
+ const { path: cookiePath = '/', secure = process.env.NODE_ENV === 'production', sameSite = 'lax', } = cookieConfig;
13
+ function createClient(customFetch) {
14
+ return new ACPAuthClient({
15
+ baseUrl,
16
+ apiKey,
17
+ fetch: customFetch,
18
+ autoRefresh: false,
19
+ });
20
+ }
21
+ /**
22
+ * Extract refresh_token from Set-Cookie header
23
+ */
24
+ function extractRefreshTokenFromCookies(response) {
25
+ const setCookieHeader = response.headers.get('set-cookie');
26
+ if (!setCookieHeader)
27
+ return null;
28
+ // Parse refresh_token from Set-Cookie header
29
+ const match = setCookieHeader.match(/refresh_token=([^;]+)/);
30
+ return match ? match[1] : null;
31
+ }
32
+ /**
33
+ * Custom fetch that captures the refresh token from Set-Cookie header
34
+ */
35
+ function createCapturingFetch() {
36
+ let capturedRefreshToken = null;
37
+ const capturingFetch = async (url, init) => {
38
+ const response = await fetch(url, init);
39
+ // Try to extract refresh token from response
40
+ const refreshToken = extractRefreshTokenFromCookies(response);
41
+ if (refreshToken) {
42
+ capturedRefreshToken = refreshToken;
43
+ }
44
+ return response;
45
+ };
46
+ return {
47
+ fetch: capturingFetch,
48
+ getRefreshToken: () => capturedRefreshToken,
49
+ };
50
+ }
51
+ async function setAuthCookies(accessToken, refreshToken) {
52
+ const cookieStore = await cookies();
53
+ cookieStore.set('access_token', accessToken, {
54
+ httpOnly: true,
55
+ secure,
56
+ sameSite,
57
+ path: cookiePath,
58
+ maxAge: 60 * 5, // 5 minutes
59
+ });
60
+ if (refreshToken) {
61
+ cookieStore.set('refresh_token', refreshToken, {
62
+ httpOnly: true,
63
+ secure,
64
+ sameSite,
65
+ path: cookiePath,
66
+ maxAge: 60 * 60 * 24 * 30, // 30 days
67
+ });
68
+ }
69
+ }
70
+ async function clearAuthCookies() {
71
+ const cookieStore = await cookies();
72
+ cookieStore.delete('access_token');
73
+ cookieStore.delete('refresh_token');
74
+ }
75
+ async function getStoredTokens() {
76
+ const cookieStore = await cookies();
77
+ return {
78
+ accessToken: cookieStore.get('access_token')?.value,
79
+ refreshToken: cookieStore.get('refresh_token')?.value,
80
+ };
81
+ }
82
+ /**
83
+ * POST /api/auth/login
84
+ * Body: { email: string, password: string }
85
+ */
86
+ async function loginHandler(request) {
87
+ try {
88
+ const body = await request.json();
89
+ const { email, password } = body;
90
+ if (!email || !password) {
91
+ return errorResponse('Email and password are required', 400);
92
+ }
93
+ // Use capturing fetch to extract refresh_token from Set-Cookie header
94
+ const { fetch: capturingFetch, getRefreshToken } = createCapturingFetch();
95
+ const client = createClient(capturingFetch);
96
+ const result = await client.login({ email, password });
97
+ if ('requiresTwoFactor' in result) {
98
+ return jsonResponse({
99
+ requiresTwoFactor: true,
100
+ tempToken: result.tempToken,
101
+ });
102
+ }
103
+ // Get refresh token from Set-Cookie header (auth server doesn't return it in JSON body)
104
+ const refreshToken = getRefreshToken();
105
+ await setAuthCookies(result.accessToken, refreshToken ?? undefined);
106
+ return jsonResponse({ user: result.user });
107
+ }
108
+ catch (error) {
109
+ if (error instanceof ACPAuthError) {
110
+ return errorResponse(error.message, error.statusCode, error.code);
111
+ }
112
+ return errorResponse('Login failed', 500);
113
+ }
114
+ }
115
+ /**
116
+ * POST /api/auth/verify-2fa
117
+ * Body: { tempToken: string, code: string }
118
+ */
119
+ async function verify2FAHandler(request) {
120
+ try {
121
+ const body = await request.json();
122
+ const { tempToken, code } = body;
123
+ if (!tempToken || !code) {
124
+ return errorResponse('Temp token and code are required', 400);
125
+ }
126
+ // Use capturing fetch to extract refresh_token from Set-Cookie header
127
+ const { fetch: capturingFetch, getRefreshToken } = createCapturingFetch();
128
+ const client = createClient(capturingFetch);
129
+ const result = await client.verify2FALogin({ tempToken, code });
130
+ // Get refresh token from Set-Cookie header
131
+ const refreshToken = getRefreshToken();
132
+ await setAuthCookies(result.accessToken, refreshToken ?? undefined);
133
+ return jsonResponse({ user: result.user });
134
+ }
135
+ catch (error) {
136
+ if (error instanceof ACPAuthError) {
137
+ return errorResponse(error.message, error.statusCode, error.code);
138
+ }
139
+ return errorResponse('2FA verification failed', 500);
140
+ }
141
+ }
142
+ /**
143
+ * POST /api/auth/logout
144
+ */
145
+ async function logoutHandler(_request) {
146
+ try {
147
+ const { refreshToken } = await getStoredTokens();
148
+ if (refreshToken) {
149
+ // Create a custom fetch that includes the refresh token cookie
150
+ const customFetch = async (url, init) => {
151
+ return fetch(url, {
152
+ ...init,
153
+ headers: {
154
+ ...init?.headers,
155
+ Cookie: `refresh_token=${refreshToken}`,
156
+ },
157
+ });
158
+ };
159
+ const client = createClient(customFetch);
160
+ try {
161
+ await client.logout();
162
+ }
163
+ catch {
164
+ // Ignore logout API errors
165
+ }
166
+ }
167
+ await clearAuthCookies();
168
+ return jsonResponse({ message: 'Logged out' });
169
+ }
170
+ catch {
171
+ await clearAuthCookies();
172
+ return jsonResponse({ message: 'Logged out' });
173
+ }
174
+ }
175
+ /**
176
+ * POST /api/auth/session
177
+ * Refreshes the session and returns current user
178
+ */
179
+ async function sessionHandler(_request) {
180
+ try {
181
+ const { accessToken, refreshToken } = await getStoredTokens();
182
+ if (!accessToken && !refreshToken) {
183
+ return errorResponse('No session', 401);
184
+ }
185
+ const customFetch = async (url, init) => {
186
+ return fetch(url, {
187
+ ...init,
188
+ headers: {
189
+ ...init?.headers,
190
+ Cookie: `refresh_token=${refreshToken}`,
191
+ },
192
+ });
193
+ };
194
+ const client = createClient(customFetch);
195
+ // Try to refresh the token
196
+ try {
197
+ const refreshResult = await client.refresh();
198
+ await setAuthCookies(refreshResult.accessToken);
199
+ // Get user data with new token
200
+ const user = await client.me(refreshResult.accessToken);
201
+ return jsonResponse({ user });
202
+ }
203
+ catch {
204
+ // Refresh failed, try to use existing access token
205
+ if (accessToken) {
206
+ try {
207
+ const user = await client.me(accessToken);
208
+ return jsonResponse({ user });
209
+ }
210
+ catch {
211
+ await clearAuthCookies();
212
+ return errorResponse('Session expired', 401);
213
+ }
214
+ }
215
+ await clearAuthCookies();
216
+ return errorResponse('Session expired', 401);
217
+ }
218
+ }
219
+ catch (error) {
220
+ if (error instanceof ACPAuthError) {
221
+ return errorResponse(error.message, error.statusCode, error.code);
222
+ }
223
+ return errorResponse('Session check failed', 500);
224
+ }
225
+ }
226
+ /**
227
+ * POST /api/auth/register
228
+ * Body: { email: string, password: string }
229
+ */
230
+ async function registerHandler(request) {
231
+ try {
232
+ const body = await request.json();
233
+ const { email, password } = body;
234
+ if (!email || !password) {
235
+ return errorResponse('Email and password are required', 400);
236
+ }
237
+ const client = createClient();
238
+ const user = await client.register({ email, password });
239
+ return jsonResponse({ user });
240
+ }
241
+ catch (error) {
242
+ if (error instanceof ACPAuthError) {
243
+ return errorResponse(error.message, error.statusCode, error.code);
244
+ }
245
+ return errorResponse('Registration failed', 500);
246
+ }
247
+ }
248
+ /**
249
+ * POST /api/auth/resend-verification
250
+ * Body: { email: string }
251
+ */
252
+ async function resendVerificationHandler(request) {
253
+ try {
254
+ const body = await request.json();
255
+ const { email } = body;
256
+ if (!email) {
257
+ return errorResponse('Email is required', 400);
258
+ }
259
+ const client = createClient();
260
+ await client.resendVerificationEmail({ email });
261
+ return jsonResponse({ message: 'Verification email sent' });
262
+ }
263
+ catch (error) {
264
+ if (error instanceof ACPAuthError) {
265
+ return errorResponse(error.message, error.statusCode, error.code);
266
+ }
267
+ return errorResponse('Failed to send verification email', 500);
268
+ }
269
+ }
270
+ /**
271
+ * POST /api/auth/forgot-password
272
+ * Body: { email: string }
273
+ */
274
+ async function forgotPasswordHandler(request) {
275
+ try {
276
+ const body = await request.json();
277
+ const { email } = body;
278
+ if (!email) {
279
+ return errorResponse('Email is required', 400);
280
+ }
281
+ const client = createClient();
282
+ await client.forgotPassword({ email });
283
+ return jsonResponse({ message: 'Password reset email sent' });
284
+ }
285
+ catch (error) {
286
+ if (error instanceof ACPAuthError) {
287
+ return errorResponse(error.message, error.statusCode, error.code);
288
+ }
289
+ return errorResponse('Failed to send reset email', 500);
290
+ }
291
+ }
292
+ /**
293
+ * POST /api/auth/reset-password
294
+ * Body: { token: string, newPassword: string }
295
+ */
296
+ async function resetPasswordHandler(request) {
297
+ try {
298
+ const body = await request.json();
299
+ const { token, newPassword } = body;
300
+ if (!token || !newPassword) {
301
+ return errorResponse('Token and new password are required', 400);
302
+ }
303
+ const client = createClient();
304
+ await client.resetPassword({ token, newPassword });
305
+ return jsonResponse({ message: 'Password reset successful' });
306
+ }
307
+ catch (error) {
308
+ if (error instanceof ACPAuthError) {
309
+ return errorResponse(error.message, error.statusCode, error.code);
310
+ }
311
+ return errorResponse('Password reset failed', 500);
312
+ }
313
+ }
314
+ /**
315
+ * Main route handler that dispatches to individual handlers
316
+ */
317
+ async function authHandler(request, context) {
318
+ const { action } = await context.params;
319
+ switch (action) {
320
+ case 'login':
321
+ return loginHandler(request);
322
+ case 'verify-2fa':
323
+ return verify2FAHandler(request);
324
+ case 'logout':
325
+ return logoutHandler(request);
326
+ case 'session':
327
+ return sessionHandler(request);
328
+ case 'register':
329
+ return registerHandler(request);
330
+ case 'resend-verification':
331
+ return resendVerificationHandler(request);
332
+ case 'forgot-password':
333
+ return forgotPasswordHandler(request);
334
+ case 'reset-password':
335
+ return resetPasswordHandler(request);
336
+ default:
337
+ return errorResponse('Not found', 404);
338
+ }
339
+ }
340
+ return {
341
+ // Main combined handler
342
+ authHandler,
343
+ // Individual handlers for custom routing
344
+ loginHandler,
345
+ verify2FAHandler,
346
+ logoutHandler,
347
+ sessionHandler,
348
+ registerHandler,
349
+ resendVerificationHandler,
350
+ forgotPasswordHandler,
351
+ resetPasswordHandler,
352
+ // Utility functions
353
+ setAuthCookies,
354
+ clearAuthCookies,
355
+ getStoredTokens,
356
+ createClient,
357
+ };
358
+ }
359
+ //# sourceMappingURL=index.js.map