@civic/auth 0.13.0-beta.0 → 0.13.0-beta.2

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.
@@ -7,7 +7,7 @@ import { clearAuthCookies, NextjsCookieStorage } from "../nextjs/cookies.js";
7
7
  import { AuthFlowCookie, CodeVerifier, UserStorage, } from "../shared/lib/types.js";
8
8
  import { revalidatePath } from "next/cache.js";
9
9
  import { NextResponse } from "next/server.js";
10
- import { prependBasePath, redirectWithBasePath } from "./utils.js";
10
+ import { getOriginUrl, redirectWithBasePath, sanitizeReturnUrl, } from "./utils.js";
11
11
  const logger = loggers.nextjs.handlers.auth;
12
12
  class AuthError extends Error {
13
13
  status;
@@ -62,7 +62,9 @@ const createCivicAuth = (request, config) => {
62
62
  redirectUrl: absoluteCallbackUrl,
63
63
  oauthServer: resolvedConfig.oauthServer,
64
64
  postLogoutRedirectUrl: absoluteLogoutCallbackUrl,
65
- loginSuccessUrl: request.url,
65
+ // Note: Do NOT use request.url here - during callback, that would be the callback URL itself,
66
+ // causing an infinite redirect loop in iframe mode fallbacks.
67
+ loginSuccessUrl: resolvedConfig.loginSuccessUrl,
66
68
  });
67
69
  return {
68
70
  civicAuth,
@@ -71,6 +73,30 @@ const createCivicAuth = (request, config) => {
71
73
  urlDetectionRequest, // Return for use in handlers
72
74
  };
73
75
  };
