@civic/auth 0.13.0-beta.0 → 0.13.0-beta.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,6 @@
1
- # 0.13.0 - Support deep-linking
2
- - support deep-linking with RETURN_URL cookie mechanism
1
+ # 0.13.0 - NextJS support for deep-linking
2
+ - support NextJS deep-linking by setting a return_to cookie and propagating it via state through the OAuth flow
3
+ - add `deepLinkHandling` configuration option with three modes: `"fullUrl"`, `"queryParamsOnly"`, `"disabled"`
3
4
 
4
5
  # 0.12.0 - Remove unused redirect parameters from NextJS provider interface
5
6
  - Remove unused redirectOnLogin and redirectOnLogout parameters
@@ -175,129 +175,6 @@ export declare const resolveAuthConfig: (config?: Partial<AuthConfig>) => AuthCo
175
175
  * The plugin sets internal environment variables that are used by
176
176
  * the auth system. These variables should not be set manually.
177
177
  */
178
- export declare const createCivicAuthPlugin: (authConfig: AuthConfig) => (nextConfig?: NextConfig) => {
179
- env: {
180
- _civic_auth_client_id: string;
181
- _civic_oauth_server: string;
182
- _civic_auth_callback_url: string;
183
- _civic_auth_login_success_url: string | undefined;
184
- _civic_auth_challenge_url: string;
185
- _civic_auth_login_url: string;
186
- _civic_auth_login_init_url: string;
187
- _civic_auth_logout_url: string;
188
- _civic_auth_logout_callback_url: string;
189
- _civic_auth_refresh_url: string;
190
- _civic_auth_user_url: string;
191
- _civic_auth_clear_session_url: string;
192
- _civic_auth_includes: string;
193
- _civic_auth_excludes: string;
194
- _civic_auth_cookie_config: string;
195
- _civic_auth_base_path: string | undefined;
196
- _civic_auth_base_url: string | undefined;
197
- _civic_auth_auto_redirect: string;
198
- _civic_auth_disable_refresh: string | undefined;
199
- _civic_auth_deep_link_handling: DeepLinkHandling;
200
- };
201
- allowedDevOrigins?: string[];
202
- exportPathMap?: (defaultMap: import("next/dist/server/config-shared.js").ExportPathMap, ctx: {
203
- dev: boolean;
204
- dir: string;
205
- outDir: string | null;
206
- distDir: string;
207
- buildId: string;
208
- }) => Promise<import("next/dist/server/config-shared.js").ExportPathMap> | import("next/dist/server/config-shared.js").ExportPathMap;
209
- i18n?: import("next/dist/server/config-shared.js").I18NConfig | null;
210
- eslint?: import("next/dist/server/config-shared.js").ESLintConfig;
211
- typescript?: import("next/dist/server/config-shared.js").TypeScriptConfig;
212
- headers?: () => Promise<import("next/dist/lib/load-custom-routes.js").Header[]>;
213
- rewrites?: () => Promise<import("next/dist/lib/load-custom-routes.js").Rewrite[] | {
214
- beforeFiles: import("next/dist/lib/load-custom-routes.js").Rewrite[];
215
- afterFiles: import("next/dist/lib/load-custom-routes.js").Rewrite[];
216
- fallback: import("next/dist/lib/load-custom-routes.js").Rewrite[];
217
- }>;
218
- redirects?: () => Promise<import("next/dist/lib/load-custom-routes.js").Redirect[]>;
219
- excludeDefaultMomentLocales?: boolean;
220
- webpack?: import("next/dist/server/config-shared.js").NextJsWebpackConfig | null;
221
- trailingSlash?: boolean;
222
- distDir?: string;
223
- cleanDistDir?: boolean;
224
- assetPrefix?: string;
225
- cacheHandler?: string | undefined;
226
- cacheMaxMemorySize?: number;
227
- useFileSystemPublicRoutes?: boolean;
228
- generateBuildId?: () => string | null | Promise<string | null>;
229
- generateEtags?: boolean;
230
- pageExtensions?: string[];
231
- compress?: boolean;
232
- analyticsId?: string;
233
- poweredByHeader?: boolean;
234
- images?: import("next/dist/shared/lib/image-config.js").ImageConfig;
235
- devIndicators?: {
236
- buildActivity?: boolean;
237
- buildActivityPosition?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
238
- };
239
- onDemandEntries?: {
240
- maxInactiveAge?: number;
241
- pagesBufferLength?: number;
242
- };
243
- amp?: {
244
- canonicalBase?: string;
245
- };
246
- deploymentId?: string;
247
- basePath?: string;
248
- sassOptions?: {
249
- [key: string]: any;
250
- };
251
- productionBrowserSourceMaps?: boolean;
252
- optimizeFonts?: boolean;
253
- reactProductionProfiling?: boolean;
254
- reactStrictMode?: boolean | null;
255
- publicRuntimeConfig?: {
256
- [key: string]: any;
257
- };
258
- serverRuntimeConfig?: {
259
- [key: string]: any;
260
- };
261
- httpAgentOptions?: {
262
- keepAlive?: boolean;
263
- };
264
- outputFileTracing?: boolean;
265
- staticPageGenerationTimeout?: number;
266
- crossOrigin?: "anonymous" | "use-credentials";
267
- swcMinify?: boolean;
268
- compiler?: {
269
- reactRemoveProperties?: boolean | {
270
- properties?: string[];
271
- };
272
- relay?: {
273
- src: string;
274
- artifactDirectory?: string;
275
- language?: "typescript" | "javascript" | "flow";
276
- eagerEsModules?: boolean;
277
- };
278
- removeConsole?: boolean | {
279
- exclude?: string[];
280
- };
281
- styledComponents?: boolean | import("next/dist/server/config-shared.js").StyledComponentsConfig;
282
- emotion?: boolean | import("next/dist/server/config-shared.js").EmotionConfig;
283
- styledJsx?: boolean | {
284
- useLightningcss?: boolean;
285
- };
286
- };
287
- output?: "standalone" | "export";
288
- transpilePackages?: string[];
289
- skipMiddlewareUrlNormalize?: boolean;
290
- skipTrailingSlashRedirect?: boolean;
291
- modularizeImports?: Record<string, {
292
- transform: string | Record<string, string>;
293
- preventFullImport?: boolean;
294
- skipDefaultConversion?: boolean;
295
- }>;
296
- logging?: {
297
- fetches?: {
298
- fullUrl?: boolean;
299
- };
300
- };
301
- experimental?: import("next/dist/server/config-shared.js").ExperimentalConfig;
302
- };
178
+ export type NextJSPluginFn = (nextConfig?: NextConfig) => NextConfig;
179
+ export declare const createCivicAuthPlugin: (authConfig: AuthConfig) => NextJSPluginFn;
303
180
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/nextjs/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/nextjs/config.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { loggers } from "../lib/logger.js";
2
2
  import { withoutUndefined } from "../utils.js";
3
- import {} from "../shared/lib/types.js";
3
+ import { AuthFlowCookie, } from "../shared/lib/types.js";
4
4
  import { DEFAULT_AUTH_SERVER } from "../constants.js";
5
5
  import { merge } from "ts-deepmerge";
6
6
  import { sanitizeBasePath } from "./utils.js";
@@ -31,6 +31,12 @@ const URL_CONFIG = {
31
31
  useBasePathOnly: true,
32
32
  checkEmpty: true,
33
33
  },
34
+ loginSuccessUrl: {
35
+ defaultPath: "",
36
+ applyBasePath: true,
37
+ useBasePathOnly: true,
38
+ checkEmpty: true,
39
+ },
34
40
  };