76
+ /**
77
+ * Injects loginSuccessUrl into an existing base64-encoded state string, or creates
78
+ * a new state if none exists. This allows the deep link destination (computed by
79
+ * middleware) to be passed through the OAuth flow via the existing loginSuccessUrl
80
+ * mechanism.
81
+ */
82
+ function injectLoginSuccessUrlIntoState(frontendState, loginSuccessUrl) {
83
+ let stateObj = {};
84
+ if (frontendState) {
85
+ try {
86
+ // Decode the existing state
87
+ const jsonString = atob(frontendState);
88
+ stateObj = JSON.parse(jsonString);
89
+ }
90
+ catch (error) {
91
+ logger.warn("[LOGIN_HANDLER] Failed to parse existing state, creating new state", { error });
92
+ // Continue with empty stateObj - we'll still add loginSuccessUrl
93
+ }
94
+ }
95
+ // Set loginSuccessUrl in state (overrides any existing value for fullUrl mode)
96
+ stateObj.loginSuccessUrl = loginSuccessUrl;
97
+ // Encode and return the state
98
+ return btoa(JSON.stringify(stateObj));
99
+ }
74
100
  /**
75
101
  * Login handler - backend OAuth login initiation endpoint
76
102
  * Uses CivicAuth.buildLoginUrl()
@@ -78,13 +104,37 @@ const createCivicAuth = (request, config) => {
78
104
  async function handleLogin(request, config) {
79
105
  const resolvedConfigs = resolveAuthConfig(config);
80
106
  try {
81
- const frontendState = request.nextUrl.searchParams.get("state");
107
+ let frontendState = request.nextUrl.searchParams.get("state");
82
108
  // Store appUrl in cookie if provided as query parameter
83
109
  const appUrlFromQuery = request.nextUrl.searchParams.get("appUrl");
110
+ const cookieStorage = new NextjsCookieStorage(resolvedConfigs.cookies?.tokens ?? {});
84
111
  if (appUrlFromQuery) {
85
- const cookieStorage = new NextjsCookieStorage(resolvedConfigs.cookies?.tokens ?? {});
86
112
  await cookieStorage.set(CodeVerifier.APP_URL, appUrlFromQuery);
87
113
  }
114
+ // Read the deep link cookie (set by middleware with deepLinkHandling already applied)
115
+ // and inject it into the OAuth state's loginSuccessUrl field. This ensures the
116
+ // destination survives the OAuth flow even when cookies aren't available in the
117
+ // callback (e.g., due to SameSite restrictions in Chromium iframes).
118
+ // Note: We don't delete the cookie here because:
119
+ // 1. The login handler may be called multiple times (iframe preload, mode switching)
120
+ // 2. We only want to consume the cookie on successful auth (handled in callback)
121
+ if (resolvedConfigs.deepLinkHandling !== "disabled") {
122
+ const deepLinkDestination = await cookieStorage.get(AuthFlowCookie.RETURN_URL);
123
+ if (deepLinkDestination) {
124
+ // Re-validate the cookie value to guard against tampering (defense-in-depth)
125
+ const originUrl = getOriginUrl(request, resolvedConfigs);
126
+ const sanitized = sanitizeReturnUrl(deepLinkDestination, originUrl);
127
+ if (sanitized) {
128
+ // Inject the destination into state.loginSuccessUrl
129
+ // Don't prepend basePath here - the callback handler's redirectWithBasePath will handle it
130
+ logger.debug("[LOGIN_HANDLER] Found deep link cookie, injecting into state.loginSuccessUrl", { deepLinkDestination, sanitized });
131
+ frontendState = injectLoginSuccessUrlIntoState(frontendState, sanitized);
132
+ }
133
+ else {
134
+ logger.warn("[LOGIN_HANDLER] Rejected invalid deep link cookie value", { deepLinkDestination });
135
+ }
136
+ }
137
+ }
88
138
  const { civicAuth } = createCivicAuth(request, resolvedConfigs);
89
139
  const url = await civicAuth.buildLoginUrl({
90
140
  state: frontendState || undefined,
@@ -96,6 +146,14 @@ async function handleLogin(request, config) {
96
146
  }
97
147
  catch (error) {
98
148
  logger.error("[LOGIN_HANDLER] Backend login error:", error);
149
+ // Clean up deep link cookie on error to prevent stale cookies
150
+ try {
151
+ const errorCookieStorage = new NextjsCookieStorage(resolvedConfigs.cookies?.tokens ?? {});
152
+ await errorCookieStorage.delete(AuthFlowCookie.RETURN_URL);
153
+ }
154
+ catch (cookieError) {
155
+ logger.warn("[LOGIN_HANDLER] Failed to clean up deep link cookie on error", { cookieError });
156
+ }
99
157
  const urlDetectionRequest = toUrlDetectionRequest(request);
100
158
  const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);
101
159
  return NextResponse.redirect(CivicAuth.toAbsoluteUrl(urlDetectionRequest, "/?error=login_failed", appUrl));
@@ -131,64 +189,59 @@ async function handleCallback(request, config) {
131
189
  if (!code || !state)
132
190
  throw new AuthError("Bad parameters", 400);
133
191
  try {
134
- const { civicAuth, appUrl, urlDetectionRequest, cookieStorage } = createCivicAuth(request, resolvedConfigs);
192
+ const { civicAuth, appUrl, urlDetectionRequest } = createCivicAuth(request, resolvedConfigs);
135
193
  // Convert NextRequest to the format expected by handleCallback
136
194
  const handleCallbackRequest = {
137
195
  headers: Object.fromEntries(request.headers.entries()),
138
196
  url: request.url.toString(),
139
197
  };
140
- // Get loginSuccessUrl with proper baseUrl handling
141
- const loginSuccessUrl = CivicAuth.getLoginSuccessUrl(urlDetectionRequest, appUrl);
142
- const defaultFrontendUrl = loginSuccessUrl || resolvedConfigs.loginSuccessUrl || "/";
143
- // Check for preserved deep link (returnUrl cookie)
144
- const returnUrl = await cookieStorage.get(AuthFlowCookie.RETURN_URL);
145
- // Delete the cookie immediately after reading (single-use)
146
- if (returnUrl) {
147
- await cookieStorage.delete(AuthFlowCookie.RETURN_URL);
148
- logger.debug("[CALLBACK_HANDLER] Found returnUrl cookie", { returnUrl });
149
- }
150
- // Determine final redirect URL based on deepLinkHandling config
151
- let frontendUrl;
152
- if (returnUrl && resolvedConfigs.deepLinkHandling === "queryParamsOnly") {
153
- // queryParamsOnly: Use loginSuccessUrl but merge query params from returnUrl
154
- try {
155
- const returnUrlObj = new URL(returnUrl, appUrl);
156
- const baseUrlObj = new URL(defaultFrontendUrl, appUrl);
157
- // Append query params from returnUrl to the base loginSuccessUrl
158
- returnUrlObj.searchParams.forEach((value, key) => {
159
- baseUrlObj.searchParams.set(key, value);
160
- });
161
- frontendUrl = baseUrlObj.pathname + baseUrlObj.search + baseUrlObj.hash;
162
- logger.debug("[CALLBACK_HANDLER] deepLinkHandling=queryParamsOnly: merged returnUrl params", { frontendUrl });
163
- }
164
- catch (error) {
165
- // If URL parsing fails, fall back to default
166
- logger.warn("[CALLBACK_HANDLER] Failed to merge query params from returnUrl", { error, returnUrl, defaultFrontendUrl });
167
- frontendUrl = defaultFrontendUrl;
168
- }
169
- }
170
- else if (returnUrl && resolvedConfigs.deepLinkHandling === "fullUrl") {
171
- // fullUrl: Use the preserved deep link directly
172
- frontendUrl = returnUrl;
173
- logger.debug("[CALLBACK_HANDLER] deepLinkHandling=fullUrl: using preserved deep link", {
174
- frontendUrl,
175
- });
176
- }
177
- else {
178
- // No returnUrl cookie or deepLinkHandling is "disabled" - use default loginSuccessUrl
179
- frontendUrl = defaultFrontendUrl;
180
- }
181
198
  // Use CivicAuth's smart callback handler
199
+ // Note: CivicAuth.handleCallback reads loginSuccessUrl from state (injected by login handler)
200
+ // with fallback to config.loginSuccessUrl, so we don't need to pass frontendUrl option
182
201
  const result = await civicAuth.handleCallback({
183
202
  code,
184
203
  state,
185
204
  req: handleCallbackRequest,
186
- }, {
187
- // Pass the properly resolved frontendUrl
188
- frontendUrl: prependBasePath(frontendUrl, config.basePath || ""),
189
205
  });
206
+ // Fallback: If state was corrupted/lost and CivicAuth redirected to the default loginSuccessUrl,
207
+ // check if we have a deep link cookie as a backup. This provides resilience against state loss.
208
+ const cookieStorage = new NextjsCookieStorage(resolvedConfigs.cookies?.tokens ?? {});
209
+ if (resolvedConfigs.deepLinkHandling !== "disabled" && result.redirectTo) {
210
+ const defaultLoginSuccessUrl = resolvedConfigs.loginSuccessUrl || "/";
211
+ const deepLinkFromCookie = await cookieStorage.get(AuthFlowCookie.RETURN_URL);
212
+ // If redirecting to default and we have a cookie, use the cookie value
213
+ if (deepLinkFromCookie && result.redirectTo === defaultLoginSuccessUrl) {
214
+ // Re-validate the cookie value to guard against tampering (defense-in-depth)
215
+ const originUrl = getOriginUrl(request, resolvedConfigs);
216
+ const sanitized = sanitizeReturnUrl(deepLinkFromCookie, originUrl);
217
+ if (sanitized) {
218
+ // Don't prepend basePath here - redirectWithBasePath will handle it
219
+ logger.debug("[CALLBACK_HANDLER] State missing loginSuccessUrl, using cookie fallback", { deepLinkFromCookie, sanitized });
220
+ result.redirectTo = sanitized;
221
+ }
222
+ else {
223
+ logger.warn("[CALLBACK_HANDLER] Rejected invalid fallback cookie value", { deepLinkFromCookie });
224
+ // Keep the default loginSuccessUrl
225
+ }
226
+ }
227
+ }
228
+ // Helper to clear the deep link cookie on successful auth
229
+ // Always clear the cookie to handle stale cookies from previous sessions
230
+ const clearDeepLinkCookie = (response) => {
231
+ const cookieConfig = resolvedConfigs.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];
232
+ response.cookies.set(AuthFlowCookie.RETURN_URL, "", {
233
+ path: cookieConfig?.path ?? resolvedConfigs.basePath ?? "/",
234
+ httpOnly: cookieConfig?.httpOnly ?? true,
235
+ secure: cookieConfig?.secure ?? true,
236
+ sameSite: cookieConfig?.sameSite ?? "strict",
237
+ maxAge: 0, // Immediately expire the cookie
238
+ });
239
+ logger.debug("[CALLBACK_HANDLER] Clearing deep link cookie after successful auth");
240
+ return response;
241
+ };
190
242
  if (result.redirectTo) {
191
- return redirectWithBasePath(config, CivicAuth.toAbsoluteUrl(urlDetectionRequest, result.redirectTo, appUrl));
243
+ const response = redirectWithBasePath(config, CivicAuth.toAbsoluteUrl(urlDetectionRequest, result.redirectTo, appUrl));
244
+ return clearDeepLinkCookie(response);
192
245
  }
193
246
  if (result.content) {
194
247
  // Handle both string content and object content
@@ -197,20 +250,7 @@ async function handleCallback(request, config) {
197
250
  status: 200,
198
251
  headers: { "Content-Type": "text/html" },
199
252
  });
200
- // If we had a returnUrl cookie, delete it using NextResponse's cookie API.
201
- // This ensures consistent cookie attributes from the resolved config.
202
- if (returnUrl) {
203
- const cookieConfig = resolvedConfigs.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];
204
- response.cookies.set(AuthFlowCookie.RETURN_URL, "", {
205
- path: cookieConfig?.path ?? resolvedConfigs.basePath ?? "/",
206
- httpOnly: cookieConfig?.httpOnly ?? true,
207
- secure: cookieConfig?.secure ?? true,
208
- sameSite: cookieConfig?.sameSite ?? "strict",
209
- maxAge: 0, // Immediately expire the cookie
210
- });
211
- logger.debug("[CALLBACK_HANDLER] Clearing returnUrl cookie via NextResponse");
212
- }
213
- return response;
253
+ return clearDeepLinkCookie(response);
214
254
  }
215
255
  else {
216
256
  // Object content (JSON response)
@@ -218,7 +258,8 @@ async function handleCallback(request, config) {
218
258
  }
219
259
  }
220
260
  // Fallback redirect
221
- return NextResponse.redirect(CivicAuth.toAbsoluteUrl(urlDetectionRequest, "/", appUrl));
261
+ const response = NextResponse.redirect(CivicAuth.toAbsoluteUrl(urlDetectionRequest, "/", appUrl));
262
+ return clearDeepLinkCookie(response);
222
263
  }
223
264
  catch (error) {
224
265
  logger.error("[CALLBACK_HANDLER] OAuth callback error:", error);
@@ -1 +1 @@
1
- {"version":3,"file":"routeHandler.js","sourceRoot":"","sources":["../../src/nextjs/routeHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import { CivicAuth, type UrlDetectionRequest } from \"@civic/auth/server\";\nimport { LOGOUT_SUCCESS_TEXT } from \"@/constants.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { displayModeFromState } from \"@/lib/oauth.js\";\nimport type { AuthConfig } from \"@/nextjs/config.js\";\nimport { resolveAuthConfig } from \"@/nextjs/config.js\";\nimport { clearAuthCookies, NextjsCookieStorage } from \"@/nextjs/cookies.js\";\nimport {\n AuthFlowCookie,\n CodeVerifier,\n UserStorage,\n} from \"@/shared/lib/types.js\";\nimport { revalidatePath } from \"next/cache.js\";\nimport type { NextRequest } from \"next/server.js\";\nimport { NextResponse } from \"next/server.js\";\nimport { prependBasePath, redirectWithBasePath } from \"./utils.js\";\n\nconst logger = loggers.nextjs.handlers.auth;\n\nclass AuthError extends Error {\n constructor(\n message: string,\n public readonly status: number = 401,\n ) {\n super(message);\n this.name = \"AuthError\";\n }\n}\n\n/**\n * Helper to convert NextRequest to UrlDetectionRequest for framework-agnostic URL handling\n */\nconst toUrlDetectionRequest = (request: NextRequest): UrlDetectionRequest => ({\n url: request.url,\n headers: Object.fromEntries(request.headers.entries()),\n searchParams: {\n get: (name: string) => request.nextUrl.searchParams.get(name),\n },\n cookies: {\n get: (name: string) => request.cookies.get(name),\n },\n});\n\n/**\n * Helper to create CivicAuth instance for a request\n * Now handles appUrl detection for proxy environments\n */\nconst createCivicAuth = (request: NextRequest, config: AuthConfig) => {\n const resolvedConfig = resolveAuthConfig(config);\n const cookieStorage = new NextjsCookieStorage({\n ...resolvedConfig.cookies?.tokens,\n [UserStorage.USER]: resolvedConfig.cookies?.user,\n });\n\n // Convert to framework-agnostic request format\n const urlDetectionRequest = toUrlDetectionRequest(request);\n\n // Get appUrl from client (for proxy environments)\n const clientAppUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n\n // Use baseUrl from config, then client appUrl, then request origin\n // This matches the main branch priority: config > client > request\n const appUrl =\n resolvedConfig.baseUrl ||\n clientAppUrl ||\n new URL(urlDetectionRequest.url).origin;\n\n // Build absolute URLs using detected appUrl or request origin\n const absoluteCallbackUrl = resolvedConfig.callbackUrl.startsWith(\"http\")\n ? resolvedConfig.callbackUrl\n : CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n resolvedConfig.callbackUrl,\n appUrl,\n );\n const absoluteLogoutCallbackUrl = resolvedConfig.logoutCallbackUrl.startsWith(\n \"http\",\n )\n ? resolvedConfig.logoutCallbackUrl\n : CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n resolvedConfig.logoutCallbackUrl,\n appUrl,\n );\n\n const civicAuth = new CivicAuth(cookieStorage, {\n disableRefresh: resolvedConfig.disableRefresh,\n clientId: resolvedConfig.clientId,\n redirectUrl: absoluteCallbackUrl,\n oauthServer: resolvedConfig.oauthServer,\n postLogoutRedirectUrl: absoluteLogoutCallbackUrl,\n loginSuccessUrl: request.url,\n });\n\n return {\n civicAuth,\n cookieStorage,\n appUrl, // Return appUrl for use in other functions\n urlDetectionRequest, // Return for use in handlers\n };\n};\n\n/**\n * Login handler - backend OAuth login initiation endpoint\n * Uses CivicAuth.buildLoginUrl()\n */\nasync function handleLogin(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n try {\n const frontendState = request.nextUrl.searchParams.get(\"state\");\n\n // Store appUrl in cookie if provided as query parameter\n const appUrlFromQuery = request.nextUrl.searchParams.get(\"appUrl\");\n if (appUrlFromQuery) {\n const cookieStorage = new NextjsCookieStorage(\n resolvedConfigs.cookies?.tokens ?? {},\n );\n await cookieStorage.set(CodeVerifier.APP_URL, appUrlFromQuery);\n }\n\n const { civicAuth } = createCivicAuth(request, resolvedConfigs);\n\n const url = await civicAuth.buildLoginUrl({\n state: frontendState || undefined,\n });\n\n logger.info(\"[LOGIN_HANDLER] Redirecting to OAuth login URL\", {\n loginUrl: url.toString(),\n });\n\n return NextResponse.redirect(url.toString());\n } catch (error) {\n logger.error(\"[LOGIN_HANDLER] Backend login error:\", error);\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n return NextResponse.redirect(\n CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n \"/?error=login_failed\",\n appUrl,\n ),\n );\n }\n}\n\nasync function handleRefresh(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n try {\n const { civicAuth } = createCivicAuth(request, resolvedConfigs);\n\n await civicAuth.refreshTokens();\n\n logger.info(\"[REFRESH_HANDLER] Tokens refreshed successfully\");\n\n return NextResponse.json({\n status: \"success\",\n message: \"Tokens refreshed\",\n });\n } catch (error) {\n logger.error(\"[REFRESH_HANDLER] Token refresh error:\", error);\n return NextResponse.json(\n { error: \"Token refresh failed\" },\n { status: 500 },\n );\n }\n}\n\nasync function handleCallback(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n const code = request.nextUrl.searchParams.get(\"code\");\n const state = request.nextUrl.searchParams.get(\"state\");\n const error = request.nextUrl.searchParams.get(\"error\");\n\n if (error) {\n logger.error(\"OAuth error in callback:\", error);\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n return NextResponse.redirect(\n CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n \"/?error=oauth_error\",\n appUrl,\n ),\n );\n }\n\n if (!code || !state) throw new AuthError(\"Bad parameters\", 400);\n\n try {\n const { civicAuth, appUrl, urlDetectionRequest, cookieStorage } =\n createCivicAuth(request, resolvedConfigs);\n\n // Convert NextRequest to the format expected by handleCallback\n const handleCallbackRequest = {\n headers: Object.fromEntries(request.headers.entries()),\n url: request.url.toString(),\n };\n\n // Get loginSuccessUrl with proper baseUrl handling\n const loginSuccessUrl = CivicAuth.getLoginSuccessUrl(\n urlDetectionRequest,\n appUrl,\n );\n const defaultFrontendUrl =\n loginSuccessUrl || resolvedConfigs.loginSuccessUrl || \"/\";\n\n // Check for preserved deep link (returnUrl cookie)\n const returnUrl = await cookieStorage.get(AuthFlowCookie.RETURN_URL);\n\n // Delete the cookie immediately after reading (single-use)\n if (returnUrl) {\n await cookieStorage.delete(AuthFlowCookie.RETURN_URL);\n logger.debug(\"[CALLBACK_HANDLER] Found returnUrl cookie\", { returnUrl });\n }\n\n // Determine final redirect URL based on deepLinkHandling config\n let frontendUrl: string;\n if (returnUrl && resolvedConfigs.deepLinkHandling === \"queryParamsOnly\") {\n // queryParamsOnly: Use loginSuccessUrl but merge query params from returnUrl\n try {\n const returnUrlObj = new URL(returnUrl, appUrl);\n const baseUrlObj = new URL(defaultFrontendUrl, appUrl);\n\n // Append query params from returnUrl to the base loginSuccessUrl\n returnUrlObj.searchParams.forEach((value, key) => {\n baseUrlObj.searchParams.set(key, value);\n });\n\n frontendUrl = baseUrlObj.pathname + baseUrlObj.search + baseUrlObj.hash;\n logger.debug(\n \"[CALLBACK_HANDLER] deepLinkHandling=queryParamsOnly: merged returnUrl params\",\n { frontendUrl },\n );\n } catch (error) {\n // If URL parsing fails, fall back to default\n logger.warn(\n \"[CALLBACK_HANDLER] Failed to merge query params from returnUrl\",\n { error, returnUrl, defaultFrontendUrl },\n );\n frontendUrl = defaultFrontendUrl;\n }\n } else if (returnUrl && resolvedConfigs.deepLinkHandling === \"fullUrl\") {\n // fullUrl: Use the preserved deep link directly\n frontendUrl = returnUrl;\n logger.debug(\n \"[CALLBACK_HANDLER] deepLinkHandling=fullUrl: using preserved deep link\",\n {\n frontendUrl,\n },\n );\n } else {\n // No returnUrl cookie or deepLinkHandling is \"disabled\" - use default loginSuccessUrl\n frontendUrl = defaultFrontendUrl;\n }\n\n // Use CivicAuth's smart callback handler\n const result = await civicAuth.handleCallback(\n {\n code,\n state,\n req: handleCallbackRequest,\n },\n {\n // Pass the properly resolved frontendUrl\n frontendUrl: prependBasePath(frontendUrl, config.basePath || \"\"),\n },\n );\n if (result.redirectTo) {\n return redirectWithBasePath(\n config,\n CivicAuth.toAbsoluteUrl(urlDetectionRequest, result.redirectTo, appUrl),\n );\n }\n\n if (result.content) {\n // Handle both string content and object content\n if (typeof result.content === \"string\") {\n const response = new NextResponse(result.content, {\n status: 200,\n headers: { \"Content-Type\": \"text/html\" },\n });\n\n // If we had a returnUrl cookie, delete it using NextResponse's cookie API.\n // This ensures consistent cookie attributes from the resolved config.\n if (returnUrl) {\n const cookieConfig =\n resolvedConfigs.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];\n response.cookies.set(AuthFlowCookie.RETURN_URL, \"\", {\n path: cookieConfig?.path ?? resolvedConfigs.basePath ?? \"/\",\n httpOnly: cookieConfig?.httpOnly ?? true,\n secure: cookieConfig?.secure ?? true,\n sameSite: cookieConfig?.sameSite ?? \"strict\",\n maxAge: 0, // Immediately expire the cookie\n });\n logger.debug(\n \"[CALLBACK_HANDLER] Clearing returnUrl cookie via NextResponse\",\n );\n }\n\n return response;\n } else {\n // Object content (JSON response)\n return NextResponse.json(result.content);\n }\n }\n\n // Fallback redirect\n return NextResponse.redirect(\n CivicAuth.toAbsoluteUrl(urlDetectionRequest, \"/\", appUrl),\n );\n } catch (error) {\n logger.error(\"[CALLBACK_HANDLER] OAuth callback error:\", error);\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n return NextResponse.redirect(\n CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n \"/?error=callback_failed\",\n appUrl,\n ),\n );\n }\n}\n\nconst revalidateUrlPath = async (url: string) => {\n try {\n const path = new URL(url).pathname;\n revalidatePath(path);\n } catch (error) {\n logger.warn(\"Failed to revalidate path after logout:\", error);\n }\n};\n\nexport async function handleLogout(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n // Get framework-agnostic request for URL utilities\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n\n // Read the state from the query parameters\n const state = request.nextUrl.searchParams.get(\"state\");\n const clientLogoutRedirectUrl =\n request.nextUrl.searchParams.get(\"logoutRedirectUrl\");\n\n try {\n logger.info(\"[LOGOUT_HANDLER] Backend logout endpoint called\");\n\n // If client provided a logoutRedirectUrl, override the config\n let configToUse = resolvedConfigs;\n if (clientLogoutRedirectUrl) {\n configToUse = {\n ...resolvedConfigs,\n logoutCallbackUrl: clientLogoutRedirectUrl,\n };\n logger.info(\"[LOGOUT_HANDLER] Overriding logout callback URL\", {\n original: resolvedConfigs.logoutCallbackUrl,\n override: clientLogoutRedirectUrl,\n });\n }\n\n const { civicAuth } = createCivicAuth(request, configToUse);\n\n // Always redirect to OAuth logout (like main branch)\n // Don't validate session - even invalid local sessions should hit OAuth logout\n logger.info(\"[LOGOUT_HANDLER] Processing logout request\", {\n state: !!state,\n });\n\n // Always redirect to OAuth logout endpoint (like main branch)\n // Client-side iframe logic will handle completion and redirect appropriately\n const logoutUrl = await civicAuth.buildLogoutRedirectUrl({\n state: state || undefined,\n });\n\n try {\n await clearAuthCookies(resolvedConfigs);\n } catch (error) {\n logger.error(\"[LOGOUT_HANDLER] Error clearing tokens:\", error);\n }\n\n // Remove state parameter from logout URL to prevent it from appearing in frontend URL\n const cleanLogoutUrl = new URL(logoutUrl);\n cleanLogoutUrl.searchParams.delete(\"state\");\n\n logger.info(\"[LOGOUT_HANDLER] Redirecting to OAuth logout endpoint\", {\n logoutUrl: cleanLogoutUrl.toString(),\n });\n\n return NextResponse.redirect(cleanLogoutUrl.toString());\n } catch (error) {\n logger.error(\"[LOGOUT_HANDLER] Logout error:\", error);\n // If logout URL generation fails, clear tokens and redirect to home\n await clearAuthCookies(resolvedConfigs);\n\n const fallbackUrl =\n clientLogoutRedirectUrl || resolvedConfigs.logoutCallbackUrl;\n const finalFallbackUrl = CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n fallbackUrl,\n appUrl,\n );\n\n return NextResponse.redirect(finalFallbackUrl);\n }\n}\n\n/**\n * Clear session handler - clears all auth cookies server-side.\n * Called by client in parallel with logout iframe to quickly clear HttpOnly cookies.\n *\n * This is part of a parallel logout strategy:\n * - Client calls clearsession (this endpoint) AND loads logout iframe simultaneously\n * - Both requests are sent while cookies are still present\n * - clearsession clears HttpOnly cookies quickly (this endpoint)\n * - logout iframe handles OAuth provider logout (slower, but had cookies when initiated)\n *\n * This ensures cookies are cleared fast (preventing race conditions if user refreshes)\n * while still performing OAuth provider logout.\n */\nasync function handleClearSession(\n _request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n try {\n logger.info(\"[CLEARSESSION_HANDLER] Clearing session cookies\");\n\n // Clear all auth cookies immediately\n await clearAuthCookies(resolvedConfigs);\n\n logger.info(\"[CLEARSESSION_HANDLER] Session cleared successfully\");\n\n return NextResponse.json({\n status: \"success\",\n message: \"Session cleared\",\n });\n } catch (error) {\n logger.error(\"[CLEARSESSION_HANDLER] Error clearing session:\", error);\n return NextResponse.json(\n { error: \"Failed to clear session\" },\n { status: 500 },\n );\n }\n}\n\nexport async function handleLogoutCallback(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n try {\n logger.info(\n \"[LOGOUT_CALLBACK_HANDLER] Backend logout callback endpoint called\",\n );\n\n // Clear authentication cookies\n await clearAuthCookies(resolvedConfigs);\n\n // Get framework-agnostic request and create CivicAuth instance\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const { civicAuth } = createCivicAuth(request, resolvedConfigs);\n\n // Get the state parameter for iframe detection\n const state = request.nextUrl.searchParams.get(\"state\");\n\n // If this is an iframe request, return HTML with logout success signal\n if (state && displayModeFromState(state, \"iframe\") === \"iframe\") {\n // For iframe mode, include the post-logout redirect URL in the HTML\n const postLogoutRedirectUrl =\n civicAuth.getPostLogoutRedirectUrl(urlDetectionRequest);\n const response = new NextResponse(\n `<html lang=\"en\"><span style=\"display:none\">${LOGOUT_SUCCESS_TEXT}<a href=\"${postLogoutRedirectUrl}\" rel=\"civic-auth-post-logout-redirect-url\"></a></span></html>`,\n );\n response.headers.set(\"Content-Type\", \"text/html; charset=utf-8\");\n logger.info(\n \"[LOGOUT_CALLBACK_HANDLER] Returning iframe logout success HTML\",\n { postLogoutRedirectUrl },\n );\n return response;\n }\n\n // For non-iframe requests, redirect to the logout callback URL or post-logout URL\n const redirectUrl = civicAuth.getPostLogoutRedirectUrl(urlDetectionRequest);\n logger.info(\n \"[LOGOUT_CALLBACK_HANDLER] Redirecting to logout callback URL\",\n {\n logoutCallbackUrl: resolvedConfigs.logoutCallbackUrl,\n redirectUrl,\n },\n );\n\n // Revalidate the redirect path to update authentication state in server components\n await revalidateUrlPath(redirectUrl);\n return redirectWithBasePath(config, redirectUrl);\n } catch (error) {\n logger.error(\"[LOGOUT_CALLBACK_HANDLER] Logout callback error:\", error);\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n return redirectWithBasePath(\n config,\n CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n resolvedConfigs.logoutCallbackUrl,\n appUrl,\n ),\n );\n }\n}\n\n/**\n * Creates an authentication handler for Next.js API routes\n *\n * Usage:\n * ```ts\n * // app/api/auth/[...civicauth]/route.ts\n * import { handler } from '@civic/auth/nextjs'\n * export const GET = handler({\n * // optional config overrides\n * })\n * export const POST = handler({\n * // optional config overrides\n * })\n * ```\n */\nexport const handler =\n (authConfig = {}) =>\n async (request: NextRequest): Promise<NextResponse> => {\n const config = resolveAuthConfig(authConfig);\n logger.debug(\"routeHandler: Auth route handler called\", config);\n try {\n const pathname = request.nextUrl.pathname;\n const pathSegments = pathname.split(\"/\");\n const lastSegment = pathSegments[pathSegments.length - 1];\n\n switch (lastSegment) {\n case \"challenge\":\n case \"login\":\n return await handleLogin(request, config);\n case \"callback\":\n return await handleCallback(request, config);\n case \"refresh\":\n return await handleRefresh(request, config);\n case \"logout\":\n return await handleLogout(request, config);\n case \"clearsession\":\n return await handleClearSession(request, config);\n case \"logoutcallback\":\n return await handleLogoutCallback(request, config);\n case \"user\":\n return await handleUser(request, config);\n default:\n throw new AuthError(`Invalid auth route: ${pathname}`, 404);\n }\n } catch (error) {\n logger.error(\"Auth handler error:\", error);\n\n const status = error instanceof AuthError ? error.status : 500;\n const message =\n error instanceof Error ? error.message : \"Authentication failed\";\n\n const response = NextResponse.json({ error: message }, { status });\n\n await clearAuthCookies(config);\n return response;\n }\n };\n\n/**\n * User endpoint - returns current user data as JSON\n * Uses CivicAuth.isLoggedIn() and getUser()\n */\nasync function handleUser(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n logger.debug(\n \"routeHandler: [USER_HANDLER] User endpoint called\",\n resolvedConfigs,\n );\n try {\n const { civicAuth } = createCivicAuth(request, resolvedConfigs);\n\n const isLoggedIn = await civicAuth.isLoggedIn();\n\n if (!isLoggedIn) {\n const statusCode = request.nextUrl.searchParams.get(\n \"optimisticRehydration\",\n )\n ? 202\n : 401;\n return NextResponse.json(\n { error: \"Not authenticated\" },\n { status: statusCode },\n );\n }\n\n const user = await civicAuth.getUser();\n\n return NextResponse.json({ user });\n } catch (error) {\n logger.error(\"[USER_HANDLER] User endpoint error:\", error);\n return NextResponse.json(\n { error: \"Internal server error\" },\n { status: 500 },\n );\n }\n}\n"]}
1
+ {"version":3,"file":"routeHandler.js","sourceRoot":"","sources":["../../src/nextjs/routeHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import { CivicAuth, type UrlDetectionRequest } from \"@civic/auth/server\";\nimport { LOGOUT_SUCCESS_TEXT } from \"@/constants.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { displayModeFromState } from \"@/lib/oauth.js\";\nimport type { AuthConfig } from \"@/nextjs/config.js\";\nimport { resolveAuthConfig } from \"@/nextjs/config.js\";\nimport { clearAuthCookies, NextjsCookieStorage } from \"@/nextjs/cookies.js\";\nimport {\n AuthFlowCookie,\n CodeVerifier,\n UserStorage,\n} from \"@/shared/lib/types.js\";\nimport { revalidatePath } from \"next/cache.js\";\nimport type { NextRequest } from \"next/server.js\";\nimport { NextResponse } from \"next/server.js\";\nimport {\n getOriginUrl,\n redirectWithBasePath,\n sanitizeReturnUrl,\n} from \"./utils.js\";\n\nconst logger = loggers.nextjs.handlers.auth;\n\nclass AuthError extends Error {\n constructor(\n message: string,\n public readonly status: number = 401,\n ) {\n super(message);\n this.name = \"AuthError\";\n }\n}\n\n/**\n * Helper to convert NextRequest to UrlDetectionRequest for framework-agnostic URL handling\n */\nconst toUrlDetectionRequest = (request: NextRequest): UrlDetectionRequest => ({\n url: request.url,\n headers: Object.fromEntries(request.headers.entries()),\n searchParams: {\n get: (name: string) => request.nextUrl.searchParams.get(name),\n },\n cookies: {\n get: (name: string) => request.cookies.get(name),\n },\n});\n\n/**\n * Helper to create CivicAuth instance for a request\n * Now handles appUrl detection for proxy environments\n */\nconst createCivicAuth = (request: NextRequest, config: AuthConfig) => {\n const resolvedConfig = resolveAuthConfig(config);\n const cookieStorage = new NextjsCookieStorage({\n ...resolvedConfig.cookies?.tokens,\n [UserStorage.USER]: resolvedConfig.cookies?.user,\n });\n\n // Convert to framework-agnostic request format\n const urlDetectionRequest = toUrlDetectionRequest(request);\n\n // Get appUrl from client (for proxy environments)\n const clientAppUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n\n // Use baseUrl from config, then client appUrl, then request origin\n // This matches the main branch priority: config > client > request\n const appUrl =\n resolvedConfig.baseUrl ||\n clientAppUrl ||\n new URL(urlDetectionRequest.url).origin;\n\n // Build absolute URLs using detected appUrl or request origin\n const absoluteCallbackUrl = resolvedConfig.callbackUrl.startsWith(\"http\")\n ? resolvedConfig.callbackUrl\n : CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n resolvedConfig.callbackUrl,\n appUrl,\n );\n const absoluteLogoutCallbackUrl = resolvedConfig.logoutCallbackUrl.startsWith(\n \"http\",\n )\n ? resolvedConfig.logoutCallbackUrl\n : CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n resolvedConfig.logoutCallbackUrl,\n appUrl,\n );\n\n const civicAuth = new CivicAuth(cookieStorage, {\n disableRefresh: resolvedConfig.disableRefresh,\n clientId: resolvedConfig.clientId,\n redirectUrl: absoluteCallbackUrl,\n oauthServer: resolvedConfig.oauthServer,\n postLogoutRedirectUrl: absoluteLogoutCallbackUrl,\n // Note: Do NOT use request.url here - during callback, that would be the callback URL itself,\n // causing an infinite redirect loop in iframe mode fallbacks.\n loginSuccessUrl: resolvedConfig.loginSuccessUrl,\n });\n\n return {\n civicAuth,\n cookieStorage,\n appUrl, // Return appUrl for use in other functions\n urlDetectionRequest, // Return for use in handlers\n };\n};\n\n/**\n * Injects loginSuccessUrl into an existing base64-encoded state string, or creates\n * a new state if none exists. This allows the deep link destination (computed by\n * middleware) to be passed through the OAuth flow via the existing loginSuccessUrl\n * mechanism.\n */\nfunction injectLoginSuccessUrlIntoState(\n frontendState: string | null,\n loginSuccessUrl: string,\n): string {\n let stateObj: Record<string, unknown> = {};\n\n if (frontendState) {\n try {\n // Decode the existing state\n const jsonString = atob(frontendState);\n stateObj = JSON.parse(jsonString);\n } catch (error) {\n logger.warn(\n \"[LOGIN_HANDLER] Failed to parse existing state, creating new state\",\n { error },\n );\n // Continue with empty stateObj - we'll still add loginSuccessUrl\n }\n }\n\n // Set loginSuccessUrl in state (overrides any existing value for fullUrl mode)\n stateObj.loginSuccessUrl = loginSuccessUrl;\n\n // Encode and return the state\n return btoa(JSON.stringify(stateObj));\n}\n\n/**\n * Login handler - backend OAuth login initiation endpoint\n * Uses CivicAuth.buildLoginUrl()\n */\nasync function handleLogin(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n try {\n let frontendState = request.nextUrl.searchParams.get(\"state\");\n\n // Store appUrl in cookie if provided as query parameter\n const appUrlFromQuery = request.nextUrl.searchParams.get(\"appUrl\");\n const cookieStorage = new NextjsCookieStorage(\n resolvedConfigs.cookies?.tokens ?? {},\n );\n\n if (appUrlFromQuery) {\n await cookieStorage.set(CodeVerifier.APP_URL, appUrlFromQuery);\n }\n\n // Read the deep link cookie (set by middleware with deepLinkHandling already applied)\n // and inject it into the OAuth state's loginSuccessUrl field. This ensures the\n // destination survives the OAuth flow even when cookies aren't available in the\n // callback (e.g., due to SameSite restrictions in Chromium iframes).\n // Note: We don't delete the cookie here because:\n // 1. The login handler may be called multiple times (iframe preload, mode switching)\n // 2. We only want to consume the cookie on successful auth (handled in callback)\n if (resolvedConfigs.deepLinkHandling !== \"disabled\") {\n const deepLinkDestination = await cookieStorage.get(\n AuthFlowCookie.RETURN_URL,\n );\n if (deepLinkDestination) {\n // Re-validate the cookie value to guard against tampering (defense-in-depth)\n const originUrl = getOriginUrl(request, resolvedConfigs);\n const sanitized = sanitizeReturnUrl(deepLinkDestination, originUrl);\n\n if (sanitized) {\n // Inject the destination into state.loginSuccessUrl\n // Don't prepend basePath here - the callback handler's redirectWithBasePath will handle it\n logger.debug(\n \"[LOGIN_HANDLER] Found deep link cookie, injecting into state.loginSuccessUrl\",\n { deepLinkDestination, sanitized },\n );\n frontendState = injectLoginSuccessUrlIntoState(\n frontendState,\n sanitized,\n );\n } else {\n logger.warn(\n \"[LOGIN_HANDLER] Rejected invalid deep link cookie value\",\n { deepLinkDestination },\n );\n }\n }\n }\n\n const { civicAuth } = createCivicAuth(request, resolvedConfigs);\n\n const url = await civicAuth.buildLoginUrl({\n state: frontendState || undefined,\n });\n\n logger.info(\"[LOGIN_HANDLER] Redirecting to OAuth login URL\", {\n loginUrl: url.toString(),\n });\n\n return NextResponse.redirect(url.toString());\n } catch (error) {\n logger.error(\"[LOGIN_HANDLER] Backend login error:\", error);\n\n // Clean up deep link cookie on error to prevent stale cookies\n try {\n const errorCookieStorage = new NextjsCookieStorage(\n resolvedConfigs.cookies?.tokens ?? {},\n );\n await errorCookieStorage.delete(AuthFlowCookie.RETURN_URL);\n } catch (cookieError) {\n logger.warn(\n \"[LOGIN_HANDLER] Failed to clean up deep link cookie on error\",\n { cookieError },\n );\n }\n\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n return NextResponse.redirect(\n CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n \"/?error=login_failed\",\n appUrl,\n ),\n );\n }\n}\n\nasync function handleRefresh(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n try {\n const { civicAuth } = createCivicAuth(request, resolvedConfigs);\n\n await civicAuth.refreshTokens();\n\n logger.info(\"[REFRESH_HANDLER] Tokens refreshed successfully\");\n\n return NextResponse.json({\n status: \"success\",\n message: \"Tokens refreshed\",\n });\n } catch (error) {\n logger.error(\"[REFRESH_HANDLER] Token refresh error:\", error);\n return NextResponse.json(\n { error: \"Token refresh failed\" },\n { status: 500 },\n );\n }\n}\n\nasync function handleCallback(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n const code = request.nextUrl.searchParams.get(\"code\");\n const state = request.nextUrl.searchParams.get(\"state\");\n const error = request.nextUrl.searchParams.get(\"error\");\n\n if (error) {\n logger.error(\"OAuth error in callback:\", error);\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n return NextResponse.redirect(\n CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n \"/?error=oauth_error\",\n appUrl,\n ),\n );\n }\n\n if (!code || !state) throw new AuthError(\"Bad parameters\", 400);\n\n try {\n const { civicAuth, appUrl, urlDetectionRequest } = createCivicAuth(\n request,\n resolvedConfigs,\n );\n\n // Convert NextRequest to the format expected by handleCallback\n const handleCallbackRequest = {\n headers: Object.fromEntries(request.headers.entries()),\n url: request.url.toString(),\n };\n\n // Use CivicAuth's smart callback handler\n // Note: CivicAuth.handleCallback reads loginSuccessUrl from state (injected by login handler)\n // with fallback to config.loginSuccessUrl, so we don't need to pass frontendUrl option\n const result = await civicAuth.handleCallback({\n code,\n state,\n req: handleCallbackRequest,\n });\n\n // Fallback: If state was corrupted/lost and CivicAuth redirected to the default loginSuccessUrl,\n // check if we have a deep link cookie as a backup. This provides resilience against state loss.\n const cookieStorage = new NextjsCookieStorage(\n resolvedConfigs.cookies?.tokens ?? {},\n );\n if (resolvedConfigs.deepLinkHandling !== \"disabled\" && result.redirectTo) {\n const defaultLoginSuccessUrl = resolvedConfigs.loginSuccessUrl || \"/\";\n const deepLinkFromCookie = await cookieStorage.get(\n AuthFlowCookie.RETURN_URL,\n );\n\n // If redirecting to default and we have a cookie, use the cookie value\n if (deepLinkFromCookie && result.redirectTo === defaultLoginSuccessUrl) {\n // Re-validate the cookie value to guard against tampering (defense-in-depth)\n const originUrl = getOriginUrl(request, resolvedConfigs);\n const sanitized = sanitizeReturnUrl(deepLinkFromCookie, originUrl);\n\n if (sanitized) {\n // Don't prepend basePath here - redirectWithBasePath will handle it\n logger.debug(\n \"[CALLBACK_HANDLER] State missing loginSuccessUrl, using cookie fallback\",\n { deepLinkFromCookie, sanitized },\n );\n result.redirectTo = sanitized;\n } else {\n logger.warn(\n \"[CALLBACK_HANDLER] Rejected invalid fallback cookie value\",\n { deepLinkFromCookie },\n );\n // Keep the default loginSuccessUrl\n }\n }\n }\n\n // Helper to clear the deep link cookie on successful auth\n // Always clear the cookie to handle stale cookies from previous sessions\n const clearDeepLinkCookie = (response: NextResponse) => {\n const cookieConfig =\n resolvedConfigs.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];\n response.cookies.set(AuthFlowCookie.RETURN_URL, \"\", {\n path: cookieConfig?.path ?? resolvedConfigs.basePath ?? \"/\",\n httpOnly: cookieConfig?.httpOnly ?? true,\n secure: cookieConfig?.secure ?? true,\n sameSite: cookieConfig?.sameSite ?? \"strict\",\n maxAge: 0, // Immediately expire the cookie\n });\n logger.debug(\n \"[CALLBACK_HANDLER] Clearing deep link cookie after successful auth\",\n );\n return response;\n };\n\n if (result.redirectTo) {\n const response = redirectWithBasePath(\n config,\n CivicAuth.toAbsoluteUrl(urlDetectionRequest, result.redirectTo, appUrl),\n );\n return clearDeepLinkCookie(response);\n }\n\n if (result.content) {\n // Handle both string content and object content\n if (typeof result.content === \"string\") {\n const response = new NextResponse(result.content, {\n status: 200,\n headers: { \"Content-Type\": \"text/html\" },\n });\n return clearDeepLinkCookie(response);\n } else {\n // Object content (JSON response)\n return NextResponse.json(result.content);\n }\n }\n\n // Fallback redirect\n const response = NextResponse.redirect(\n CivicAuth.toAbsoluteUrl(urlDetectionRequest, \"/\", appUrl),\n );\n return clearDeepLinkCookie(response);\n } catch (error) {\n logger.error(\"[CALLBACK_HANDLER] OAuth callback error:\", error);\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n return NextResponse.redirect(\n CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n \"/?error=callback_failed\",\n appUrl,\n ),\n );\n }\n}\n\nconst revalidateUrlPath = async (url: string) => {\n try {\n const path = new URL(url).pathname;\n revalidatePath(path);\n } catch (error) {\n logger.warn(\"Failed to revalidate path after logout:\", error);\n }\n};\n\nexport async function handleLogout(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n // Get framework-agnostic request for URL utilities\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n\n // Read the state from the query parameters\n const state = request.nextUrl.searchParams.get(\"state\");\n const clientLogoutRedirectUrl =\n request.nextUrl.searchParams.get(\"logoutRedirectUrl\");\n\n try {\n logger.info(\"[LOGOUT_HANDLER] Backend logout endpoint called\");\n\n // If client provided a logoutRedirectUrl, override the config\n let configToUse = resolvedConfigs;\n if (clientLogoutRedirectUrl) {\n configToUse = {\n ...resolvedConfigs,\n logoutCallbackUrl: clientLogoutRedirectUrl,\n };\n logger.info(\"[LOGOUT_HANDLER] Overriding logout callback URL\", {\n original: resolvedConfigs.logoutCallbackUrl,\n override: clientLogoutRedirectUrl,\n });\n }\n\n const { civicAuth } = createCivicAuth(request, configToUse);\n\n // Always redirect to OAuth logout (like main branch)\n // Don't validate session - even invalid local sessions should hit OAuth logout\n logger.info(\"[LOGOUT_HANDLER] Processing logout request\", {\n state: !!state,\n });\n\n // Always redirect to OAuth logout endpoint (like main branch)\n // Client-side iframe logic will handle completion and redirect appropriately\n const logoutUrl = await civicAuth.buildLogoutRedirectUrl({\n state: state || undefined,\n });\n\n try {\n await clearAuthCookies(resolvedConfigs);\n } catch (error) {\n logger.error(\"[LOGOUT_HANDLER] Error clearing tokens:\", error);\n }\n\n // Remove state parameter from logout URL to prevent it from appearing in frontend URL\n const cleanLogoutUrl = new URL(logoutUrl);\n cleanLogoutUrl.searchParams.delete(\"state\");\n\n logger.info(\"[LOGOUT_HANDLER] Redirecting to OAuth logout endpoint\", {\n logoutUrl: cleanLogoutUrl.toString(),\n });\n\n return NextResponse.redirect(cleanLogoutUrl.toString());\n } catch (error) {\n logger.error(\"[LOGOUT_HANDLER] Logout error:\", error);\n // If logout URL generation fails, clear tokens and redirect to home\n await clearAuthCookies(resolvedConfigs);\n\n const fallbackUrl =\n clientLogoutRedirectUrl || resolvedConfigs.logoutCallbackUrl;\n const finalFallbackUrl = CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n fallbackUrl,\n appUrl,\n );\n\n return NextResponse.redirect(finalFallbackUrl);\n }\n}\n\n/**\n * Clear session handler - clears all auth cookies server-side.\n * Called by client in parallel with logout iframe to quickly clear HttpOnly cookies.\n *\n * This is part of a parallel logout strategy:\n * - Client calls clearsession (this endpoint) AND loads logout iframe simultaneously\n * - Both requests are sent while cookies are still present\n * - clearsession clears HttpOnly cookies quickly (this endpoint)\n * - logout iframe handles OAuth provider logout (slower, but had cookies when initiated)\n *\n * This ensures cookies are cleared fast (preventing race conditions if user refreshes)\n * while still performing OAuth provider logout.\n */\nasync function handleClearSession(\n _request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n try {\n logger.info(\"[CLEARSESSION_HANDLER] Clearing session cookies\");\n\n // Clear all auth cookies immediately\n await clearAuthCookies(resolvedConfigs);\n\n logger.info(\"[CLEARSESSION_HANDLER] Session cleared successfully\");\n\n return NextResponse.json({\n status: \"success\",\n message: \"Session cleared\",\n });\n } catch (error) {\n logger.error(\"[CLEARSESSION_HANDLER] Error clearing session:\", error);\n return NextResponse.json(\n { error: \"Failed to clear session\" },\n { status: 500 },\n );\n }\n}\n\nexport async function handleLogoutCallback(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n\n try {\n logger.info(\n \"[LOGOUT_CALLBACK_HANDLER] Backend logout callback endpoint called\",\n );\n\n // Clear authentication cookies\n await clearAuthCookies(resolvedConfigs);\n\n // Get framework-agnostic request and create CivicAuth instance\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const { civicAuth } = createCivicAuth(request, resolvedConfigs);\n\n // Get the state parameter for iframe detection\n const state = request.nextUrl.searchParams.get(\"state\");\n\n // If this is an iframe request, return HTML with logout success signal\n if (state && displayModeFromState(state, \"iframe\") === \"iframe\") {\n // For iframe mode, include the post-logout redirect URL in the HTML\n const postLogoutRedirectUrl =\n civicAuth.getPostLogoutRedirectUrl(urlDetectionRequest);\n const response = new NextResponse(\n `<html lang=\"en\"><span style=\"display:none\">${LOGOUT_SUCCESS_TEXT}<a href=\"${postLogoutRedirectUrl}\" rel=\"civic-auth-post-logout-redirect-url\"></a></span></html>`,\n );\n response.headers.set(\"Content-Type\", \"text/html; charset=utf-8\");\n logger.info(\n \"[LOGOUT_CALLBACK_HANDLER] Returning iframe logout success HTML\",\n { postLogoutRedirectUrl },\n );\n return response;\n }\n\n // For non-iframe requests, redirect to the logout callback URL or post-logout URL\n const redirectUrl = civicAuth.getPostLogoutRedirectUrl(urlDetectionRequest);\n logger.info(\n \"[LOGOUT_CALLBACK_HANDLER] Redirecting to logout callback URL\",\n {\n logoutCallbackUrl: resolvedConfigs.logoutCallbackUrl,\n redirectUrl,\n },\n );\n\n // Revalidate the redirect path to update authentication state in server components\n await revalidateUrlPath(redirectUrl);\n return redirectWithBasePath(config, redirectUrl);\n } catch (error) {\n logger.error(\"[LOGOUT_CALLBACK_HANDLER] Logout callback error:\", error);\n const urlDetectionRequest = toUrlDetectionRequest(request);\n const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);\n return redirectWithBasePath(\n config,\n CivicAuth.toAbsoluteUrl(\n urlDetectionRequest,\n resolvedConfigs.logoutCallbackUrl,\n appUrl,\n ),\n );\n }\n}\n\n/**\n * Creates an authentication handler for Next.js API routes\n *\n * Usage:\n * ```ts\n * // app/api/auth/[...civicauth]/route.ts\n * import { handler } from '@civic/auth/nextjs'\n * export const GET = handler({\n * // optional config overrides\n * })\n * export const POST = handler({\n * // optional config overrides\n * })\n * ```\n */\nexport const handler =\n (authConfig = {}) =>\n async (request: NextRequest): Promise<NextResponse> => {\n const config = resolveAuthConfig(authConfig);\n logger.debug(\"routeHandler: Auth route handler called\", config);\n try {\n const pathname = request.nextUrl.pathname;\n const pathSegments = pathname.split(\"/\");\n const lastSegment = pathSegments[pathSegments.length - 1];\n\n switch (lastSegment) {\n case \"challenge\":\n case \"login\":\n return await handleLogin(request, config);\n case \"callback\":\n return await handleCallback(request, config);\n case \"refresh\":\n return await handleRefresh(request, config);\n case \"logout\":\n return await handleLogout(request, config);\n case \"clearsession\":\n return await handleClearSession(request, config);\n case \"logoutcallback\":\n return await handleLogoutCallback(request, config);\n case \"user\":\n return await handleUser(request, config);\n default:\n throw new AuthError(`Invalid auth route: ${pathname}`, 404);\n }\n } catch (error) {\n logger.error(\"Auth handler error:\", error);\n\n const status = error instanceof AuthError ? error.status : 500;\n const message =\n error instanceof Error ? error.message : \"Authentication failed\";\n\n const response = NextResponse.json({ error: message }, { status });\n\n await clearAuthCookies(config);\n return response;\n }\n };\n\n/**\n * User endpoint - returns current user data as JSON\n * Uses CivicAuth.isLoggedIn() and getUser()\n */\nasync function handleUser(\n request: NextRequest,\n config: AuthConfig,\n): Promise<NextResponse> {\n const resolvedConfigs = resolveAuthConfig(config);\n logger.debug(\n \"routeHandler: [USER_HANDLER] User endpoint called\",\n resolvedConfigs,\n );\n try {\n const { civicAuth } = createCivicAuth(request, resolvedConfigs);\n\n const isLoggedIn = await civicAuth.isLoggedIn();\n\n if (!isLoggedIn) {\n const statusCode = request.nextUrl.searchParams.get(\n \"optimisticRehydration\",\n )\n ? 202\n : 401;\n return NextResponse.json(\n { error: \"Not authenticated\" },\n { status: statusCode },\n );\n }\n\n const user = await civicAuth.getUser();\n\n return NextResponse.json({ user });\n } catch (error) {\n logger.error(\"[USER_HANDLER] User endpoint error:\", error);\n return NextResponse.json(\n { error: \"Internal server error\" },\n { status: 500 },\n );\n }\n}\n"]}
@@ -3,6 +3,8 @@ import type { NextRequest } from "next/server.js";
3
3
  import { NextResponse } from "next/server.js";
4
4
  import type { SessionData } from "../types.js";
5
5
  import { type CookieConfig, type KeySetter } from "../shared/lib/types.js";
6
+ import { sanitizeReturnUrl } from "../shared/lib/util.js";
7
+ export { sanitizeReturnUrl };
6
8
  import { CookieStorage } from "../server/index.js";
7
9
  export declare const resolveCallbackUrl: (config: AuthConfigWithDefaults, baseUrl?: string) => string;
8
10
  export declare function sanitizeBasePath(path: string): string;
@@ -11,6 +13,7 @@ export declare function sanitizeBasePath(path: string): string;
11
13
  * This is the inverse operation of adding basePath to a URL
12
14
  */
13
15
  export declare function removeBasePathFromPath(pathname: string, basePath?: string): string;
16
+ export declare const getOriginUrl: (request: NextRequest, authConfig: AuthConfigWithDefaults) => string;
14
17
  /**
15
18
  * Determines if we should attempt token refresh based on session state
16
19
  */
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/nextjs/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,uBAAuB,CAAC;AAM/B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKlD,eAAO,MAAM,kBAAkB,WACrB,sBAAsB,YACpB,MAAM,KACf,MAGF,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAoBR;AA+BD;;GAEG;AACH,eAAO,MAAM,oBAAoB,YAAa,WAAW,KAAG,OAE3D,CAAC;AAKF;;GAEG;AACH,eAAO,MAAM,2BAA2B,aAC5B,MAAM,cACJ,sBAAsB,KACjC,OAyBF,CAAC;AAEF;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,aAAa;IAErD,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACvD,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;gBAFT,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,YAAK,EACpD,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY;IAQ1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwBxC,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,oBAAoB,EAAE,YAAY,GACjC,OAAO,CAAC,IAAI,CAAC;IAkBV,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAczC;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,aACf,MAAM,WACP,WAAW,cACR,sBAAsB,KACjC,IAWF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,8BAA8B,aAC/B,MAAM,cACJ,sBAAsB,KACjC,OAiBF,CAAC;AAEF,eAAO,MAAM,gBAAgB,mBACX,YAAY,cAChB,YAAY,GAAG,WAAW,SAoBvC,CAAC;AACF;;GAEG;AACH,eAAO,MAAM,yBAAyB,YAC3B,WAAW,WACX,WAAW,YACV,YAAY,WACb,6BAA6B,cAC1B,sBAAsB,KACjC,OAAO,CAAC,YAAY,GAAG,SAAS,CAuDlC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,QAAS,MAAM,YAAY,MAAM,WAiB5D,CAAC;AAEF,eAAO,MAAM,oBAAoB,WACvB,UAAU,aACP,MAAM,KAChB,YACuE,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/nextjs/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAGL,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKlD,eAAO,MAAM,kBAAkB,WACrB,sBAAsB,YACpB,MAAM,KACf,MAGF,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAoBR;AAED,eAAO,MAAM,YAAY,YACd,WAAW,cACR,sBAAsB,KACjC,MAQF,CAAC;AAkBF;;GAEG;AACH,eAAO,MAAM,oBAAoB,YAAa,WAAW,KAAG,OAE3D,CAAC;AAKF;;GAEG;AACH,eAAO,MAAM,2BAA2B,aAC5B,MAAM,cACJ,sBAAsB,KACjC,OAyBF,CAAC;AAEF;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,aAAa;IAErD,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACvD,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;gBAFT,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,YAAK,EACpD,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY;IAQ1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwBxC,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,oBAAoB,EAAE,YAAY,GACjC,OAAO,CAAC,IAAI,CAAC;IAkBV,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAczC;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,aACf,MAAM,WACP,WAAW,cACR,sBAAsB,KACjC,IAWF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,8BAA8B,aAC/B,MAAM,cACJ,sBAAsB,KACjC,OAiBF,CAAC;AAEF,eAAO,MAAM,gBAAgB,mBACX,YAAY,cAChB,YAAY,GAAG,WAAW,SAoBvC,CAAC;AACF;;GAEG;AACH,eAAO,MAAM,yBAAyB,YAC3B,WAAW,WACX,WAAW,YACV,YAAY,WACb,6BAA6B,cAC1B,sBAAsB,KACjC,OAAO,CAAC,YAAY,GAAG,SAAS,CAiGlC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,QAAS,MAAM,YAAY,MAAM,WAiB5D,CAAC;AAEF,eAAO,MAAM,oBAAoB,WACvB,UAAU,aACP,MAAM,KAChB,YACuE,CAAC"}
@@ -4,6 +4,8 @@ import { loggers } from "../lib/logger.js";
4
4
  import picomatch from "picomatch";
5
5
  import { AuthFlowCookie, CodeVerifier, OAuthTokenTypes, UserStorage, } from "../shared/lib/types.js";
6
6
  import { clearTokens, getCookieConfiguration, sanitizeReturnUrl, } from "../shared/lib/util.js";
7
+ // Re-export for use by routeHandler
8
+ export { sanitizeReturnUrl };
7
9
  import { CookieStorage } from "../server/index.js";
8
10
  import { extractCookieFromRawHeader } from "../shared/lib/cookieUtils.js";
9
11
  const logger = loggers.nextjs.middleware;
@@ -42,7 +44,7 @@ export function removeBasePathFromPath(pathname, basePath) {
42
44
  // If basePath doesn't match as a complete path segment, return pathname as-is
43
45
  return pathname;
44
46
  }
45
- const getOriginUrl = (request, authConfig) => {
47
+ export const getOriginUrl = (request, authConfig) => {
46
48
  // Use configured baseUrl if provided (for reverse proxy scenarios)
47
49
  if (authConfig.baseUrl) {
48
50
  return authConfig.baseUrl;
@@ -148,7 +150,7 @@ export class NextjsMiddlewareCookieStorage extends CookieStorage {
148
150
  // If we have a path configured, use it when deleting the cookie
149
151
  if (cookieSettings.path) {
150
152
  this.response.cookies.set(key, "", {
151
- expires: new Date(0), // Expire in the past
153
+ maxAge: 0, // Immediately expire the cookie
152
154
  path: cookieSettings.path,
153
155
  });
154
156
  }
@@ -226,6 +228,7 @@ export const handleUnauthenticatedUser = async (session, request, response, stor
226
228
  if (request.nextUrl.pathname !== loginPathWithoutBasePath) {
227
229
  logger.debug(`→ No valid tokens found - redirecting to login "${redirectUrl}"`);
228
230
  // Preserve the original URL as a deep link for post-authentication redirect
231
+ // Apply deepLinkHandling logic here to compute the final redirect URL
229
232
  if (authConfig.deepLinkHandling !== "disabled") {
230
233
  const originUrl = getOriginUrl(request, authConfig);
231
234
  // Get the full path including query string
@@ -235,8 +238,41 @@ export const handleUnauthenticatedUser = async (session, request, response, stor
235
238
  // Sanitize the return URL to prevent open redirect vulnerabilities
236
239
  const sanitizedReturnUrl = sanitizeReturnUrl(fullPath, originUrl);
237
240
  if (sanitizedReturnUrl) {
238
- logger.debug(`→ Preserving deep link: "${sanitizedReturnUrl}" for post-auth redirect`);
239
- await storage.set(AuthFlowCookie.RETURN_URL, sanitizedReturnUrl, {});
241
+ let returnTo;
242
+ if (authConfig.deepLinkHandling === "queryParamsOnly") {
243
+ // queryParamsOnly: Merge query params from original URL into loginSuccessUrl
244
+ const loginSuccessUrl = authConfig.loginSuccessUrl || "/";
245
+ try {
246
+ const returnUrlObj = new URL(sanitizedReturnUrl, originUrl);
247
+ // If no query params, use loginSuccessUrl directly
248
+ if (returnUrlObj.searchParams.size === 0) {
249
+ logger.debug(`→ deepLinkHandling=queryParamsOnly: no query params to preserve, using loginSuccessUrl "${loginSuccessUrl}"`);
250
+ returnTo = loginSuccessUrl;
251
+ }
252
+ else {
253
+ const baseUrlObj = new URL(loginSuccessUrl, originUrl);
254
+ // Append query params from original URL to loginSuccessUrl
255
+ returnUrlObj.searchParams.forEach((value, key) => {
256
+ baseUrlObj.searchParams.set(key, value);
257
+ });
258
+ returnTo =
259
+ baseUrlObj.pathname + baseUrlObj.search + baseUrlObj.hash;
260
+ logger.debug(`→ deepLinkHandling=queryParamsOnly: merged params into "${returnTo}"`);
261
+ }
262
+ }
263
+ catch {
264
+ // If URL parsing fails, fall back to loginSuccessUrl
265
+ logger.warn(`→ Failed to merge query params, falling back to loginSuccessUrl`);
266
+ returnTo = loginSuccessUrl;
267
+ }
268
+ }
269
+ else {
270
+ // fullUrl: Use the full path directly
271
+ returnTo = sanitizedReturnUrl;
272
+ logger.debug(`→ deepLinkHandling=fullUrl: preserving deep link "${returnTo}"`);
273
+ }
274
+ // Set the cookie with the computed return URL
275
+ await storage.set(AuthFlowCookie.RETURN_URL, returnTo, {});
240
276
  }
241
277
  }
242
278
  const redirectedResponse = redirectWithBasePath(authConfig, redirectUrl);