35
41
  /**
36
42
  * Default configuration values that will be used if not overridden
@@ -53,7 +59,7 @@ export const defaultAuthConfig = {
53
59
  baseUrl: undefined, // No default - will use request.nextUrl.origin when undefined
54
60
  autoRedirect: true, // Default to current behavior
55
61
  cookies: createNextJSCookieConfig(),
56
- deepLinkHandling: "fullUrl", // Default to full deep link preservation
62
+ deepLinkHandling: "queryParamsOnly", // Default to preserving query params while respecting loginSuccessUrl
57
63
  };
58
64
  /**
59
65
  * Resolves the authentication configuration by combining:
@@ -129,6 +135,19 @@ export const resolveAuthConfig = (config = {}) => {
129
135
  if (explicitCookies && mergedConfig.cookies) {
130
136
  mergedConfig.cookies = merge.withOptions({ mergeArrays: false }, mergedConfig.cookies, explicitCookies);
131
137
  }
138
+ // Security warnings for insecure cookie configurations
139
+ const returnUrlCookieConfig = mergedConfig.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];
140
+ if (returnUrlCookieConfig) {
141
+ if (returnUrlCookieConfig.httpOnly === false) {
142
+ logger.warn("SECURITY WARNING: RETURN_URL cookie httpOnly is disabled. This exposes the cookie to XSS attacks.");
143
+ }
144
+ if (returnUrlCookieConfig.secure === false) {
145
+ logger.warn("SECURITY WARNING: RETURN_URL cookie secure is disabled. This allows cookie theft over HTTP.");
146
+ }
147
+ if (returnUrlCookieConfig.sameSite === "none") {
148
+ logger.warn("SECURITY WARNING: RETURN_URL cookie sameSite is set to 'none'. This allows cross-site request access.");
149
+ }
150
+ }
132
151
  if (mergedConfig.clientId === undefined) {
133
152
  throw new Error("Civic Auth client ID is required");
134
153
  }
@@ -156,44 +175,6 @@ function applyBasePathToUrls(authConfig, basePath) {
156
175
  });
157
176
  return defaultUrlsWithBasePath;
158
177
  }
159
- /**
160
- * Creates a Next.js plugin that handles auth configuration.
161
- *
162
- * This is the main configuration point for the auth system.
163
- * Do not set _civic_auth_* environment variables directly - instead,
164
- * pass your configuration here.
165
- *
166
- * The only required field is clientId.
167
- *
168
- * Notes:
169
- * - If you provide explicit URLs, they will be used exactly as provided.
170
- * - Default URLs will automatically include the basePath from your Next.js config.
171
- *
172
- * @example
173
- * ```js
174
- * // next.config.js
175
- * export default createCivicAuthPlugin({
176
- * clientId: 'my-client-id',
177
- * });
178
- * ```
179
- *
180
- * @example
181
- * ```js
182
- * // next.config.js
183
- * export default createCivicAuthPlugin({
184
- * clientId: 'my-client-id',
185
- * callbackUrl: '/custom/callback',
186
- * loginUrl: '/custom/login',
187
- * logoutUrl: '/custom/logout',
188
- * logoutCallbackUrl: '/custom/logoutcallback',
189
- * include: ['/protected/*'],
190
- * exclude: ['/public/*']
191
- * })
192
- * ```
193
- *
194
- * The plugin sets internal environment variables that are used by
195
- * the auth system. These variables should not be set manually.
196
- */
197
178
  export const createCivicAuthPlugin = (authConfig) => {
198
179
  return (nextConfig) => {
199
180
  logger.debug("createCivicAuthPlugin nextConfig", JSON.stringify(nextConfig, null, 2));
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/nextjs/config.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable turbo/no-undeclared-env-vars */\nimport type { NextConfig } from \"next\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { withoutUndefined } from \"@/utils.js\";\nimport {\n type CookieConfig,\n type TokensCookieConfig,\n} from \"@/shared/lib/types.js\";\nimport { DEFAULT_AUTH_SERVER } from \"@/constants.js\";\nimport { merge } from \"ts-deepmerge\";\nimport { sanitizeBasePath } from \"./utils.js\";\nimport {\n createNextJSCookieConfig,\n type CookiesConfigObject,\n} from \"@/shared/lib/cookieConfig.js\";\n\n// Re-export the shared type for public API\nexport type { CookiesConfigObject };\n\nconst logger = loggers.nextjs.handlers.auth;\n\n/**\n * Controls how deep links (original URLs) are handled after authentication.\n *\n * - `\"fullUrl\"`: Redirect to the original URL the user tried to access.\n * `loginSuccessUrl` is used as fallback only when no deep link exists.\n * - `\"queryParamsOnly\"`: Redirect to `loginSuccessUrl`, but merge query params from original URL.\n * - `\"disabled\"`: No deep link preservation. Always use `loginSuccessUrl`.\n *\n * @default \"fullUrl\"\n */\nexport type DeepLinkHandling = \"fullUrl\" | \"queryParamsOnly\" | \"disabled\";\n\n/**\n * URL configuration mapping for applying basePath\n */\ntype UrlConfigEntry = {\n defaultPath: string;\n applyBasePath: boolean;\n useBasePathOnly?: boolean;\n checkEmpty?: boolean;\n};\n\nexport const systemUrlsConfig = {\n callbackUrl: { defaultPath: \"/api/auth/callback\", applyBasePath: true },\n challengeUrl: { defaultPath: \"/api/auth/challenge\", applyBasePath: true },\n refreshUrl: { defaultPath: \"/api/auth/refresh\", applyBasePath: true },\n logoutUrl: { defaultPath: \"/api/auth/logout\", applyBasePath: true },\n loginInitUrl: { defaultPath: \"/api/auth/login\", applyBasePath: true },\n userUrl: { defaultPath: \"/api/auth/user\", applyBasePath: true },\n clearSessionUrl: {\n defaultPath: \"/api/auth/clearsession\",\n applyBasePath: true,\n },\n};\nconst URL_CONFIG: Record<string, UrlConfigEntry> = {\n ...systemUrlsConfig,\n logoutCallbackUrl: {\n defaultPath: \"\",\n applyBasePath: true,\n useBasePathOnly: true,\n },\n loginUrl: {\n defaultPath: \"\",\n applyBasePath: true,\n useBasePathOnly: true,\n checkEmpty: true,\n },\n};\n\n/**\n * Configuration values for Civic Auth.\n * Only clientId is required, all others are optional.\n */\nexport type AuthConfigWithDefaults = {\n clientId: string;\n oauthServer: string;\n // Internal API routes\n callbackUrl: string; // where Civic's internal OAuth callback is hosted\n loginInitUrl: string; // Internal auth route for initiating login flow (e.g., /api/auth/login)\n logoutUrl: string;\n challengeUrl: string;\n refreshUrl: string;\n userUrl: string;\n clearSessionUrl: string;\n // User-facing routes\n loginSuccessUrl?: string; // where the user should be sent after the entire login completes, including the token exchange\n // The path the user will be sent to if they access a resource that needs\n // them to be logged in. If there is a dedicated login page, it can be set here.\n // Note: This is NOT for internal auth routes - see loginInitUrl for that.\n loginUrl: string;\n logoutCallbackUrl: string;\n include: string[];\n exclude: string[];\n cookies: CookiesConfigObject;\n basePath?: string;\n baseUrl?: string; // Public domain for apps behind reverse proxies (e.g., \"https://myapp.com\")\n autoRedirect: boolean;\n disableRefresh?: boolean; // Disable automatic token refresh\n targetContainerElement?: HTMLElement | string;\n /**\n * Controls how deep links (original URLs) are handled after authentication.\n * @see DeepLinkHandling\n * @default \"fullUrl\"\n */\n deepLinkHandling: DeepLinkHandling;\n};\n\n/**\n * All possible config values for Civic Auth\n */\nexport type OptionalAuthConfig = Partial<\n | AuthConfigWithDefaults\n | {\n cookies?: {\n tokens?: Partial<TokensCookieConfig>;\n user?: CookieConfig;\n };\n }\n> & {\n // Ensure TypeScript understands these properties are available\n // This doesn't change the public API, just helps TypeScript internally\n callbackUrl?: string; // where Civic's internal OAuth callback is hosted\n loginSuccessUrl?: string; // where the user should be sent after the entire login completes, including the token exchange\n loginUrl?: string;\n loginInitUrl?: string;\n logoutUrl?: string;\n logoutCallbackUrl?: string;\n challengeUrl?: string;\n refreshUrl?: string;\n userUrl?: string;\n clearSessionUrl?: string;\n include?: string[];\n exclude?: string[];\n basePath?: string;\n baseUrl?: string;\n oauthServer?: string;\n autoRedirect?: boolean;\n disableRefresh?: boolean;\n targetContainerElement?: HTMLElement | string;\n deepLinkHandling?: DeepLinkHandling;\n};\n\n/**\n * Configuration values for Civic Auth.\n * Only clientId is required, all others are optional.\n */\nexport type AuthConfig = OptionalAuthConfig & {\n clientId: string;\n exclude?: string[];\n};\n\n/**\n * Default configuration values that will be used if not overridden\n */\nexport const defaultAuthConfig: Omit<AuthConfigWithDefaults, \"clientId\"> = {\n oauthServer: DEFAULT_AUTH_SERVER,\n // Internal API routes\n // deconstruct systemUrls to set the keys and default values\n ...(Object.fromEntries(\n Object.entries(systemUrlsConfig).map(([key, value]) => [\n key,\n value.defaultPath,\n ]),\n ) as {\n [key in keyof typeof systemUrlsConfig]: string;\n }),\n // User-facing routes\n loginSuccessUrl: undefined, // By default, the user is sent to the redirect_url\n loginUrl: \"/\",\n logoutCallbackUrl: \"/\",\n include: [\"/**\"],\n exclude: [\"/api/auth/**\"],\n basePath: \"\",\n baseUrl: undefined, // No default - will use request.nextUrl.origin when undefined\n autoRedirect: true, // Default to current behavior\n cookies: createNextJSCookieConfig(),\n deepLinkHandling: \"fullUrl\", // Default to full deep link preservation\n};\n\n/**\n * Resolves the authentication configuration by combining:\n * 1. Default values\n * 2. Environment variables (set internally by the plugin)\n * 3. Explicitly passed configuration\n *\n * Config will be merged deeply, with arrays not merged, so that the\n * default include list (for example) [\"/*\"] will not be added\n *\n * Note: Developers should not set _civic_auth_* environment variables directly.\n * Instead, pass configuration to the createCivicAuthPlugin in next.config.js:\n *\n * @example\n * ```js\n * // next.config.js\n * export default createCivicAuthPlugin({\n * callbackUrl: '/custom/callback',\n * })\n * ```\n */\nexport const resolveAuthConfig = (\n config: Partial<AuthConfig> = {},\n): AuthConfigWithDefaults => {\n // Read configuration that was set by the plugin via environment variables\n // Don't load environment cookies if explicit cookies are provided\n const hasExplicitCookies = config.cookies;\n\n const configFromEnv = withoutUndefined({\n clientId: process.env._civic_auth_client_id,\n oauthServer: process.env._civic_oauth_server,\n callbackUrl: process.env._civic_auth_callback_url,\n loginSuccessUrl: process.env._civic_auth_login_success_url,\n challengeUrl: process.env._civic_auth_challenge_url,\n loginUrl: process.env._civic_auth_login_url,\n loginInitUrl: process.env._civic_auth_login_init_url,\n logoutUrl: process.env._civic_auth_logout_url,\n logoutCallbackUrl: process.env._civic_auth_logout_callback_url,\n refreshUrl: process.env._civic_auth_refresh_url,\n userUrl: process.env._civic_auth_user_url,\n clearSessionUrl: process.env._civic_auth_clear_session_url,\n include: process.env._civic_auth_includes?.split(\",\"),\n exclude: process.env._civic_auth_excludes?.split(\",\"),\n cookies: hasExplicitCookies\n ? undefined\n : process.env._civic_auth_cookie_config\n ? JSON.parse(process.env._civic_auth_cookie_config)\n : undefined,\n basePath: process.env._civic_auth_base_path || \"\",\n baseUrl: process.env._civic_auth_base_url,\n autoRedirect:\n process.env._civic_auth_auto_redirect === \"false\" ? false : undefined,\n disableRefresh:\n process.env._civic_auth_disable_refresh === \"true\" ? true : undefined,\n targetContainerElement: process.env._civic_auth_target_container_element,\n deepLinkHandling: ([\"fullUrl\", \"queryParamsOnly\", \"disabled\"].includes(\n process.env._civic_auth_deep_link_handling || \"\",\n )\n ? process.env._civic_auth_deep_link_handling\n : undefined) as DeepLinkHandling | undefined,\n }) as AuthConfig;\n\n // Ensure \"/api/auth/**\" is always excluded\n const finalExclude = new Set([\n ...defaultAuthConfig.exclude,\n ...(configFromEnv.exclude || []),\n ...(config.exclude ?? []),\n ]);\n\n // Store explicit cookies config before merge to preserve it\n const explicitCookies = config.cookies;\n\n // Perform a deep merge of the configurations\n const mergedConfig = merge.withOptions(\n { mergeArrays: false },\n defaultAuthConfig,\n configFromEnv,\n config,\n );\n\n // Override the exclude list with the ensured list\n mergedConfig.exclude = Array.from(finalExclude);\n\n // Update cookie configuration with basePath if it wasn't explicitly provided\n // Only auto-configure if no explicit cookies are provided anywhere\n if (!config.cookies && !configFromEnv.cookies && mergedConfig.basePath) {\n mergedConfig.cookies = createNextJSCookieConfig(mergedConfig.basePath);\n }\n\n // Merge explicit cookies with merged config to preserve both explicit settings and defaults\n if (explicitCookies && mergedConfig.cookies) {\n mergedConfig.cookies = merge.withOptions(\n { mergeArrays: false },\n mergedConfig.cookies,\n explicitCookies,\n );\n }\n\n if (mergedConfig.clientId === undefined) {\n throw new Error(\"Civic Auth client ID is required\");\n }\n\n return mergedConfig as AuthConfigWithDefaults & { clientId: string };\n};\n\n/**\n * Applies basePath to URLs based on URL_CONFIG rules\n */\nfunction applyBasePathToUrls(\n authConfig: AuthConfig,\n basePath: string,\n): Partial<AuthConfig> {\n const defaultUrlsWithBasePath: Record<string, string> = {};\n\n if (!basePath) return defaultUrlsWithBasePath;\n\n Object.entries(URL_CONFIG).forEach(([key, config]) => {\n // Skip if explicitly set in authConfig\n const currentValue = authConfig[key as keyof AuthConfig];\n if (\n currentValue !== undefined &&\n !(config.checkEmpty && currentValue === \"\")\n ) {\n return;\n }\n\n // Apply basePath based on config rules\n const urlValue = config.useBasePathOnly\n ? basePath\n : `${basePath}${config.defaultPath}`;\n\n defaultUrlsWithBasePath[key] = urlValue;\n });\n\n return defaultUrlsWithBasePath as Partial<AuthConfig>;\n}\n\n/**\n * Creates a Next.js plugin that handles auth configuration.\n *\n * This is the main configuration point for the auth system.\n * Do not set _civic_auth_* environment variables directly - instead,\n * pass your configuration here.\n *\n * The only required field is clientId.\n *\n * Notes:\n * - If you provide explicit URLs, they will be used exactly as provided.\n * - Default URLs will automatically include the basePath from your Next.js config.\n *\n * @example\n * ```js\n * // next.config.js\n * export default createCivicAuthPlugin({\n * clientId: 'my-client-id',\n * });\n * ```\n *\n * @example\n * ```js\n * // next.config.js\n * export default createCivicAuthPlugin({\n * clientId: 'my-client-id',\n * callbackUrl: '/custom/callback',\n * loginUrl: '/custom/login',\n * logoutUrl: '/custom/logout',\n * logoutCallbackUrl: '/custom/logoutcallback',\n * include: ['/protected/*'],\n * exclude: ['/public/*']\n * })\n * ```\n *\n * The plugin sets internal environment variables that are used by\n * the auth system. These variables should not be set manually.\n */\nexport const createCivicAuthPlugin = (authConfig: AuthConfig) => {\n return (nextConfig?: NextConfig) => {\n logger.debug(\n \"createCivicAuthPlugin nextConfig\",\n JSON.stringify(nextConfig, null, 2),\n );\n\n // Extract basePath from Next.js config\n const basePath = sanitizeBasePath(nextConfig?.basePath || \"\");\n\n // Apply basePath to URLs using the configuration mapping\n const defaultUrlsWithBasePath = applyBasePathToUrls(authConfig, basePath);\n\n // Create final config with basePath and possibly modified URLs\n const resolvedConfig = resolveAuthConfig({\n ...defaultUrlsWithBasePath,\n ...authConfig,\n basePath,\n });\n\n return {\n ...nextConfig,\n env: {\n ...nextConfig?.env,\n // Internal environment variables - do not set these manually\n _civic_auth_client_id: resolvedConfig.clientId,\n _civic_oauth_server: resolvedConfig.oauthServer,\n _civic_auth_callback_url: resolvedConfig.callbackUrl,\n _civic_auth_login_success_url: resolvedConfig.loginSuccessUrl,\n _civic_auth_challenge_url: resolvedConfig.challengeUrl,\n _civic_auth_login_url: resolvedConfig.loginUrl,\n _civic_auth_login_init_url: resolvedConfig.loginInitUrl,\n _civic_auth_logout_url: resolvedConfig.logoutUrl,\n _civic_auth_logout_callback_url: resolvedConfig.logoutCallbackUrl,\n _civic_auth_refresh_url: resolvedConfig.refreshUrl,\n _civic_auth_user_url: resolvedConfig.userUrl,\n _civic_auth_clear_session_url: resolvedConfig.clearSessionUrl,\n _civic_auth_includes: resolvedConfig.include.join(\",\"),\n _civic_auth_excludes: resolvedConfig.exclude.join(\",\"),\n _civic_auth_cookie_config: JSON.stringify(resolvedConfig.cookies),\n _civic_auth_base_path: resolvedConfig.basePath,\n _civic_auth_base_url: resolvedConfig.baseUrl,\n _civic_auth_auto_redirect: resolvedConfig.autoRedirect.toString(),\n _civic_auth_disable_refresh: resolvedConfig.disableRefresh?.toString(),\n _civic_auth_deep_link_handling: resolvedConfig.deepLinkHandling,\n },\n };\n };\n};\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/nextjs/config.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable turbo/no-undeclared-env-vars */\nimport type { NextConfig } from \"next\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { withoutUndefined } from \"@/utils.js\";\nimport {\n AuthFlowCookie,\n type CookieConfig,\n type TokensCookieConfig,\n} from \"@/shared/lib/types.js\";\nimport { DEFAULT_AUTH_SERVER } from \"@/constants.js\";\nimport { merge } from \"ts-deepmerge\";\nimport { sanitizeBasePath } from \"./utils.js\";\nimport {\n createNextJSCookieConfig,\n type CookiesConfigObject,\n} from \"@/shared/lib/cookieConfig.js\";\n\n// Re-export the shared type for public API\nexport type { CookiesConfigObject };\n\nconst logger = loggers.nextjs.handlers.auth;\n\n/**\n * Controls how deep links (original URLs) are handled after authentication.\n *\n * - `\"fullUrl\"`: Redirect to the original URL the user tried to access.\n * `loginSuccessUrl` is used as fallback only when no deep link exists.\n * - `\"queryParamsOnly\"`: Redirect to `loginSuccessUrl`, but merge query params from original URL.\n * - `\"disabled\"`: No deep link preservation. Always use `loginSuccessUrl`.\n *\n * @default \"fullUrl\"\n */\nexport type DeepLinkHandling = \"fullUrl\" | \"queryParamsOnly\" | \"disabled\";\n\n/**\n * URL configuration mapping for applying basePath\n */\ntype UrlConfigEntry = {\n defaultPath: string;\n applyBasePath: boolean;\n useBasePathOnly?: boolean;\n checkEmpty?: boolean;\n};\n\nexport const systemUrlsConfig = {\n callbackUrl: { defaultPath: \"/api/auth/callback\", applyBasePath: true },\n challengeUrl: { defaultPath: \"/api/auth/challenge\", applyBasePath: true },\n refreshUrl: { defaultPath: \"/api/auth/refresh\", applyBasePath: true },\n logoutUrl: { defaultPath: \"/api/auth/logout\", applyBasePath: true },\n loginInitUrl: { defaultPath: \"/api/auth/login\", applyBasePath: true },\n userUrl: { defaultPath: \"/api/auth/user\", applyBasePath: true },\n clearSessionUrl: {\n defaultPath: \"/api/auth/clearsession\",\n applyBasePath: true,\n },\n};\nconst URL_CONFIG: Record<string, UrlConfigEntry> = {\n ...systemUrlsConfig,\n logoutCallbackUrl: {\n defaultPath: \"\",\n applyBasePath: true,\n useBasePathOnly: true,\n },\n loginUrl: {\n defaultPath: \"\",\n applyBasePath: true,\n useBasePathOnly: true,\n checkEmpty: true,\n },\n loginSuccessUrl: {\n defaultPath: \"\",\n applyBasePath: true,\n useBasePathOnly: true,\n checkEmpty: true,\n },\n};\n\n/**\n * Configuration values for Civic Auth.\n * Only clientId is required, all others are optional.\n */\nexport type AuthConfigWithDefaults = {\n clientId: string;\n oauthServer: string;\n // Internal API routes\n callbackUrl: string; // where Civic's internal OAuth callback is hosted\n loginInitUrl: string; // Internal auth route for initiating login flow (e.g., /api/auth/login)\n logoutUrl: string;\n challengeUrl: string;\n refreshUrl: string;\n userUrl: string;\n clearSessionUrl: string;\n // User-facing routes\n loginSuccessUrl?: string; // where the user should be sent after the entire login completes, including the token exchange\n // The path the user will be sent to if they access a resource that needs\n // them to be logged in. If there is a dedicated login page, it can be set here.\n // Note: This is NOT for internal auth routes - see loginInitUrl for that.\n loginUrl: string;\n logoutCallbackUrl: string;\n include: string[];\n exclude: string[];\n cookies: CookiesConfigObject;\n basePath?: string;\n baseUrl?: string; // Public domain for apps behind reverse proxies (e.g., \"https://myapp.com\")\n autoRedirect: boolean;\n disableRefresh?: boolean; // Disable automatic token refresh\n targetContainerElement?: HTMLElement | string;\n /**\n * Controls how deep links (original URLs) are handled after authentication.\n * @see DeepLinkHandling\n * @default \"fullUrl\"\n */\n deepLinkHandling: DeepLinkHandling;\n};\n\n/**\n * All possible config values for Civic Auth\n */\nexport type OptionalAuthConfig = Partial<\n | AuthConfigWithDefaults\n | {\n cookies?: {\n tokens?: Partial<TokensCookieConfig>;\n user?: CookieConfig;\n };\n }\n> & {\n // Ensure TypeScript understands these properties are available\n // This doesn't change the public API, just helps TypeScript internally\n callbackUrl?: string; // where Civic's internal OAuth callback is hosted\n loginSuccessUrl?: string; // where the user should be sent after the entire login completes, including the token exchange\n loginUrl?: string;\n loginInitUrl?: string;\n logoutUrl?: string;\n logoutCallbackUrl?: string;\n challengeUrl?: string;\n refreshUrl?: string;\n userUrl?: string;\n clearSessionUrl?: string;\n include?: string[];\n exclude?: string[];\n basePath?: string;\n baseUrl?: string;\n oauthServer?: string;\n autoRedirect?: boolean;\n disableRefresh?: boolean;\n targetContainerElement?: HTMLElement | string;\n deepLinkHandling?: DeepLinkHandling;\n};\n\n/**\n * Configuration values for Civic Auth.\n * Only clientId is required, all others are optional.\n */\nexport type AuthConfig = OptionalAuthConfig & {\n clientId: string;\n exclude?: string[];\n};\n\n/**\n * Default configuration values that will be used if not overridden\n */\nexport const defaultAuthConfig: Omit<AuthConfigWithDefaults, \"clientId\"> = {\n oauthServer: DEFAULT_AUTH_SERVER,\n // Internal API routes\n // deconstruct systemUrls to set the keys and default values\n ...(Object.fromEntries(\n Object.entries(systemUrlsConfig).map(([key, value]) => [\n key,\n value.defaultPath,\n ]),\n ) as {\n [key in keyof typeof systemUrlsConfig]: string;\n }),\n // User-facing routes\n loginSuccessUrl: undefined, // By default, the user is sent to the redirect_url\n loginUrl: \"/\",\n logoutCallbackUrl: \"/\",\n include: [\"/**\"],\n exclude: [\"/api/auth/**\"],\n basePath: \"\",\n baseUrl: undefined, // No default - will use request.nextUrl.origin when undefined\n autoRedirect: true, // Default to current behavior\n cookies: createNextJSCookieConfig(),\n deepLinkHandling: \"queryParamsOnly\", // Default to preserving query params while respecting loginSuccessUrl\n};\n\n/**\n * Resolves the authentication configuration by combining:\n * 1. Default values\n * 2. Environment variables (set internally by the plugin)\n * 3. Explicitly passed configuration\n *\n * Config will be merged deeply, with arrays not merged, so that the\n * default include list (for example) [\"/*\"] will not be added\n *\n * Note: Developers should not set _civic_auth_* environment variables directly.\n * Instead, pass configuration to the createCivicAuthPlugin in next.config.js:\n *\n * @example\n * ```js\n * // next.config.js\n * export default createCivicAuthPlugin({\n * callbackUrl: '/custom/callback',\n * })\n * ```\n */\nexport const resolveAuthConfig = (\n config: Partial<AuthConfig> = {},\n): AuthConfigWithDefaults => {\n // Read configuration that was set by the plugin via environment variables\n // Don't load environment cookies if explicit cookies are provided\n const hasExplicitCookies = config.cookies;\n\n const configFromEnv = withoutUndefined({\n clientId: process.env._civic_auth_client_id,\n oauthServer: process.env._civic_oauth_server,\n callbackUrl: process.env._civic_auth_callback_url,\n loginSuccessUrl: process.env._civic_auth_login_success_url,\n challengeUrl: process.env._civic_auth_challenge_url,\n loginUrl: process.env._civic_auth_login_url,\n loginInitUrl: process.env._civic_auth_login_init_url,\n logoutUrl: process.env._civic_auth_logout_url,\n logoutCallbackUrl: process.env._civic_auth_logout_callback_url,\n refreshUrl: process.env._civic_auth_refresh_url,\n userUrl: process.env._civic_auth_user_url,\n clearSessionUrl: process.env._civic_auth_clear_session_url,\n include: process.env._civic_auth_includes?.split(\",\"),\n exclude: process.env._civic_auth_excludes?.split(\",\"),\n cookies: hasExplicitCookies\n ? undefined\n : process.env._civic_auth_cookie_config\n ? JSON.parse(process.env._civic_auth_cookie_config)\n : undefined,\n basePath: process.env._civic_auth_base_path || \"\",\n baseUrl: process.env._civic_auth_base_url,\n autoRedirect:\n process.env._civic_auth_auto_redirect === \"false\" ? false : undefined,\n disableRefresh:\n process.env._civic_auth_disable_refresh === \"true\" ? true : undefined,\n targetContainerElement: process.env._civic_auth_target_container_element,\n deepLinkHandling: ([\"fullUrl\", \"queryParamsOnly\", \"disabled\"].includes(\n process.env._civic_auth_deep_link_handling || \"\",\n )\n ? process.env._civic_auth_deep_link_handling\n : undefined) as DeepLinkHandling | undefined,\n }) as AuthConfig;\n\n // Ensure \"/api/auth/**\" is always excluded\n const finalExclude = new Set([\n ...defaultAuthConfig.exclude,\n ...(configFromEnv.exclude || []),\n ...(config.exclude ?? []),\n ]);\n\n // Store explicit cookies config before merge to preserve it\n const explicitCookies = config.cookies;\n\n // Perform a deep merge of the configurations\n const mergedConfig = merge.withOptions(\n { mergeArrays: false },\n defaultAuthConfig,\n configFromEnv,\n config,\n );\n\n // Override the exclude list with the ensured list\n mergedConfig.exclude = Array.from(finalExclude);\n\n // Update cookie configuration with basePath if it wasn't explicitly provided\n // Only auto-configure if no explicit cookies are provided anywhere\n if (!config.cookies && !configFromEnv.cookies && mergedConfig.basePath) {\n mergedConfig.cookies = createNextJSCookieConfig(mergedConfig.basePath);\n }\n\n // Merge explicit cookies with merged config to preserve both explicit settings and defaults\n if (explicitCookies && mergedConfig.cookies) {\n mergedConfig.cookies = merge.withOptions(\n { mergeArrays: false },\n mergedConfig.cookies,\n explicitCookies,\n );\n }\n\n // Security warnings for insecure cookie configurations\n const returnUrlCookieConfig =\n mergedConfig.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];\n if (returnUrlCookieConfig) {\n if (returnUrlCookieConfig.httpOnly === false) {\n logger.warn(\n \"SECURITY WARNING: RETURN_URL cookie httpOnly is disabled. This exposes the cookie to XSS attacks.\",\n );\n }\n if (returnUrlCookieConfig.secure === false) {\n logger.warn(\n \"SECURITY WARNING: RETURN_URL cookie secure is disabled. This allows cookie theft over HTTP.\",\n );\n }\n if (returnUrlCookieConfig.sameSite === \"none\") {\n logger.warn(\n \"SECURITY WARNING: RETURN_URL cookie sameSite is set to 'none'. This allows cross-site request access.\",\n );\n }\n }\n\n if (mergedConfig.clientId === undefined) {\n throw new Error(\"Civic Auth client ID is required\");\n }\n\n return mergedConfig as AuthConfigWithDefaults & { clientId: string };\n};\n\n/**\n * Applies basePath to URLs based on URL_CONFIG rules\n */\nfunction applyBasePathToUrls(\n authConfig: AuthConfig,\n basePath: string,\n): Partial<AuthConfig> {\n const defaultUrlsWithBasePath: Record<string, string> = {};\n\n if (!basePath) return defaultUrlsWithBasePath;\n\n Object.entries(URL_CONFIG).forEach(([key, config]) => {\n // Skip if explicitly set in authConfig\n const currentValue = authConfig[key as keyof AuthConfig];\n if (\n currentValue !== undefined &&\n !(config.checkEmpty && currentValue === \"\")\n ) {\n return;\n }\n\n // Apply basePath based on config rules\n const urlValue = config.useBasePathOnly\n ? basePath\n : `${basePath}${config.defaultPath}`;\n\n defaultUrlsWithBasePath[key] = urlValue;\n });\n\n return defaultUrlsWithBasePath as Partial<AuthConfig>;\n}\n\n/**\n * Creates a Next.js plugin that handles auth configuration.\n *\n * This is the main configuration point for the auth system.\n * Do not set _civic_auth_* environment variables directly - instead,\n * pass your configuration here.\n *\n * The only required field is clientId.\n *\n * Notes:\n * - If you provide explicit URLs, they will be used exactly as provided.\n * - Default URLs will automatically include the basePath from your Next.js config.\n *\n * @example\n * ```js\n * // next.config.js\n * export default createCivicAuthPlugin({\n * clientId: 'my-client-id',\n * });\n * ```\n *\n * @example\n * ```js\n * // next.config.js\n * export default createCivicAuthPlugin({\n * clientId: 'my-client-id',\n * callbackUrl: '/custom/callback',\n * loginUrl: '/custom/login',\n * logoutUrl: '/custom/logout',\n * logoutCallbackUrl: '/custom/logoutcallback',\n * include: ['/protected/*'],\n * exclude: ['/public/*']\n * })\n * ```\n *\n * The plugin sets internal environment variables that are used by\n * the auth system. These variables should not be set manually.\n */\nexport type NextJSPluginFn = (nextConfig?: NextConfig) => NextConfig;\n\nexport const createCivicAuthPlugin = (\n authConfig: AuthConfig,\n): NextJSPluginFn => {\n return (nextConfig?: NextConfig) => {\n logger.debug(\n \"createCivicAuthPlugin nextConfig\",\n JSON.stringify(nextConfig, null, 2),\n );\n\n // Extract basePath from Next.js config\n const basePath = sanitizeBasePath(nextConfig?.basePath || \"\");\n\n // Apply basePath to URLs using the configuration mapping\n const defaultUrlsWithBasePath = applyBasePathToUrls(authConfig, basePath);\n\n // Create final config with basePath and possibly modified URLs\n const resolvedConfig = resolveAuthConfig({\n ...defaultUrlsWithBasePath,\n ...authConfig,\n basePath,\n });\n\n return {\n ...nextConfig,\n env: {\n ...nextConfig?.env,\n // Internal environment variables - do not set these manually\n _civic_auth_client_id: resolvedConfig.clientId,\n _civic_oauth_server: resolvedConfig.oauthServer,\n _civic_auth_callback_url: resolvedConfig.callbackUrl,\n _civic_auth_login_success_url: resolvedConfig.loginSuccessUrl,\n _civic_auth_challenge_url: resolvedConfig.challengeUrl,\n _civic_auth_login_url: resolvedConfig.loginUrl,\n _civic_auth_login_init_url: resolvedConfig.loginInitUrl,\n _civic_auth_logout_url: resolvedConfig.logoutUrl,\n _civic_auth_logout_callback_url: resolvedConfig.logoutCallbackUrl,\n _civic_auth_refresh_url: resolvedConfig.refreshUrl,\n _civic_auth_user_url: resolvedConfig.userUrl,\n _civic_auth_clear_session_url: resolvedConfig.clearSessionUrl,\n _civic_auth_includes: resolvedConfig.include.join(\",\"),\n _civic_auth_excludes: resolvedConfig.exclude.join(\",\"),\n _civic_auth_cookie_config: JSON.stringify(resolvedConfig.cookies),\n _civic_auth_base_path: resolvedConfig.basePath,\n _civic_auth_base_url: resolvedConfig.baseUrl,\n _civic_auth_auto_redirect: resolvedConfig.autoRedirect.toString(),\n _civic_auth_disable_refresh: resolvedConfig.disableRefresh?.toString(),\n _civic_auth_deep_link_handling: resolvedConfig.deepLinkHandling,\n },\n };\n };\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/nextjs/cookies.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAqCjE;;GAEG;AACH,QAAA,MAAM,gBAAgB,YAAmB,OAAO,CAAC,sBAAsB,CAAC,kBAOvE,CAAC;AAEF,cAAM,mBAAoB,SAAQ,aAAa;IAC1B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;gBAAhD,MAAM,GAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAM;IAOlE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAqBxC,GAAG,CACP,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,oBAAoB,GAAE,OAAO,CAAC,YAAY,CAAM,GAC/C,OAAO,CAAC,IAAI,CAAC;IAsBV,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAgB5C;AAED,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/nextjs/cookies.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAqCjE;;GAEG;AACH,QAAA,MAAM,gBAAgB,YAAmB,OAAO,CAAC,sBAAsB,CAAC,kBAsBvE,CAAC;AAEF,cAAM,mBAAoB,SAAQ,aAAa;IAC1B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;gBAAhD,MAAM,GAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,CAAM;IAOlE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAqBxC,GAAG,CACP,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,oBAAoB,GAAE,OAAO,CAAC,YAAY,CAAM,GAC/C,OAAO,CAAC,IAAI,CAAC;IAsBV,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAgB5C;AAED,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { cookies, headers } from "next/headers.js";
2
2
  import { extractCookieFromRawHeader } from "../shared/lib/cookieUtils.js";
3
- import { UserStorage } from "../shared/lib/types.js";
3
+ import { AuthFlowCookie, UserStorage, } from "../shared/lib/types.js";
4
4
  import { CookieStorage } from "../shared/lib/storage.js";
5
5
  import * as session from "../shared/lib/session.js";
6
6
  import { getCookieConfiguration } from "../shared/lib/util.js";
@@ -45,6 +45,19 @@ const clearAuthCookies = async (config) => {
45
45
  [UserStorage.USER]: config?.cookies?.user,
46
46
  });
47
47
  await session.clearAuthCookies(cookieStorage);
48
+ // Also clear auth flow cookies (like returnUrl) that are not part of the session
49
+ // This prevents stale deep-link cookies from being re-instated during logout
50
+ // Use the same path fallback chain as handleCallback's clearReturnUrlCookie
51
+ const returnUrlCookieConfig = config?.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];
52
+ const cookiePath = returnUrlCookieConfig?.path ?? config?.basePath ?? "/";
53
+ const cookieStore = await cookies();
54
+ cookieStore.set(AuthFlowCookie.RETURN_URL, "", {
55
+ maxAge: 0, // Immediately expire the cookie
56
+ path: cookiePath,
57
+ httpOnly: returnUrlCookieConfig?.httpOnly ?? true,
58
+ secure: returnUrlCookieConfig?.secure ?? true,
59
+ sameSite: returnUrlCookieConfig?.sameSite ?? "strict",
60
+ });
48
61
  };
49
62
  class NextjsCookieStorage extends CookieStorage {
50
63
  config;
@@ -98,7 +111,7 @@ class NextjsCookieStorage extends CookieStorage {
98
111
  // If we have a path configured, use it when deleting the cookie
99
112
  if (cookieSettings.path) {
100
113
  cookieStore.set(key, "", {
101
- expires: new Date(0), // Expire in the past
114
+ maxAge: 0, // Immediately expire the cookie
102
115
  path: cookieSettings.path,
103
116
  });
104
117
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/nextjs/cookies.ts"],"names":[],"mappings":"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","sourcesContent":["import { cookies, headers } from \"next/headers.js\";\nimport { extractCookieFromRawHeader } from \"@/shared/lib/cookieUtils.js\";\n\nimport type { KeySetter } from \"@/shared/lib/types.js\";\nimport { UserStorage, type CookieConfig } from \"@/shared/lib/types.js\";\nimport { CookieStorage } from \"@/shared/lib/storage.js\";\nimport * as session from \"@/shared/lib/session.js\";\nimport { getCookieConfiguration } from \"@/shared/lib/util.js\";\nimport type { AuthConfigWithDefaults } from \"@/nextjs/config.js\";\n\n/**\n * Create a mock Request object from Next.js headers for cookie configuration\n */\nconst createRequestFromHeaders = async (): Promise<Request | undefined> => {\n try {\n const headerStore = await headers();\n const host = headerStore.get(\"host\");\n const userAgent = headerStore.get(\"user-agent\");\n const protocol =\n headerStore.get(\"x-forwarded-proto\") ||\n headerStore.get(\"x-forwarded-protocol\") ||\n (process.env.NODE_ENV === \"production\" ? \"https\" : \"http\");\n\n if (!host) return undefined;\n\n const url = `${protocol}://${host}/`;\n\n // Create a minimal Request object with the headers we need\n const request = new Request(url, {\n headers: {\n \"user-agent\": userAgent || \"\",\n \"x-forwarded-proto\": headerStore.get(\"x-forwarded-proto\") || \"\",\n \"x-forwarded-protocol\": headerStore.get(\"x-forwarded-protocol\") || \"\",\n forwarded: headerStore.get(\"forwarded\") || \"\",\n },\n });\n\n return request;\n } catch (error) {\n console.error(\"Error creating request from headers\", error);\n // Headers might not be available in all contexts\n return undefined;\n }\n};\n\n/**\n * Clears all authentication cookies on server. Note, this can only be called by the server\n */\nconst clearAuthCookies = async (config?: Partial<AuthConfigWithDefaults>) => {\n // Use resolved config cookies if available, otherwise use default\n const cookieStorage = new NextjsCookieStorage({\n ...config?.cookies?.tokens,\n [UserStorage.USER]: config?.cookies?.user,\n });\n await session.clearAuthCookies(cookieStorage);\n};\n\nclass NextjsCookieStorage extends CookieStorage {\n constructor(public config: Partial<Record<KeySetter, CookieConfig>> = {}) {\n super({\n secure: true,\n httpOnly: true,\n });\n }\n\n async get(key: string): Promise<string | null> {\n const cookieStore = await cookies();\n const cookieSettings = this.config?.[key as KeySetter] || {};\n const configuredPath = cookieSettings.path;\n\n // If we have a non-root basePath, use raw header parsing to get the first cookie\n // which should be from the most specific path, avoiding duplicate cookie conflicts\n if (configuredPath && configuredPath !== \"/\") {\n const headerStore = await headers();\n const cookieHeader = headerStore.get(\"cookie\");\n const rawValue = extractCookieFromRawHeader(cookieHeader, key);\n if (rawValue) {\n return rawValue;\n }\n }\n\n // Fallback to standard Next.js cookie store\n const cookieValue = cookieStore.get(key);\n return cookieValue?.value || null;\n }\n\n async set(\n key: KeySetter,\n value: string,\n cookieConfigOverride: Partial<CookieConfig> = {},\n ): Promise<void> {\n const cookieStore = await cookies();\n const cookieSettings = this.config?.[key as KeySetter] || {\n ...this.settings,\n };\n\n // Get dynamic cookie configuration based on environment and browser\n const request = await createRequestFromHeaders();\n const dynamicConfig = getCookieConfiguration(request);\n\n const useCookieSettings = {\n ...cookieSettings,\n ...cookieConfigOverride,\n // Apply dynamic configuration for secure and sameSite\n secure: dynamicConfig.secure,\n sameSite: dynamicConfig.sameSite,\n };\n\n // Respect the httpOnly setting from configuration instead of hardcoding it\n await cookieStore.set(key, value, useCookieSettings);\n }\n\n async delete(key: KeySetter): Promise<void> {\n const cookieStore = await cookies();\n\n // Get cookie configuration for this key to respect the path setting\n const cookieSettings = this.config?.[key as KeySetter] || {};\n\n // If we have a path configured, use it when deleting the cookie\n if (cookieSettings.path) {\n cookieStore.set(key, \"\", {\n expires: new Date(0), // Expire in the past\n path: cookieSettings.path,\n });\n } else {\n cookieStore.delete(key);\n }\n }\n}\n\nexport { clearAuthCookies, NextjsCookieStorage };\n"]}
1
+ {"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/nextjs/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAGzE,OAAO,EACL,cAAc,EACd,WAAW,GAEZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,OAAO,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAG9D;;GAEG;AACH,MAAM,wBAAwB,GAAG,KAAK,IAAkC,EAAE;IACxE,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAChD,MAAM,QAAQ,GACZ,WAAW,CAAC,GAAG,CAAC,mBAAmB,CAAC;YACpC,WAAW,CAAC,GAAG,CAAC,sBAAsB,CAAC;YACvC,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAE7D,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QAE5B,MAAM,GAAG,GAAG,GAAG,QAAQ,MAAM,IAAI,GAAG,CAAC;QAErC,2DAA2D;QAC3D,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;YAC/B,OAAO,EAAE;gBACP,YAAY,EAAE,SAAS,IAAI,EAAE;gBAC7B,mBAAmB,EAAE,WAAW,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,EAAE;gBAC/D,sBAAsB,EAAE,WAAW,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,EAAE;gBACrE,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE;aAC9C;SACF,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC5D,iDAAiD;QACjD,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,gBAAgB,GAAG,KAAK,EAAE,MAAwC,EAAE,EAAE;IAC1E,kEAAkE;IAClE,MAAM,aAAa,GAAG,IAAI,mBAAmB,CAAC;QAC5C,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM;QAC1B,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;KAC1C,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;IAE9C,iFAAiF;IACjF,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,qBAAqB,GACzB,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,qBAAqB,EAAE,IAAI,IAAI,MAAM,EAAE,QAAQ,IAAI,GAAG,CAAC;IAC1E,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;IACpC,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,EAAE;QAC7C,MAAM,EAAE,CAAC,EAAE,gCAAgC;QAC3C,IAAI,EAAE,UAAU;QAChB,QAAQ,EAAE,qBAAqB,EAAE,QAAQ,IAAI,IAAI;QACjD,MAAM,EAAE,qBAAqB,EAAE,MAAM,IAAI,IAAI;QAC7C,QAAQ,EAAE,qBAAqB,EAAE,QAAQ,IAAI,QAAQ;KACtD,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,mBAAoB,SAAQ,aAAa;IAC1B;IAAnB,YAAmB,SAAmD,EAAE;QACtE,KAAK,CAAC;YACJ,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QAJc,WAAM,GAAN,MAAM,CAA+C;IAKxE,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;QACpC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAgB,CAAC,IAAI,EAAE,CAAC;QAC7D,MAAM,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC;QAE3C,iFAAiF;QACjF,mFAAmF;QACnF,IAAI,cAAc,IAAI,cAAc,KAAK,GAAG,EAAE,CAAC;YAC7C,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;YACpC,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC/C,MAAM,QAAQ,GAAG,0BAA0B,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;YAC/D,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,4CAA4C;QAC5C,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzC,OAAO,WAAW,EAAE,KAAK,IAAI,IAAI,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,GAAG,CACP,GAAc,EACd,KAAa,EACb,uBAA8C,EAAE;QAEhD,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;QACpC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAgB,CAAC,IAAI;YACxD,GAAG,IAAI,CAAC,QAAQ;SACjB,CAAC;QAEF,oEAAoE;QACpE,MAAM,OAAO,GAAG,MAAM,wBAAwB,EAAE,CAAC;QACjD,MAAM,aAAa,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAEtD,MAAM,iBAAiB,GAAG;YACxB,GAAG,cAAc;YACjB,GAAG,oBAAoB;YACvB,sDAAsD;YACtD,MAAM,EAAE,aAAa,CAAC,MAAM;YAC5B,QAAQ,EAAE,aAAa,CAAC,QAAQ;SACjC,CAAC;QAEF,2EAA2E;QAC3E,MAAM,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAc;QACzB,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;QAEpC,oEAAoE;QACpE,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAgB,CAAC,IAAI,EAAE,CAAC;QAE7D,gEAAgE;QAChE,IAAI,cAAc,CAAC,IAAI,EAAE,CAAC;YACxB,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,EAAE;gBACvB,MAAM,EAAE,CAAC,EAAE,gCAAgC;gBAC3C,IAAI,EAAE,cAAc,CAAC,IAAI;aAC1B,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;CACF;AAED,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC","sourcesContent":["import { cookies, headers } from \"next/headers.js\";\nimport { extractCookieFromRawHeader } from \"@/shared/lib/cookieUtils.js\";\n\nimport type { KeySetter } from \"@/shared/lib/types.js\";\nimport {\n AuthFlowCookie,\n UserStorage,\n type CookieConfig,\n} from \"@/shared/lib/types.js\";\nimport { CookieStorage } from \"@/shared/lib/storage.js\";\nimport * as session from \"@/shared/lib/session.js\";\nimport { getCookieConfiguration } from \"@/shared/lib/util.js\";\nimport type { AuthConfigWithDefaults } from \"@/nextjs/config.js\";\n\n/**\n * Create a mock Request object from Next.js headers for cookie configuration\n */\nconst createRequestFromHeaders = async (): Promise<Request | undefined> => {\n try {\n const headerStore = await headers();\n const host = headerStore.get(\"host\");\n const userAgent = headerStore.get(\"user-agent\");\n const protocol =\n headerStore.get(\"x-forwarded-proto\") ||\n headerStore.get(\"x-forwarded-protocol\") ||\n (process.env.NODE_ENV === \"production\" ? \"https\" : \"http\");\n\n if (!host) return undefined;\n\n const url = `${protocol}://${host}/`;\n\n // Create a minimal Request object with the headers we need\n const request = new Request(url, {\n headers: {\n \"user-agent\": userAgent || \"\",\n \"x-forwarded-proto\": headerStore.get(\"x-forwarded-proto\") || \"\",\n \"x-forwarded-protocol\": headerStore.get(\"x-forwarded-protocol\") || \"\",\n forwarded: headerStore.get(\"forwarded\") || \"\",\n },\n });\n\n return request;\n } catch (error) {\n console.error(\"Error creating request from headers\", error);\n // Headers might not be available in all contexts\n return undefined;\n }\n};\n\n/**\n * Clears all authentication cookies on server. Note, this can only be called by the server\n */\nconst clearAuthCookies = async (config?: Partial<AuthConfigWithDefaults>) => {\n // Use resolved config cookies if available, otherwise use default\n const cookieStorage = new NextjsCookieStorage({\n ...config?.cookies?.tokens,\n [UserStorage.USER]: config?.cookies?.user,\n });\n await session.clearAuthCookies(cookieStorage);\n\n // Also clear auth flow cookies (like returnUrl) that are not part of the session\n // This prevents stale deep-link cookies from being re-instated during logout\n // Use the same path fallback chain as handleCallback's clearReturnUrlCookie\n const returnUrlCookieConfig =\n config?.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];\n const cookiePath = returnUrlCookieConfig?.path ?? config?.basePath ?? \"/\";\n const cookieStore = await cookies();\n cookieStore.set(AuthFlowCookie.RETURN_URL, \"\", {\n maxAge: 0, // Immediately expire the cookie\n path: cookiePath,\n httpOnly: returnUrlCookieConfig?.httpOnly ?? true,\n secure: returnUrlCookieConfig?.secure ?? true,\n sameSite: returnUrlCookieConfig?.sameSite ?? \"strict\",\n });\n};\n\nclass NextjsCookieStorage extends CookieStorage {\n constructor(public config: Partial<Record<KeySetter, CookieConfig>> = {}) {\n super({\n secure: true,\n httpOnly: true,\n });\n }\n\n async get(key: string): Promise<string | null> {\n const cookieStore = await cookies();\n const cookieSettings = this.config?.[key as KeySetter] || {};\n const configuredPath = cookieSettings.path;\n\n // If we have a non-root basePath, use raw header parsing to get the first cookie\n // which should be from the most specific path, avoiding duplicate cookie conflicts\n if (configuredPath && configuredPath !== \"/\") {\n const headerStore = await headers();\n const cookieHeader = headerStore.get(\"cookie\");\n const rawValue = extractCookieFromRawHeader(cookieHeader, key);\n if (rawValue) {\n return rawValue;\n }\n }\n\n // Fallback to standard Next.js cookie store\n const cookieValue = cookieStore.get(key);\n return cookieValue?.value || null;\n }\n\n async set(\n key: KeySetter,\n value: string,\n cookieConfigOverride: Partial<CookieConfig> = {},\n ): Promise<void> {\n const cookieStore = await cookies();\n const cookieSettings = this.config?.[key as KeySetter] || {\n ...this.settings,\n };\n\n // Get dynamic cookie configuration based on environment and browser\n const request = await createRequestFromHeaders();\n const dynamicConfig = getCookieConfiguration(request);\n\n const useCookieSettings = {\n ...cookieSettings,\n ...cookieConfigOverride,\n // Apply dynamic configuration for secure and sameSite\n secure: dynamicConfig.secure,\n sameSite: dynamicConfig.sameSite,\n };\n\n // Respect the httpOnly setting from configuration instead of hardcoding it\n await cookieStore.set(key, value, useCookieSettings);\n }\n\n async delete(key: KeySetter): Promise<void> {\n const cookieStore = await cookies();\n\n // Get cookie configuration for this key to respect the path setting\n const cookieSettings = this.config?.[key as KeySetter] || {};\n\n // If we have a path configured, use it when deleting the cookie\n if (cookieSettings.path) {\n cookieStore.set(key, \"\", {\n maxAge: 0, // Immediately expire the cookie\n path: cookieSettings.path,\n });\n } else {\n cookieStore.delete(key);\n }\n }\n}\n\nexport { clearAuthCookies, NextjsCookieStorage };\n"]}
@@ -7,7 +7,7 @@ export declare const getUser: <T extends UnknownObject = EmptyObject>() => Promi
7
7
  export declare const getTokens: () => Promise<OAuthTokens | null>;
8
8
  export { handler } from "../nextjs/routeHandler.js";
9
9
  export { NextjsCookieStorage } from "../nextjs/cookies.js";
10
- export type { AuthConfig, CookiesConfigObject, AuthConfigWithDefaults, } from "../nextjs/config.js";
10
+ export type { AuthConfig, CookiesConfigObject, AuthConfigWithDefaults, DeepLinkHandling, NextJSPluginFn, } from "../nextjs/config.js";
11
11
  export { CivicNextAuthProvider as CivicAuthProvider, type NextCivicAuthProviderProps as AuthProviderProps, } from "../nextjs/providers/NextAuthProvider.js";
12
12
  export { revalidateUserData } from "../nextjs/actions.js";
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAWvD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAmB9E,eAAO,MAAM,UAAU,QAAa,OAAO,CAAC,OAAO,CAIlD,CAAC;AAEF,eAAO,MAAM,OAAO,GAClB,CAAC,SAAS,aAAa,qBACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAK1B,CAAC;AAEF,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,WAAW,GAAG,IAAI,CAK5D,CAAC;AAEF,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EACV,UAAU,EACV,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,IAAI,iBAAiB,EAC1C,KAAK,0BAA0B,IAAI,iBAAiB,GACrD,MAAM,wCAAwC,CAAC;AAGhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAWvD,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAmB9E,eAAO,MAAM,UAAU,QAAa,OAAO,CAAC,OAAO,CAIlD,CAAC;AAEF,eAAO,MAAM,OAAO,GAClB,CAAC,SAAS,aAAa,qBACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAK1B,CAAC;AAEF,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,WAAW,GAAG,IAAI,CAK5D,CAAC;AAEF,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EACV,UAAU,EACV,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,EAChB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,IAAI,iBAAiB,EAC1C,KAAK,0BAA0B,IAAI,iBAAiB,GACrD,MAAM,wCAAwC,CAAC;AAGhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,4BAA4B,EAAE,MAAM,0CAA0C,CAAC;AAExF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,CAAC;AACf,OAAO,EACL,SAAS,IAAI,gBAAgB,EAC7B,OAAO,IAAI,cAAc,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,0BAA0B,GAAG,GAAwB,EAAE;IAC3D,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,OAAO,IAAI,mBAAmB,CAAC;QAC7B,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM;QACzB,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI;KACzC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,KAAK,IAAqC,EAAE;IAClE,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,aAAa,GAAG,0BAA0B,EAAE,CAAC;IACnD,OAAO,4BAA4B,CAAC,KAAK,CACvC,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,EAC9C,aAAa,CACd,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,IAAsB,EAAE;IACrD,MAAM,YAAY,GAAG,MAAM,eAAe,EAAE,CAAC;IAC7C,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAC1E,OAAO,eAAe,CAAC,aAAa,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,IAEC,EAAE;IAC7B,MAAM,eAAe,GAAG,MAAM,UAAU,EAAE,CAAC;IAC3C,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,aAAa,GAAG,0BAA0B,EAAE,CAAC;IACnD,OAAO,cAAc,CAAI,aAAa,CAAC,CAAC;AAC1C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,IAAiC,EAAE;IAC/D,MAAM,eAAe,GAAG,MAAM,UAAU,EAAE,CAAC;IAC3C,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,aAAa,GAAG,0BAA0B,EAAE,CAAC;IACnD,OAAO,gBAAgB,CAAC,aAAa,CAAC,CAAC;AACzC,CAAC,CAAC;AAEF,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAM1D,OAAO,EACL,qBAAqB,IAAI,iBAAiB,GAE3C,MAAM,wCAAwC,CAAC;AAEhD,wBAAwB;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["import { resolveAuthConfig } from \"@/nextjs/config.js\";\nimport { NextjsCookieStorage } from \"@/nextjs/cookies.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\nimport type { AuthenticationResolver } from \"@/services/types.js\";\nimport { printVersion } from \"@/shared/index.js\";\nprintVersion();\nimport {\n getTokens as getSessionTokens,\n getUser as getSessionUser,\n} from \"@/shared/lib/session.js\";\nimport { UserStorage } from \"@/shared/lib/types.js\";\nimport type { EmptyObject, OAuthTokens, UnknownObject, User } from \"@/types.js\";\nexport { resolveAuthConfig };\n\nexport { createCivicAuthPlugin, defaultAuthConfig } from \"@/nextjs/config.js\";\n\nconst getConfiguredCookieStorage = (): NextjsCookieStorage => {\n const config = resolveAuthConfig();\n return new NextjsCookieStorage({\n ...config.cookies?.tokens,\n [UserStorage.USER]: config.cookies?.user,\n });\n};\n\nconst getAuthResolver = async (): Promise<AuthenticationResolver> => {\n const config = resolveAuthConfig();\n const clientStorage = getConfiguredCookieStorage();\n return ServerAuthenticationResolver.build(\n { ...config, redirectUrl: config.callbackUrl },\n clientStorage,\n );\n};\n\nexport const isLoggedIn = async (): Promise<boolean> => {\n const authResolver = await getAuthResolver();\n const existingSession = await authResolver.validateExistingSession(false);\n return existingSession.authenticated;\n};\n\nexport const getUser = async <\n T extends UnknownObject = EmptyObject,\n>(): Promise<User<T> | null> => {\n const hasValidSession = await isLoggedIn();\n if (!hasValidSession) return null;\n const clientStorage = getConfiguredCookieStorage();\n return getSessionUser<T>(clientStorage);\n};\n\nexport const getTokens = async (): Promise<OAuthTokens | null> => {\n const hasValidSession = await isLoggedIn();\n if (!hasValidSession) return null;\n const clientStorage = getConfiguredCookieStorage();\n return getSessionTokens(clientStorage);\n};\n\nexport { handler } from \"@/nextjs/routeHandler.js\";\nexport { NextjsCookieStorage } from \"@/nextjs/cookies.js\";\nexport type {\n AuthConfig,\n CookiesConfigObject,\n AuthConfigWithDefaults,\n} from \"@/nextjs/config.js\";\nexport {\n CivicNextAuthProvider as CivicAuthProvider,\n type NextCivicAuthProviderProps as AuthProviderProps,\n} from \"@/nextjs/providers/NextAuthProvider.js\";\n\n// NextJS server actions\nexport { revalidateUserData } from \"@/nextjs/actions.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nextjs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,4BAA4B,EAAE,MAAM,0CAA0C,CAAC;AAExF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,CAAC;AACf,OAAO,EACL,SAAS,IAAI,gBAAgB,EAC7B,OAAO,IAAI,cAAc,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,0BAA0B,GAAG,GAAwB,EAAE;IAC3D,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,OAAO,IAAI,mBAAmB,CAAC;QAC7B,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM;QACzB,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI;KACzC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,KAAK,IAAqC,EAAE;IAClE,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,aAAa,GAAG,0BAA0B,EAAE,CAAC;IACnD,OAAO,4BAA4B,CAAC,KAAK,CACvC,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,EAC9C,aAAa,CACd,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,IAAsB,EAAE;IACrD,MAAM,YAAY,GAAG,MAAM,eAAe,EAAE,CAAC;IAC7C,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAC1E,OAAO,eAAe,CAAC,aAAa,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,IAEC,EAAE;IAC7B,MAAM,eAAe,GAAG,MAAM,UAAU,EAAE,CAAC;IAC3C,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,aAAa,GAAG,0BAA0B,EAAE,CAAC;IACnD,OAAO,cAAc,CAAI,aAAa,CAAC,CAAC;AAC1C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,IAAiC,EAAE;IAC/D,MAAM,eAAe,GAAG,MAAM,UAAU,EAAE,CAAC;IAC3C,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,aAAa,GAAG,0BAA0B,EAAE,CAAC;IACnD,OAAO,gBAAgB,CAAC,aAAa,CAAC,CAAC;AACzC,CAAC,CAAC;AAEF,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAQ1D,OAAO,EACL,qBAAqB,IAAI,iBAAiB,GAE3C,MAAM,wCAAwC,CAAC;AAEhD,wBAAwB;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["import { resolveAuthConfig } from \"@/nextjs/config.js\";\nimport { NextjsCookieStorage } from \"@/nextjs/cookies.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\nimport type { AuthenticationResolver } from \"@/services/types.js\";\nimport { printVersion } from \"@/shared/index.js\";\nprintVersion();\nimport {\n getTokens as getSessionTokens,\n getUser as getSessionUser,\n} from \"@/shared/lib/session.js\";\nimport { UserStorage } from \"@/shared/lib/types.js\";\nimport type { EmptyObject, OAuthTokens, UnknownObject, User } from \"@/types.js\";\nexport { resolveAuthConfig };\n\nexport { createCivicAuthPlugin, defaultAuthConfig } from \"@/nextjs/config.js\";\n\nconst getConfiguredCookieStorage = (): NextjsCookieStorage => {\n const config = resolveAuthConfig();\n return new NextjsCookieStorage({\n ...config.cookies?.tokens,\n [UserStorage.USER]: config.cookies?.user,\n });\n};\n\nconst getAuthResolver = async (): Promise<AuthenticationResolver> => {\n const config = resolveAuthConfig();\n const clientStorage = getConfiguredCookieStorage();\n return ServerAuthenticationResolver.build(\n { ...config, redirectUrl: config.callbackUrl },\n clientStorage,\n );\n};\n\nexport const isLoggedIn = async (): Promise<boolean> => {\n const authResolver = await getAuthResolver();\n const existingSession = await authResolver.validateExistingSession(false);\n return existingSession.authenticated;\n};\n\nexport const getUser = async <\n T extends UnknownObject = EmptyObject,\n>(): Promise<User<T> | null> => {\n const hasValidSession = await isLoggedIn();\n if (!hasValidSession) return null;\n const clientStorage = getConfiguredCookieStorage();\n return getSessionUser<T>(clientStorage);\n};\n\nexport const getTokens = async (): Promise<OAuthTokens | null> => {\n const hasValidSession = await isLoggedIn();\n if (!hasValidSession) return null;\n const clientStorage = getConfiguredCookieStorage();\n return getSessionTokens(clientStorage);\n};\n\nexport { handler } from \"@/nextjs/routeHandler.js\";\nexport { NextjsCookieStorage } from \"@/nextjs/cookies.js\";\nexport type {\n AuthConfig,\n CookiesConfigObject,\n AuthConfigWithDefaults,\n DeepLinkHandling,\n NextJSPluginFn,\n} from \"@/nextjs/config.js\";\nexport {\n CivicNextAuthProvider as CivicAuthProvider,\n type NextCivicAuthProviderProps as AuthProviderProps,\n} from \"@/nextjs/providers/NextAuthProvider.js\";\n\n// NextJS server actions\nexport { revalidateUserData } from \"@/nextjs/actions.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"routeHandler.d.ts","sourceRoot":"","sources":["../../src/nextjs/routeHandler.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AASrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA0U9C,wBAAsB,YAAY,CAChC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,CAAC,CAwEvB;AA0CD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,CAAC,CA4DvB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,iCAEF,WAAW,KAAG,OAAO,CAAC,YAAY,CAuCjD,CAAC"}
1
+ {"version":3,"file":"routeHandler.d.ts","sourceRoot":"","sources":["../../src/nextjs/routeHandler.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AASrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA4Y9C,wBAAsB,YAAY,CAChC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,CAAC,CAwEvB;AA0CD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,CAAC,CA4DvB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,iCAEF,WAAW,KAAG,OAAO,CAAC,YAAY,CAuCjD,CAAC"}
@@ -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;
@@ -71,6 +71,30 @@ const createCivicAuth = (request, config) => {
71
71
  urlDetectionRequest, // Return for use in handlers
72
72
  };
73
73
  };
74
+ /**
75
+ * Injects loginSuccessUrl into an existing base64-encoded state string, or creates
76
+ * a new state if none exists. This allows the deep link destination (computed by
77
+ * middleware) to be passed through the OAuth flow via the existing loginSuccessUrl
78
+ * mechanism.
79
+ */
80
+ function injectLoginSuccessUrlIntoState(frontendState, loginSuccessUrl) {
81
+ let stateObj = {};
82
+ if (frontendState) {
83
+ try {
84
+ // Decode the existing state
85
+ const jsonString = atob(frontendState);
86
+ stateObj = JSON.parse(jsonString);
87
+ }
88
+ catch (error) {
89
+ logger.warn("[LOGIN_HANDLER] Failed to parse existing state, creating new state", { error });
90
+ // Continue with empty stateObj - we'll still add loginSuccessUrl
91
+ }
92
+ }
93
+ // Set loginSuccessUrl in state (overrides any existing value for fullUrl mode)
94
+ stateObj.loginSuccessUrl = loginSuccessUrl;
95
+ // Encode and return the state
96
+ return btoa(JSON.stringify(stateObj));
97
+ }
74
98
  /**
75
99
  * Login handler - backend OAuth login initiation endpoint
76
100
  * Uses CivicAuth.buildLoginUrl()
@@ -78,13 +102,37 @@ const createCivicAuth = (request, config) => {
78
102
  async function handleLogin(request, config) {
79
103
  const resolvedConfigs = resolveAuthConfig(config);
80
104
  try {
81
- const frontendState = request.nextUrl.searchParams.get("state");
105
+ let frontendState = request.nextUrl.searchParams.get("state");
82
106
  // Store appUrl in cookie if provided as query parameter
83
107
  const appUrlFromQuery = request.nextUrl.searchParams.get("appUrl");
108
+ const cookieStorage = new NextjsCookieStorage(resolvedConfigs.cookies?.tokens ?? {});
84
109
  if (appUrlFromQuery) {
85
- const cookieStorage = new NextjsCookieStorage(resolvedConfigs.cookies?.tokens ?? {});
86
110
  await cookieStorage.set(CodeVerifier.APP_URL, appUrlFromQuery);
87
111
  }
112
+ // Read the deep link cookie (set by middleware with deepLinkHandling already applied)
113
+ // and inject it into the OAuth state's loginSuccessUrl field. This ensures the
114
+ // destination survives the OAuth flow even when cookies aren't available in the
115
+ // callback (e.g., due to SameSite restrictions in Chromium iframes).
116
+ // Note: We don't delete the cookie here because:
117
+ // 1. The login handler may be called multiple times (iframe preload, mode switching)
118
+ // 2. We only want to consume the cookie on successful auth (handled in callback)
119
+ if (resolvedConfigs.deepLinkHandling !== "disabled") {
120
+ const deepLinkDestination = await cookieStorage.get(AuthFlowCookie.RETURN_URL);
121
+ if (deepLinkDestination) {
122
+ // Re-validate the cookie value to guard against tampering (defense-in-depth)
123
+ const originUrl = getOriginUrl(request, resolvedConfigs);
124
+ const sanitized = sanitizeReturnUrl(deepLinkDestination, originUrl);
125
+ if (sanitized) {
126
+ // Inject the destination into state.loginSuccessUrl
127
+ // Don't prepend basePath here - the callback handler's redirectWithBasePath will handle it
128
+ logger.debug("[LOGIN_HANDLER] Found deep link cookie, injecting into state.loginSuccessUrl", { deepLinkDestination, sanitized });
129
+ frontendState = injectLoginSuccessUrlIntoState(frontendState, sanitized);
130
+ }
131
+ else {
132
+ logger.warn("[LOGIN_HANDLER] Rejected invalid deep link cookie value", { deepLinkDestination });
133
+ }
134
+ }
135
+ }
88
136
  const { civicAuth } = createCivicAuth(request, resolvedConfigs);
89
137
  const url = await civicAuth.buildLoginUrl({
90
138
  state: frontendState || undefined,
@@ -96,6 +144,14 @@ async function handleLogin(request, config) {
96
144
  }
97
145
  catch (error) {
98
146
  logger.error("[LOGIN_HANDLER] Backend login error:", error);
147
+ // Clean up deep link cookie on error to prevent stale cookies
148
+ try {
149
+ const errorCookieStorage = new NextjsCookieStorage(resolvedConfigs.cookies?.tokens ?? {});
150
+ await errorCookieStorage.delete(AuthFlowCookie.RETURN_URL);
151
+ }
152
+ catch (cookieError) {
153
+ logger.warn("[LOGIN_HANDLER] Failed to clean up deep link cookie on error", { cookieError });
154
+ }
99
155
  const urlDetectionRequest = toUrlDetectionRequest(request);
100
156
  const appUrl = CivicAuth.getAppUrl(urlDetectionRequest);
101
157
  return NextResponse.redirect(CivicAuth.toAbsoluteUrl(urlDetectionRequest, "/?error=login_failed", appUrl));
@@ -131,64 +187,59 @@ async function handleCallback(request, config) {
131
187
  if (!code || !state)
132
188
  throw new AuthError("Bad parameters", 400);
133
189
  try {
134
- const { civicAuth, appUrl, urlDetectionRequest, cookieStorage } = createCivicAuth(request, resolvedConfigs);
190
+ const { civicAuth, appUrl, urlDetectionRequest } = createCivicAuth(request, resolvedConfigs);
135
191
  // Convert NextRequest to the format expected by handleCallback
136
192
  const handleCallbackRequest = {
137
193
  headers: Object.fromEntries(request.headers.entries()),
138
194
  url: request.url.toString(),
139
195
  };
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
196
  // Use CivicAuth's smart callback handler
197
+ // Note: CivicAuth.handleCallback reads loginSuccessUrl from state (injected by login handler)
198
+ // with fallback to config.loginSuccessUrl, so we don't need to pass frontendUrl option
182
199
  const result = await civicAuth.handleCallback({
183
200
  code,
184
201
  state,
185
202
  req: handleCallbackRequest,
186
- }, {
187
- // Pass the properly resolved frontendUrl
188
- frontendUrl: prependBasePath(frontendUrl, config.basePath || ""),
189
203
  });
204
+ // Fallback: If state was corrupted/lost and CivicAuth redirected to the default loginSuccessUrl,
205
+ // check if we have a deep link cookie as a backup. This provides resilience against state loss.
206
+ const cookieStorage = new NextjsCookieStorage(resolvedConfigs.cookies?.tokens ?? {});
207
+ if (resolvedConfigs.deepLinkHandling !== "disabled" && result.redirectTo) {
208
+ const defaultLoginSuccessUrl = resolvedConfigs.loginSuccessUrl || "/";
209
+ const deepLinkFromCookie = await cookieStorage.get(AuthFlowCookie.RETURN_URL);
210
+ // If redirecting to default and we have a cookie, use the cookie value
211
+ if (deepLinkFromCookie && result.redirectTo === defaultLoginSuccessUrl) {
212
+ // Re-validate the cookie value to guard against tampering (defense-in-depth)
213
+ const originUrl = getOriginUrl(request, resolvedConfigs);
214
+ const sanitized = sanitizeReturnUrl(deepLinkFromCookie, originUrl);
215
+ if (sanitized) {
216
+ // Don't prepend basePath here - redirectWithBasePath will handle it
217
+ logger.debug("[CALLBACK_HANDLER] State missing loginSuccessUrl, using cookie fallback", { deepLinkFromCookie, sanitized });
218
+ result.redirectTo = sanitized;
219
+ }
220
+ else {
221
+ logger.warn("[CALLBACK_HANDLER] Rejected invalid fallback cookie value", { deepLinkFromCookie });
222
+ // Keep the default loginSuccessUrl
223
+ }
224
+ }
225
+ }
226
+ // Helper to clear the deep link cookie on successful auth
227
+ // Always clear the cookie to handle stale cookies from previous sessions
228
+ const clearDeepLinkCookie = (response) => {
229
+ const cookieConfig = resolvedConfigs.cookies?.tokens?.[AuthFlowCookie.RETURN_URL];
230
+ response.cookies.set(AuthFlowCookie.RETURN_URL, "", {
231
+ path: cookieConfig?.path ?? resolvedConfigs.basePath ?? "/",
232
+ httpOnly: cookieConfig?.httpOnly ?? true,
233
+ secure: cookieConfig?.secure ?? true,
234
+ sameSite: cookieConfig?.sameSite ?? "strict",
235
+ maxAge: 0, // Immediately expire the cookie
236
+ });
237
+ logger.debug("[CALLBACK_HANDLER] Clearing deep link cookie after successful auth");
238
+ return response;
239
+ };
190
240
  if (result.redirectTo) {
191
- return redirectWithBasePath(config, CivicAuth.toAbsoluteUrl(urlDetectionRequest, result.redirectTo, appUrl));
241
+ const response = redirectWithBasePath(config, CivicAuth.toAbsoluteUrl(urlDetectionRequest, result.redirectTo, appUrl));
242
+ return clearDeepLinkCookie(response);
192
243
  }
193
244
  if (result.content) {
194
245
  // Handle both string content and object content
@@ -197,20 +248,7 @@ async function handleCallback(request, config) {
197
248
  status: 200,
198
249
  headers: { "Content-Type": "text/html" },
199
250
  });
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;
251
+ return clearDeepLinkCookie(response);
214
252
  }
215
253
  else {
216
254
  // Object content (JSON response)
@@ -218,7 +256,8 @@ async function handleCallback(request, config) {
218
256
  }
219
257
  }
220
258
  // Fallback redirect
221
- return NextResponse.redirect(CivicAuth.toAbsoluteUrl(urlDetectionRequest, "/", appUrl));
259
+ const response = NextResponse.redirect(CivicAuth.toAbsoluteUrl(urlDetectionRequest, "/", appUrl));
260
+ return clearDeepLinkCookie(response);
222
261
  }
223
262
  catch (error) {
224
263
  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 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 * 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);
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/nextjs/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,GAGjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EACL,cAAc,EACd,YAAY,EACZ,eAAe,EACf,WAAW,GAGZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAEzE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,MAA8B,EAC9B,OAAgB,EACR,EAAE;IACV,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACrE,OAAO,WAAW,CAAC,QAAQ,EAAE,CAAC;AAChC,CAAC,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC;IAErC,gCAAgC;IAChC,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAElE,6CAA6C;IAC7C,OAAO,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAgB,EAChB,QAAiB;IAEjB,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QACrD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,oDAAoD;IACpD,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAErD,kFAAkF;IAClF,oEAAoE;IACpE,IAAI,QAAQ,KAAK,iBAAiB,EAAE,CAAC;QACnC,4BAA4B;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;SAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,iBAAiB,GAAG,GAAG,CAAC,EAAE,CAAC;QACxD,kEAAkE;QAClE,OAAO,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,8EAA8E;IAC9E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,YAAY,GAAG,CACnB,OAAoB,EACpB,UAAkC,EAC1B,EAAE;IACV,mEAAmE;IACnE,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,UAAU,CAAC,OAAO,CAAC;IAC5B,CAAC;IAED,2DAA2D;IAC3D,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;AAChC,CAAC,CAAC;AAEF,iBAAiB;AACjB,YAAY;AACZ,QAAQ;AACR,UAAU;AACV,gBAAgB;AAChB,MAAM,SAAS,GAAG,CAAC,QAAgB,EAAE,WAAmB,EAAE,EAAE;IAC1D,MAAM,OAAO,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;IACvC,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAE,QAAkB,EAAE,EAAE,CAC5D,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;IACxB,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,OAAO,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC;AAEL;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAoB,EAAW,EAAE;IACpE,OAAO,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAAC,IAAY,EAAE,WAAmB,EAAE,EAAE,EAAE;IAClE,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC;AACF;;GAEG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,QAAgB,EAChB,UAAkC,EACzB,EAAE;IACX,mFAAmF;IACnF,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAClD,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAmC,CAAW,CACnE,CAAC;IAEF,6EAA6E;IAC7E,0FAA0F;IAC1F,8FAA8F;IAC9F,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC5E,MAAM,kBAAkB,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAChD,mBAAmB,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,CAAC,CAC9C,CAAC;IACF,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CACzC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,QAAQ,IAAI,QAAQ,KAAK,GAAG,IAAI,GAAG,KAAK,gBAAgB,CACzE,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;IAC3E,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,CACV,0DAA0D,EAC1D,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,OAAO,6BAA8B,SAAQ,aAAa;IAErD;IACC;IACA;IAHV,YACS,SAAmD,EAAE,EACpD,OAAoB,EACpB,QAAsB;QAE9B,KAAK,CAAC;YACJ,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QAPI,WAAM,GAAN,MAAM,CAA+C;QACpD,YAAO,GAAP,OAAO,CAAa;QACpB,aAAQ,GAAR,QAAQ,CAAc;IAMhC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,wEAAwE;QACxE,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;QAC1D,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAgB,CAAC,IAAI,EAAE,CAAC;QAC7D,MAAM,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC;QAE3C,iFAAiF;QACjF,mFAAmF;QACnF,IAAI,cAAc,IAAI,cAAc,KAAK,GAAG,EAAE,CAAC;YAC7C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAG,0BAA0B,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;YAC/D,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,+CAA+C;QAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,GAAG,CACP,GAAW,EACX,KAAa,EACb,oBAAkC;QAElC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAgB,CAAC,IAAI;YACxD,GAAG,IAAI,CAAC,QAAQ;SACjB,CAAC;QACF,MAAM,aAAa,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE3D,MAAM,iBAAiB,GAAG;YACxB,GAAG,cAAc;YACjB,GAAG,oBAAoB;YACvB,sDAAsD;YACtD,MAAM,EAAE,aAAa,CAAC,MAAM;YAC5B,QAAQ,EAAE,aAAa,CAAC,QAAQ;SACjC,CAAC;QAEF,2EAA2E;QAC3E,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,oEAAoE;QACpE,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAgB,CAAC,IAAI,EAAE,CAAC;QAE7D,gEAAgE;QAChE,IAAI,cAAc,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,EAAE;gBACjC,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,qBAAqB;gBAC3C,IAAI,EAAE,cAAc,CAAC,IAAI;aAC1B,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,QAAgB,EAChB,OAAoB,EACpB,UAAkC,EAC5B,EAAE;IACR,IAAI,QAAQ,KAAK,UAAU,CAAC,QAAQ,EAAE,CAAC;QACrC,OAAO;IACT,CAAC;IAED,mDAAmD;IACnD,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC5C,QAAgB,EAChB,UAAkC,EACzB,EAAE;IACX,yBAAyB;IACzB,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAChD,MAAM,CAAC,KAAK,CACV,sDAAsD,EACtD,QAAQ,CACT,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,yBAAyB;IACzB,IAAI,YAAY,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,kDAAkD,EAAE,QAAQ,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,cAA4B,EAC5B,UAAsC,EACtC,EAAE;IACF,MAAM,gBAAgB,GAAG;QACvB,GAAG,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC;QACjC,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;QAC7B,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;KAC/B,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE;QACrC,GAAG,EAAE,cAAc,CAAC,GAAG;QACvB,MAAM,EAAE,UAAU,CAAC,GAAG;KACvB,CAAC,CAAC;IACH,cAAc,EAAE,OAAO;SACpB,MAAM,EAAE;SACR,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CACjB,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAuB,CAAC,CAC1D;SACA,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;QAClB,MAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE,MAAM,CAAC,CAAC;QAC3D,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AACF;;GAEG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,KAAK,EAC5C,OAAoB,EACpB,OAAoB,EACpB,QAAsB,EACtB,OAAsC,EACtC,UAAkC,EACC,EAAE;IACrC,6CAA6C;IAC7C,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACnE,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAClD,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,gEAAgE;IAChE,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,UAAU,CAAC,QAAQ,EACnB,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,CAClC,CAAC;IACF,MAAM,WAAW,GAAG,GAAG,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC7C,MAAM,wBAAwB,GAAG,sBAAsB,CACrD,QAAQ,CAAC,QAAQ,EACjB,UAAU,CAAC,QAAQ,CACpB,CAAC;IAEF,gFAAgF;IAChF,mGAAmG;IACnG,gJAAgJ;IAChJ,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,wBAAwB,EAAE,CAAC;QAC1D,MAAM,CAAC,KAAK,CACV,mDAAmD,WAAW,GAAG,CAClE,CAAC;QAEF,4EAA4E;QAC5E,IAAI,UAAU,CAAC,gBAAgB,KAAK,UAAU,EAAE,CAAC;YAC/C,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YACpD,2CAA2C;YAC3C,MAAM,QAAQ,GACZ,OAAO,CAAC,OAAO,CAAC,QAAQ;gBACxB,OAAO,CAAC,OAAO,CAAC,MAAM;gBACtB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;YAEvB,mEAAmE;YACnE,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAElE,IAAI,kBAAkB,EAAE,CAAC;gBACvB,MAAM,CAAC,KAAK,CACV,4BAA4B,kBAAkB,0BAA0B,CACzE,CAAC;gBACF,MAAM,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;QAED,MAAM,kBAAkB,GAAG,oBAAoB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QACzE,0DAA0D;QAC1D,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YAC3C,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QACH,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAW,EAAE,QAAgB,EAAE,EAAE;IAC/D,QAAQ,GAAG,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,qBAAqB;IAExE,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE5C,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,CAAC,CAAC,QAAQ;gBACR,QAAQ,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACpE,CAAC;QACD,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IACtB,CAAC;IAED,OAAO,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;QAC7B,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,QAAQ,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AACxD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,MAAkB,EAClB,SAAiB,EACH,EAAE,CAChB,YAAY,CAAC,QAAQ,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC","sourcesContent":["import {\n systemUrlsConfig,\n type AuthConfig,\n type AuthConfigWithDefaults,\n} from \"@/nextjs/config.js\";\nimport type { NextRequest } from \"next/server.js\";\nimport { NextResponse } from \"next/server.js\";\nimport type { SessionData } from \"@/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport picomatch from \"picomatch\";\nimport {\n AuthFlowCookie,\n CodeVerifier,\n OAuthTokenTypes,\n UserStorage,\n type CookieConfig,\n type KeySetter,\n} from \"@/shared/lib/types.js\";\nimport {\n clearTokens,\n getCookieConfiguration,\n sanitizeReturnUrl,\n} from \"@/shared/lib/util.js\";\nimport { CookieStorage } from \"@/server/index.js\";\nimport { extractCookieFromRawHeader } from \"@/shared/lib/cookieUtils.js\";\n\nconst logger = loggers.nextjs.middleware;\n\nexport const resolveCallbackUrl = (\n config: AuthConfigWithDefaults,\n baseUrl?: string,\n): string => {\n const callbackUrl = new URL(config?.callbackUrl, baseUrl).toString();\n return callbackUrl.toString();\n};\n\nexport function sanitizeBasePath(path: string): string {\n if (!path || path === \"/\") return \"\";\n\n // Ensure it starts with a slash\n const withLeadingSlash = path.startsWith(\"/\") ? path : `/${path}`;\n\n // Remove all trailing slashes (not just one)\n return withLeadingSlash.replace(/\\/+$/, \"\");\n}\n\n/**\n * Removes the basePath prefix from a pathname, properly handling edge cases\n * This is the inverse operation of adding basePath to a URL\n */\nexport function removeBasePathFromPath(\n pathname: string,\n basePath?: string,\n): string {\n if (!basePath || basePath === \"\" || basePath === \"/\") {\n return pathname;\n }\n\n // Sanitize the basePath to ensure consistent format\n const sanitizedBasePath = sanitizeBasePath(basePath);\n\n // Check if pathname starts with the basePath followed by a slash or end of string\n // This prevents partial matches like \"/app\" matching \"/application\"\n if (pathname === sanitizedBasePath) {\n // Exact match - return root\n return \"/\";\n } else if (pathname.startsWith(sanitizedBasePath + \"/\")) {\n // basePath followed by slash - remove basePath but keep the slash\n return pathname.slice(sanitizedBasePath.length);\n }\n\n // If basePath doesn't match as a complete path segment, return pathname as-is\n return pathname;\n}\n\nconst getOriginUrl = (\n request: NextRequest,\n authConfig: AuthConfigWithDefaults,\n): string => {\n // Use configured baseUrl if provided (for reverse proxy scenarios)\n if (authConfig.baseUrl) {\n return authConfig.baseUrl;\n }\n\n // Fallback to nextUrl.origin (includes port automatically)\n return request.nextUrl.origin;\n};\n\n// Matches globs:\n// Examples:\n// /user\n// /user/*\n// /user/**/info\nconst matchGlob = (pathname: string, globPattern: string) => {\n const matches = picomatch(globPattern);\n return matches(pathname);\n};\n\nconst matchesGlobs = (pathname: string, patterns: string[]) =>\n patterns.some((pattern) => {\n if (!pattern) return false;\n return matchGlob(pathname, pattern);\n });\n\n/**\n * Determines if we should attempt token refresh based on session state\n */\nexport const shouldAttemptRefresh = (session: SessionData): boolean => {\n return !session.authenticated && !!session.refreshToken;\n};\n\nconst stripBasePathPrefix = (path: string, basePath: string = \"\") => {\n return path.replace(new RegExp(`^${basePath}`), \"\");\n};\n/**\n * Checks if the current path is a system URL that should skip auth\n */\nexport const shouldSkipAuthForSystemUrls = (\n pathname: string,\n authConfig: AuthConfigWithDefaults,\n): boolean => {\n // make an array of all system URLs from authConfig using the systemUrlsConfig keys\n const systemUrls = Object.keys(systemUrlsConfig).map(\n (key) => authConfig[key as keyof AuthConfigWithDefaults] as string,\n );\n\n // check if any of the urls in systemUrls has a substring match with pathname\n // the systemUrl or incoming path could have a basePath, i.e. /dashboard/api/auth/callback\n // therefore we check if the systemUrl equals the pathname after stripping the basePath prefix\n const strippedPathname = stripBasePathPrefix(pathname, authConfig.basePath);\n const strippedSystemUrls = systemUrls.map((url) =>\n stripBasePathPrefix(url, authConfig.basePath),\n );\n const isSystemUrl = strippedSystemUrls.some(\n (url) => url && pathname && pathname !== \"/\" && url === strippedPathname,\n );\n logger.debug(\"→ isSystemUrl check\", { pathname, isSystemUrl, systemUrls });\n if (isSystemUrl) {\n logger.debug(\n \"→ Skipping auth check - this a URL defined in authConfig\",\n pathname,\n );\n }\n\n return isSystemUrl;\n};\n\n/**\n * CookieStorage implementation for NextJS middleware context that works with NextRequest\n */\nexport class NextjsMiddlewareCookieStorage extends CookieStorage {\n constructor(\n public config: Partial<Record<KeySetter, CookieConfig>> = {},\n private request: NextRequest,\n private response: NextResponse,\n ) {\n super({\n secure: true,\n httpOnly: true,\n });\n }\n\n async get(key: string): Promise<string | null> {\n // First try to get cookies from the response if it has already been set\n const cookieValue = this.response.cookies.get(key)?.value;\n if (cookieValue) {\n return cookieValue;\n }\n\n const cookieSettings = this.config?.[key as KeySetter] || {};\n const configuredPath = cookieSettings.path;\n\n // If we have a non-root basePath, use raw header parsing to get the first cookie\n // which should be from the most specific path, avoiding duplicate cookie conflicts\n if (configuredPath && configuredPath !== \"/\") {\n const cookieHeader = this.request.headers.get(\"cookie\");\n const rawValue = extractCookieFromRawHeader(cookieHeader, key);\n if (rawValue) {\n return rawValue;\n }\n }\n\n // Fallback to standard Next.js request cookies\n return this.request.cookies.get(key)?.value || null;\n }\n\n async set(\n key: string,\n value: string,\n cookieConfigOverride: CookieConfig,\n ): Promise<void> {\n const cookieSettings = this.config?.[key as KeySetter] || {\n ...this.settings,\n };\n const dynamicConfig = getCookieConfiguration(this.request);\n\n const useCookieSettings = {\n ...cookieSettings,\n ...cookieConfigOverride,\n // Apply dynamic configuration for secure and sameSite\n secure: dynamicConfig.secure,\n sameSite: dynamicConfig.sameSite,\n };\n\n // Respect the httpOnly setting from configuration instead of hardcoding it\n this.response.cookies.set(key, value, useCookieSettings);\n }\n\n async delete(key: string): Promise<void> {\n // Get cookie configuration for this key to respect the path setting\n const cookieSettings = this.config?.[key as KeySetter] || {};\n\n // If we have a path configured, use it when deleting the cookie\n if (cookieSettings.path) {\n this.response.cookies.set(key, \"\", {\n expires: new Date(0), // Expire in the past\n path: cookieSettings.path,\n });\n } else {\n this.response.cookies.delete(key);\n }\n }\n}\n\n/**\n * Handles authentication logic specifically for the login URL\n * Provides logging for login URL access patterns\n */\nexport const handleLoginUrl = (\n pathname: string,\n session: SessionData,\n authConfig: AuthConfigWithDefaults,\n): void => {\n if (pathname !== authConfig.loginUrl) {\n return;\n }\n\n // We are on the login URL - log the access pattern\n if (session.authenticated) {\n logger.debug(`→ Authenticated user accessing login page`);\n } else {\n logger.debug(`→ Unauthenticated user accessing login page`);\n }\n};\n\n/**\n * Checks if the current path should skip auth based on include/exclude patterns\n */\nexport const shouldSkipAuthForRoutePatterns = (\n pathname: string,\n authConfig: AuthConfigWithDefaults,\n): boolean => {\n // Check include patterns\n if (!matchesGlobs(pathname, authConfig.include)) {\n logger.debug(\n \"→ Skipping auth check - path not in include patterns\",\n pathname,\n );\n return true;\n }\n\n // Check exclude patterns\n if (matchesGlobs(pathname, authConfig.exclude)) {\n logger.debug(\"→ Skipping auth check - path in exclude patterns\", pathname);\n return true;\n }\n\n return false;\n};\n\nexport const copyCivicCookies = (\n sourceResponse: NextResponse,\n targetCall: NextResponse | NextRequest,\n) => {\n const civicCookieNames = [\n ...Object.values(OAuthTokenTypes),\n ...Object.values(UserStorage),\n ...Object.values(CodeVerifier),\n ];\n logger.debug(\"Copying Civic cookies:\", {\n src: sourceResponse.url,\n target: targetCall.url,\n });\n sourceResponse?.cookies\n .getAll()\n .filter((cookie) =>\n civicCookieNames.includes(cookie.name as OAuthTokenTypes),\n )\n .forEach((cookie) => {\n logger.debug(\"Setting middlewareResponse cookie:\", cookie);\n targetCall.cookies.set(cookie);\n });\n};\n/**\n * Handles final authentication logic for unauthenticated users on protected routes\n */\nexport const handleUnauthenticatedUser = async (\n session: SessionData,\n request: NextRequest,\n response: NextResponse,\n storage: NextjsMiddlewareCookieStorage,\n authConfig: AuthConfigWithDefaults,\n): Promise<NextResponse | undefined> => {\n // Clear expired/invalid tokens if they exist\n if (session.accessToken || session.idToken || session.refreshToken) {\n logger.debug(`→ Clearing expired/invalid tokens`);\n await clearTokens(storage);\n }\n\n // Final fallback: redirect to login unless we're already there.\n const loginUrl = new URL(\n authConfig.loginUrl,\n getOriginUrl(request, authConfig),\n );\n const redirectUrl = `${loginUrl.toString()}`;\n const loginPathWithoutBasePath = removeBasePathFromPath(\n loginUrl.pathname,\n authConfig.basePath,\n );\n\n // If we're already at the login URL, the middleware will just return undefined.\n // This is to prevent an infinite redirect loop if middleware is applied to the login route itself.\n // The loginUrl from getOriginUrl already includes the basePath, but request.nextUrl.pathname does not. So we strip it off to enable comparison.\n if (request.nextUrl.pathname !== loginPathWithoutBasePath) {\n logger.debug(\n `→ No valid tokens found - redirecting to login \"${redirectUrl}\"`,\n );\n\n // Preserve the original URL as a deep link for post-authentication redirect\n if (authConfig.deepLinkHandling !== \"disabled\") {\n const originUrl = getOriginUrl(request, authConfig);\n // Get the full path including query string\n const fullPath =\n request.nextUrl.pathname +\n request.nextUrl.search +\n request.nextUrl.hash;\n\n // Sanitize the return URL to prevent open redirect vulnerabilities\n const sanitizedReturnUrl = sanitizeReturnUrl(fullPath, originUrl);\n\n if (sanitizedReturnUrl) {\n logger.debug(\n `→ Preserving deep link: \"${sanitizedReturnUrl}\" for post-auth redirect`,\n );\n await storage.set(AuthFlowCookie.RETURN_URL, sanitizedReturnUrl, {});\n }\n }\n\n const redirectedResponse = redirectWithBasePath(authConfig, redirectUrl);\n // Copy any cookies that were set to the redirect response\n response.cookies.getAll().forEach((cookie) => {\n redirectedResponse.cookies.set(cookie);\n });\n return redirectedResponse;\n }\n\n return response;\n};\n\n/**\n * Prepends the basePath onto a given URL if it's not already there. Works for both relative and absolute URLs.\n * @param url\n * @param basePath\n * @returns\n */\nexport const prependBasePath = (url: string, basePath: string) => {\n basePath = \"/\" + basePath.replace(/^\\/|\\/$/g, \"\"); // normalize basePath\n\n const isAbsolute = /^https?:\\/\\//.test(url);\n\n if (isAbsolute) {\n const u = new URL(url);\n if (!u.pathname.startsWith(basePath)) {\n u.pathname =\n basePath + (u.pathname.startsWith(\"/\") ? \"\" : \"/\") + u.pathname;\n }\n return u.toString();\n }\n\n return url.startsWith(basePath)\n ? url\n : basePath + (url.startsWith(\"/\") ? \"\" : \"/\") + url;\n};\n\nexport const redirectWithBasePath = (\n config: AuthConfig,\n targetUrl: string,\n): NextResponse =>\n NextResponse.redirect(prependBasePath(targetUrl, config.basePath || \"\"));\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/nextjs/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n systemUrlsConfig,\n type AuthConfig,\n type AuthConfigWithDefaults,\n} from \"@/nextjs/config.js\";\nimport type { NextRequest } from \"next/server.js\";\nimport { NextResponse } from \"next/server.js\";\nimport type { SessionData } from \"@/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport picomatch from \"picomatch\";\nimport {\n AuthFlowCookie,\n CodeVerifier,\n OAuthTokenTypes,\n UserStorage,\n type CookieConfig,\n type KeySetter,\n} from \"@/shared/lib/types.js\";\nimport {\n clearTokens,\n getCookieConfiguration,\n sanitizeReturnUrl,\n} from \"@/shared/lib/util.js\";\n\n// Re-export for use by routeHandler\nexport { sanitizeReturnUrl };\nimport { CookieStorage } from \"@/server/index.js\";\nimport { extractCookieFromRawHeader } from \"@/shared/lib/cookieUtils.js\";\n\nconst logger = loggers.nextjs.middleware;\n\nexport const resolveCallbackUrl = (\n config: AuthConfigWithDefaults,\n baseUrl?: string,\n): string => {\n const callbackUrl = new URL(config?.callbackUrl, baseUrl).toString();\n return callbackUrl.toString();\n};\n\nexport function sanitizeBasePath(path: string): string {\n if (!path || path === \"/\") return \"\";\n\n // Ensure it starts with a slash\n const withLeadingSlash = path.startsWith(\"/\") ? path : `/${path}`;\n\n // Remove all trailing slashes (not just one)\n return withLeadingSlash.replace(/\\/+$/, \"\");\n}\n\n/**\n * Removes the basePath prefix from a pathname, properly handling edge cases\n * This is the inverse operation of adding basePath to a URL\n */\nexport function removeBasePathFromPath(\n pathname: string,\n basePath?: string,\n): string {\n if (!basePath || basePath === \"\" || basePath === \"/\") {\n return pathname;\n }\n\n // Sanitize the basePath to ensure consistent format\n const sanitizedBasePath = sanitizeBasePath(basePath);\n\n // Check if pathname starts with the basePath followed by a slash or end of string\n // This prevents partial matches like \"/app\" matching \"/application\"\n if (pathname === sanitizedBasePath) {\n // Exact match - return root\n return \"/\";\n } else if (pathname.startsWith(sanitizedBasePath + \"/\")) {\n // basePath followed by slash - remove basePath but keep the slash\n return pathname.slice(sanitizedBasePath.length);\n }\n\n // If basePath doesn't match as a complete path segment, return pathname as-is\n return pathname;\n}\n\nexport const getOriginUrl = (\n request: NextRequest,\n authConfig: AuthConfigWithDefaults,\n): string => {\n // Use configured baseUrl if provided (for reverse proxy scenarios)\n if (authConfig.baseUrl) {\n return authConfig.baseUrl;\n }\n\n // Fallback to nextUrl.origin (includes port automatically)\n return request.nextUrl.origin;\n};\n\n// Matches globs:\n// Examples:\n// /user\n// /user/*\n// /user/**/info\nconst matchGlob = (pathname: string, globPattern: string) => {\n const matches = picomatch(globPattern);\n return matches(pathname);\n};\n\nconst matchesGlobs = (pathname: string, patterns: string[]) =>\n patterns.some((pattern) => {\n if (!pattern) return false;\n return matchGlob(pathname, pattern);\n });\n\n/**\n * Determines if we should attempt token refresh based on session state\n */\nexport const shouldAttemptRefresh = (session: SessionData): boolean => {\n return !session.authenticated && !!session.refreshToken;\n};\n\nconst stripBasePathPrefix = (path: string, basePath: string = \"\") => {\n return path.replace(new RegExp(`^${basePath}`), \"\");\n};\n/**\n * Checks if the current path is a system URL that should skip auth\n */\nexport const shouldSkipAuthForSystemUrls = (\n pathname: string,\n authConfig: AuthConfigWithDefaults,\n): boolean => {\n // make an array of all system URLs from authConfig using the systemUrlsConfig keys\n const systemUrls = Object.keys(systemUrlsConfig).map(\n (key) => authConfig[key as keyof AuthConfigWithDefaults] as string,\n );\n\n // check if any of the urls in systemUrls has a substring match with pathname\n // the systemUrl or incoming path could have a basePath, i.e. /dashboard/api/auth/callback\n // therefore we check if the systemUrl equals the pathname after stripping the basePath prefix\n const strippedPathname = stripBasePathPrefix(pathname, authConfig.basePath);\n const strippedSystemUrls = systemUrls.map((url) =>\n stripBasePathPrefix(url, authConfig.basePath),\n );\n const isSystemUrl = strippedSystemUrls.some(\n (url) => url && pathname && pathname !== \"/\" && url === strippedPathname,\n );\n logger.debug(\"→ isSystemUrl check\", { pathname, isSystemUrl, systemUrls });\n if (isSystemUrl) {\n logger.debug(\n \"→ Skipping auth check - this a URL defined in authConfig\",\n pathname,\n );\n }\n\n return isSystemUrl;\n};\n\n/**\n * CookieStorage implementation for NextJS middleware context that works with NextRequest\n */\nexport class NextjsMiddlewareCookieStorage extends CookieStorage {\n constructor(\n public config: Partial<Record<KeySetter, CookieConfig>> = {},\n private request: NextRequest,\n private response: NextResponse,\n ) {\n super({\n secure: true,\n httpOnly: true,\n });\n }\n\n async get(key: string): Promise<string | null> {\n // First try to get cookies from the response if it has already been set\n const cookieValue = this.response.cookies.get(key)?.value;\n if (cookieValue) {\n return cookieValue;\n }\n\n const cookieSettings = this.config?.[key as KeySetter] || {};\n const configuredPath = cookieSettings.path;\n\n // If we have a non-root basePath, use raw header parsing to get the first cookie\n // which should be from the most specific path, avoiding duplicate cookie conflicts\n if (configuredPath && configuredPath !== \"/\") {\n const cookieHeader = this.request.headers.get(\"cookie\");\n const rawValue = extractCookieFromRawHeader(cookieHeader, key);\n if (rawValue) {\n return rawValue;\n }\n }\n\n // Fallback to standard Next.js request cookies\n return this.request.cookies.get(key)?.value || null;\n }\n\n async set(\n key: string,\n value: string,\n cookieConfigOverride: CookieConfig,\n ): Promise<void> {\n const cookieSettings = this.config?.[key as KeySetter] || {\n ...this.settings,\n };\n const dynamicConfig = getCookieConfiguration(this.request);\n\n const useCookieSettings = {\n ...cookieSettings,\n ...cookieConfigOverride,\n // Apply dynamic configuration for secure and sameSite\n secure: dynamicConfig.secure,\n sameSite: dynamicConfig.sameSite,\n };\n\n // Respect the httpOnly setting from configuration instead of hardcoding it\n this.response.cookies.set(key, value, useCookieSettings);\n }\n\n async delete(key: string): Promise<void> {\n // Get cookie configuration for this key to respect the path setting\n const cookieSettings = this.config?.[key as KeySetter] || {};\n\n // If we have a path configured, use it when deleting the cookie\n if (cookieSettings.path) {\n this.response.cookies.set(key, \"\", {\n maxAge: 0, // Immediately expire the cookie\n path: cookieSettings.path,\n });\n } else {\n this.response.cookies.delete(key);\n }\n }\n}\n\n/**\n * Handles authentication logic specifically for the login URL\n * Provides logging for login URL access patterns\n */\nexport const handleLoginUrl = (\n pathname: string,\n session: SessionData,\n authConfig: AuthConfigWithDefaults,\n): void => {\n if (pathname !== authConfig.loginUrl) {\n return;\n }\n\n // We are on the login URL - log the access pattern\n if (session.authenticated) {\n logger.debug(`→ Authenticated user accessing login page`);\n } else {\n logger.debug(`→ Unauthenticated user accessing login page`);\n }\n};\n\n/**\n * Checks if the current path should skip auth based on include/exclude patterns\n */\nexport const shouldSkipAuthForRoutePatterns = (\n pathname: string,\n authConfig: AuthConfigWithDefaults,\n): boolean => {\n // Check include patterns\n if (!matchesGlobs(pathname, authConfig.include)) {\n logger.debug(\n \"→ Skipping auth check - path not in include patterns\",\n pathname,\n );\n return true;\n }\n\n // Check exclude patterns\n if (matchesGlobs(pathname, authConfig.exclude)) {\n logger.debug(\"→ Skipping auth check - path in exclude patterns\", pathname);\n return true;\n }\n\n return false;\n};\n\nexport const copyCivicCookies = (\n sourceResponse: NextResponse,\n targetCall: NextResponse | NextRequest,\n) => {\n const civicCookieNames = [\n ...Object.values(OAuthTokenTypes),\n ...Object.values(UserStorage),\n ...Object.values(CodeVerifier),\n ];\n logger.debug(\"Copying Civic cookies:\", {\n src: sourceResponse.url,\n target: targetCall.url,\n });\n sourceResponse?.cookies\n .getAll()\n .filter((cookie) =>\n civicCookieNames.includes(cookie.name as OAuthTokenTypes),\n )\n .forEach((cookie) => {\n logger.debug(\"Setting middlewareResponse cookie:\", cookie);\n targetCall.cookies.set(cookie);\n });\n};\n/**\n * Handles final authentication logic for unauthenticated users on protected routes\n */\nexport const handleUnauthenticatedUser = async (\n session: SessionData,\n request: NextRequest,\n response: NextResponse,\n storage: NextjsMiddlewareCookieStorage,\n authConfig: AuthConfigWithDefaults,\n): Promise<NextResponse | undefined> => {\n // Clear expired/invalid tokens if they exist\n if (session.accessToken || session.idToken || session.refreshToken) {\n logger.debug(`→ Clearing expired/invalid tokens`);\n await clearTokens(storage);\n }\n\n // Final fallback: redirect to login unless we're already there.\n const loginUrl = new URL(\n authConfig.loginUrl,\n getOriginUrl(request, authConfig),\n );\n const redirectUrl = `${loginUrl.toString()}`;\n const loginPathWithoutBasePath = removeBasePathFromPath(\n loginUrl.pathname,\n authConfig.basePath,\n );\n\n // If we're already at the login URL, the middleware will just return undefined.\n // This is to prevent an infinite redirect loop if middleware is applied to the login route itself.\n // The loginUrl from getOriginUrl already includes the basePath, but request.nextUrl.pathname does not. So we strip it off to enable comparison.\n if (request.nextUrl.pathname !== loginPathWithoutBasePath) {\n logger.debug(\n `→ No valid tokens found - redirecting to login \"${redirectUrl}\"`,\n );\n\n // Preserve the original URL as a deep link for post-authentication redirect\n // Apply deepLinkHandling logic here to compute the final redirect URL\n if (authConfig.deepLinkHandling !== \"disabled\") {\n const originUrl = getOriginUrl(request, authConfig);\n // Get the full path including query string\n const fullPath =\n request.nextUrl.pathname +\n request.nextUrl.search +\n request.nextUrl.hash;\n\n // Sanitize the return URL to prevent open redirect vulnerabilities\n const sanitizedReturnUrl = sanitizeReturnUrl(fullPath, originUrl);\n\n if (sanitizedReturnUrl) {\n let returnTo: string;\n\n if (authConfig.deepLinkHandling === \"queryParamsOnly\") {\n // queryParamsOnly: Merge query params from original URL into loginSuccessUrl\n const loginSuccessUrl = authConfig.loginSuccessUrl || \"/\";\n try {\n const returnUrlObj = new URL(sanitizedReturnUrl, originUrl);\n\n // If no query params, use loginSuccessUrl directly\n if (returnUrlObj.searchParams.size === 0) {\n logger.debug(\n `→ deepLinkHandling=queryParamsOnly: no query params to preserve, using loginSuccessUrl \"${loginSuccessUrl}\"`,\n );\n returnTo = loginSuccessUrl;\n } else {\n const baseUrlObj = new URL(loginSuccessUrl, originUrl);\n\n // Append query params from original URL to loginSuccessUrl\n returnUrlObj.searchParams.forEach((value, key) => {\n baseUrlObj.searchParams.set(key, value);\n });\n\n returnTo =\n baseUrlObj.pathname + baseUrlObj.search + baseUrlObj.hash;\n logger.debug(\n `→ deepLinkHandling=queryParamsOnly: merged params into \"${returnTo}\"`,\n );\n }\n } catch {\n // If URL parsing fails, fall back to loginSuccessUrl\n logger.warn(\n `→ Failed to merge query params, falling back to loginSuccessUrl`,\n );\n returnTo = loginSuccessUrl;\n }\n } else {\n // fullUrl: Use the full path directly\n returnTo = sanitizedReturnUrl;\n logger.debug(\n `→ deepLinkHandling=fullUrl: preserving deep link \"${returnTo}\"`,\n );\n }\n\n // Set the cookie with the computed return URL\n await storage.set(AuthFlowCookie.RETURN_URL, returnTo, {});\n }\n }\n\n const redirectedResponse = redirectWithBasePath(authConfig, redirectUrl);\n // Copy any cookies that were set to the redirect response\n response.cookies.getAll().forEach((cookie) => {\n redirectedResponse.cookies.set(cookie);\n });\n return redirectedResponse;\n }\n\n return response;\n};\n\n/**\n * Prepends the basePath onto a given URL if it's not already there. Works for both relative and absolute URLs.\n * @param url\n * @param basePath\n * @returns\n */\nexport const prependBasePath = (url: string, basePath: string) => {\n basePath = \"/\" + basePath.replace(/^\\/|\\/$/g, \"\"); // normalize basePath\n\n const isAbsolute = /^https?:\\/\\//.test(url);\n\n if (isAbsolute) {\n const u = new URL(url);\n if (!u.pathname.startsWith(basePath)) {\n u.pathname =\n basePath + (u.pathname.startsWith(\"/\") ? \"\" : \"/\") + u.pathname;\n }\n return u.toString();\n }\n\n return url.startsWith(basePath)\n ? url\n : basePath + (url.startsWith(\"/\") ? \"\" : \"/\") + url;\n};\n\nexport const redirectWithBasePath = (\n config: AuthConfig,\n targetUrl: string,\n): NextResponse =>\n NextResponse.redirect(prependBasePath(targetUrl, config.basePath || \"\"));\n"]}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "@civic/auth:0.13.0-beta.0";
1
+ export declare const VERSION = "@civic/auth:0.13.0-beta.1";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // This is an auto-generated file. Do not edit.
2
- export const VERSION = "@civic/auth:0.13.0-beta.0";
2
+ export const VERSION = "@civic/auth:0.13.0-beta.1";
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/shared/version.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAE/C,MAAM,CAAC,MAAM,OAAO,GAAG,2BAA2B,CAAC","sourcesContent":["// This is an auto-generated file. Do not edit.\n\nexport const VERSION = \"@civic/auth:0.13.0-beta.0\";\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/shared/version.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAE/C,MAAM,CAAC,MAAM,OAAO,GAAG,2BAA2B,CAAC","sourcesContent":["// This is an auto-generated file. Do not edit.\n\nexport const VERSION = \"@civic/auth:0.13.0-beta.1\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@civic/auth",
3
- "version": "0.13.0-beta.0",
3
+ "version": "0.13.0-beta.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",