@civic/auth 0.15.4 → 0.15.5
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 +6 -0
- package/dist/nextjs/middleware.d.ts.map +1 -1
- package/dist/nextjs/middleware.js +1 -1
- package/dist/nextjs/middleware.js.map +1 -1
- package/dist/server/session.d.ts +1 -1
- package/dist/server/session.d.ts.map +1 -1
- package/dist/server/session.js +23 -3
- package/dist/server/session.js.map +1 -1
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/version.js +1 -1
- package/dist/shared/version.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
# 0.15.5 - Deep linking RSC/prefetch fix
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
- Fixed deep links being lost on Next.js navigations. RSC prefetches (`Next-Router-Prefetch: 1`) and RSC navigations (`RSC: 1`) are speculative requests that were consuming the one-shot `AUTH_REDIRECT_MARKER` and clearing the `RETURN_URL` cookie, racing the real navigation that follows so the deeplink was dropped and the user landed on the basePath root instead of their original destination. `handleDeepLinkingAtLoginUrl` now detects these headers and returns early (mirroring the existing non-GET skip), and `handleDeepLinking` accepts an optional request-headers argument passed through from the Next.js middleware.
|
|
5
|
+
- Made the request-header lookup case-insensitive on the plain-record path in `handleDeepLinkingAtLoginUrl`.
|
|
6
|
+
|
|
1
7
|
# 0.15.4 - Next.js Emotion SSR Support
|
|
2
8
|
|
|
3
9
|
### Features
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/nextjs/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAa9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAM7D,KAAK,UAAU,GAAG,CAChB,OAAO,EAAE,WAAW,KACjB,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC;AAE1C;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACtC,wBAAwB,sBAAsB,EAC9C,SAAS,aAAa,KACrB,OAAO,CAAC,WAAW,CAgBrB,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/nextjs/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAa9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAM7D,KAAK,UAAU,GAAG,CAChB,OAAO,EAAE,WAAW,KACjB,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC;AAE1C;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACtC,wBAAwB,sBAAsB,EAC9C,SAAS,aAAa,KACrB,OAAO,CAAC,WAAW,CAgBrB,CAAC;AAgIF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GACxB,aAAY,kBAAuB,MAC7B,SAAS,WAAW,KAAG,OAAO,CAAC,YAAY,CAOjD,CAAC;AAEJ;;;;;;;GAOG;AAEH,wBAAgB,QAAQ,CACtB,UAAU,EAAE,UAAU,GACrB,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAEjD;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,UAAU,GAAE,kBAAuB,IAEpD,YAAY,UAAU,KACrB,CAAC,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC,CA2BrD"}
|
|
@@ -96,7 +96,7 @@ const applyAuth = async (authConfig, request) => {
|
|
|
96
96
|
});
|
|
97
97
|
const originUrl = getOriginUrl(request, authConfigWithDefaults);
|
|
98
98
|
const requestUrl = `${originUrl}${request.nextUrl.pathname}${request.nextUrl.search}${request.nextUrl.hash}`;
|
|
99
|
-
await civicAuth.handleDeepLinking(requestUrl, originUrl, request.method);
|
|
99
|
+
await civicAuth.handleDeepLinking(requestUrl, originUrl, request.method, request.headers);
|
|
100
100
|
}
|
|
101
101
|
// Step 3: Handle login URL with special logic
|
|
102
102
|
if (pathNameIsLoginUrl) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/nextjs/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Authenticates the user on all requests by checking the token cookie\n *\n * Usage:\n * Option 1: use if no other middleware (e.g. no next-intl etc)\n * export default authMiddleware();\n *\n * Option 2: use if other middleware is needed - default auth config\n * export default withAuth((request) => {\n * logger.debug('in custom middleware', request.nextUrl.pathname);\n * return NextResponse.next();\n * })\n *\n * Option 3: use if other middleware is needed - specifying auth config\n * const withCivicAuth = auth({ loginUrl: '/login', include: ['/[.*]/user'] })\n * export default withCivicAuth((request) => {\n * logger.debug('in custom middleware', request.url);\n * return NextResponse.next();\n * })\n *\n */\nimport type { NextRequest } from \"next/server.js\";\nimport { NextResponse } from \"next/server.js\";\nimport type {\n AuthConfigWithDefaults,\n OptionalAuthConfig,\n} from \"@/nextjs/config.js\";\nimport { resolveAuthConfig } from \"@/nextjs/config.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\nimport type { SessionData } from \"@/types.js\";\nimport { HybridAuthStorage } from \"./HybridAuthStorage.js\";\nimport {\n shouldAttemptRefresh,\n shouldSkipAuthForSystemUrls,\n handleLoginUrl,\n shouldSkipAuthForRoutePatterns,\n handleUnauthenticatedUser,\n copyCivicCookies,\n getOriginUrl,\n removeBasePathFromPath,\n} from \"./utils.js\";\nimport { NextjsMiddlewareCookieStorage } from \"./utils.js\";\nimport type { CookieStorage } from \"@/shared/lib/storage.js\";\nimport { UserStorage } from \"@/shared/lib/types.js\";\nimport { CivicAuth } from \"@/server/session.js\";\n\nconst logger = loggers.nextjs.middleware;\n\ntype Middleware = (\n request: NextRequest,\n) => Promise<NextResponse> | NextResponse;\n\n/**\n * use a ServerAuthenticationResolver to validate the existing session\n * using any CookieStorage implementation\n * @param authConfigWithDefaults\n * @param storage CookieStorage implementation (cookie, hybrid, etc.)\n * @returns {Promise<SessionData>}\n */\nexport const validateAuthTokensIfPresent = async (\n authConfigWithDefaults: AuthConfigWithDefaults,\n storage: CookieStorage,\n): Promise<SessionData> => {\n try {\n const authSessionService = await ServerAuthenticationResolver.build(\n {\n ...authConfigWithDefaults,\n redirectUrl: authConfigWithDefaults.callbackUrl,\n },\n storage,\n );\n // validate the existing session and rehydrate and update cookies to the response if necessary\n const existingSession = await authSessionService.validateExistingSession();\n return existingSession;\n } catch (error) {\n logger.error(\"Error validating tokens\", error);\n return { authenticated: false };\n }\n};\n\n// internal - used by all exported functions\n/**\n * Core authentication middleware logic.\n *\n * The Authentication Story:\n * 1. Validate tokens to understand current authentication state\n * 2. Attempt token refresh if needed (the OAuth pipeline approach)\n * 3. Apply route-specific authentication rules based on final state\n */\nconst applyAuth = async (\n authConfig: OptionalAuthConfig,\n request: NextRequest,\n): Promise<NextResponse | undefined> => {\n const authConfigWithDefaults = resolveAuthConfig(authConfig);\n const response = NextResponse.next({\n request, // ensure incoming request headers get propagated\n });\n const cookieConfig = {\n ...authConfigWithDefaults?.cookies?.tokens,\n [UserStorage.USER]: authConfigWithDefaults?.cookies?.user || {},\n };\n logger.debug(\"Incoming request:\", {\n pathName: request.nextUrl.pathname,\n method: request.method,\n });\n // Use HybridAuthStorage to support both bearer tokens and cookies\n const storage =\n authConfigWithDefaults.enableBearerToken !== false\n ? new HybridAuthStorage(request, response, cookieConfig)\n : new NextjsMiddlewareCookieStorage(cookieConfig, request, response);\n // check if the incoming path is a system URL\n const shouldSkipAuthForSystemUrlsCheck = shouldSkipAuthForSystemUrls(\n request.nextUrl.pathname,\n authConfigWithDefaults,\n );\n\n // Skip authentication for system URLs (callback, challenge, logout)\n // we don't want to validate the session in this case as this would\n // auto-hydrate and potential conflict with the logic of the api call\n if (shouldSkipAuthForSystemUrlsCheck) {\n return response;\n }\n\n // Step 1: Understand the current authentication state\n const session = await validateAuthTokensIfPresent(\n authConfigWithDefaults,\n storage,\n );\n\n const shouldSkipAuthForRoutePatternsCheck = shouldSkipAuthForRoutePatterns(\n request.nextUrl.pathname,\n authConfigWithDefaults,\n );\n\n // Normalize loginUrl for comparison (remove basePath if present)\n const loginPathWithoutBasePath = removeBasePathFromPath(\n authConfigWithDefaults.loginUrl,\n authConfigWithDefaults.basePath,\n );\n const pathNameIsLoginUrl =\n request.nextUrl.pathname === loginPathWithoutBasePath;\n\n logger.debug(\"Authentication state:\", {\n authenticated: session.authenticated,\n pathName: request.nextUrl.pathname,\n pathNameIsLoginUrl,\n shouldAttemptRefresh: shouldAttemptRefresh(session),\n shouldSkipAuthForSystemUrls: shouldSkipAuthForSystemUrlsCheck,\n shouldSkipAuthForRoutePatterns: shouldSkipAuthForRoutePatternsCheck,\n });\n\n // Skip authentication for routes not matching include/exclude patterns\n // This must happen BEFORE deep linking to prevent capturing excluded paths\n // (e.g., analytics proxy paths like /ga/*, /gtm/*, /ingest/*) as return URLs\n // BUT we still allow the login URL through, as it needs deep linking support\n if (shouldSkipAuthForRoutePatternsCheck && !pathNameIsLoginUrl) {\n return response;\n }\n\n // Step 2: Handle deep link cookie for unauthenticated users using CivicAuth\n // handleDeepLinking automatically detects if we're at loginUrl or a protected route\n // and applies the appropriate logic for each case\n // Note: handleDeepLinking internally skips non-GET requests (POST/PUT/DELETE etc.)\n if (!session.authenticated) {\n const civicAuth = new CivicAuth(storage, {\n ...authConfigWithDefaults,\n redirectUrl: authConfigWithDefaults.callbackUrl,\n });\n const originUrl = getOriginUrl(request, authConfigWithDefaults);\n const requestUrl = `${originUrl}${request.nextUrl.pathname}${request.nextUrl.search}${request.nextUrl.hash}`;\n\n await civicAuth.handleDeepLinking(requestUrl, originUrl, request.method);\n }\n\n // Step 3: Handle login URL with special logic\n if (pathNameIsLoginUrl) {\n handleLoginUrl(request.nextUrl.pathname, session, authConfigWithDefaults);\n return response; // Always allow access to login URL\n }\n\n // Handle unauthenticated users on protected routes\n if (!session.authenticated) {\n return handleUnauthenticatedUser(\n session,\n request,\n response,\n storage,\n authConfigWithDefaults,\n );\n }\n\n // Happy ending - authentication passed\n logger.debug(\"→ Authentication successful, allowing access to\", response.url);\n copyCivicCookies(response, request);\n const authenticatedResponse = NextResponse.next({\n request, // ensure that the cookies get added to the request as well as the response\n });\n copyCivicCookies(response, authenticatedResponse);\n return authenticatedResponse;\n};\n\n/**\n *\n * Use this when auth is the only middleware you need.\n * Usage:\n *\n * export default authMiddleware({ loginUrl = '/login' }); // or just authMiddleware();\n *\n */\nexport const authMiddleware =\n (authConfig: OptionalAuthConfig = {}) =>\n async (request: NextRequest): Promise<NextResponse> => {\n const response = await applyAuth(authConfig, request);\n if (response) return response;\n\n // NextJS doesn't do middleware chaining yet, so this does not mean\n // \"call the next middleware\" - it means \"continue to the route handler\"\n return NextResponse.next();\n };\n\n/**\n * Usage:\n *\n * export default withAuth(async (request) => {\n * logger.debug('my middleware');\n * return NextResponse.next();\n * })\n */\n// use this when you have your own middleware to chain\nexport function withAuth(\n middleware: Middleware,\n): (request: NextRequest) => Promise<NextResponse> {\n return auth()(middleware);\n}\n\nconst isRedirectResponse = (response?: NextResponse) =>\n response && response.status === 307;\n\n/**\n * Use this when you want to configure the middleware here (an alternative is to do it in the next.config file)\n *\n * Usage:\n *\n * export default auth(authConfig: AuthConfig ) => {\n * logger.debug('my middleware');\n * return NextResponse.next();\n * })\n *\n */\nexport function auth(authConfig: OptionalAuthConfig = {}) {\n return (\n middleware: Middleware,\n ): ((request: NextRequest) => Promise<NextResponse>) => {\n return async (request: NextRequest): Promise<NextResponse> => {\n const response = await applyAuth(authConfig, request);\n // if the response is redirected it means that the user is not authenticated\n // so we skip the rest of the custom middleware to redirect to the login page\n if (response && isRedirectResponse(response)) {\n logger.debug(\n \"User is unauthenticated, redirecting to \",\n response.headers.get(\"location\"),\n );\n return response;\n }\n // ensure requests get the cookies in case of redirects\n if (response) {\n copyCivicCookies(response, request);\n }\n const middlewareResponse = await middleware(request);\n\n // we need to ensure that the civic cookies that were potentially\n // added by CivicAuth are set on the final response after all\n // middleware has been run\n if (response) {\n copyCivicCookies(response, middlewareResponse);\n }\n return middlewareResponse;\n };\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/nextjs/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Authenticates the user on all requests by checking the token cookie\n *\n * Usage:\n * Option 1: use if no other middleware (e.g. no next-intl etc)\n * export default authMiddleware();\n *\n * Option 2: use if other middleware is needed - default auth config\n * export default withAuth((request) => {\n * logger.debug('in custom middleware', request.nextUrl.pathname);\n * return NextResponse.next();\n * })\n *\n * Option 3: use if other middleware is needed - specifying auth config\n * const withCivicAuth = auth({ loginUrl: '/login', include: ['/[.*]/user'] })\n * export default withCivicAuth((request) => {\n * logger.debug('in custom middleware', request.url);\n * return NextResponse.next();\n * })\n *\n */\nimport type { NextRequest } from \"next/server.js\";\nimport { NextResponse } from \"next/server.js\";\nimport type {\n AuthConfigWithDefaults,\n OptionalAuthConfig,\n} from \"@/nextjs/config.js\";\nimport { resolveAuthConfig } from \"@/nextjs/config.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\nimport type { SessionData } from \"@/types.js\";\nimport { HybridAuthStorage } from \"./HybridAuthStorage.js\";\nimport {\n shouldAttemptRefresh,\n shouldSkipAuthForSystemUrls,\n handleLoginUrl,\n shouldSkipAuthForRoutePatterns,\n handleUnauthenticatedUser,\n copyCivicCookies,\n getOriginUrl,\n removeBasePathFromPath,\n} from \"./utils.js\";\nimport { NextjsMiddlewareCookieStorage } from \"./utils.js\";\nimport type { CookieStorage } from \"@/shared/lib/storage.js\";\nimport { UserStorage } from \"@/shared/lib/types.js\";\nimport { CivicAuth } from \"@/server/session.js\";\n\nconst logger = loggers.nextjs.middleware;\n\ntype Middleware = (\n request: NextRequest,\n) => Promise<NextResponse> | NextResponse;\n\n/**\n * use a ServerAuthenticationResolver to validate the existing session\n * using any CookieStorage implementation\n * @param authConfigWithDefaults\n * @param storage CookieStorage implementation (cookie, hybrid, etc.)\n * @returns {Promise<SessionData>}\n */\nexport const validateAuthTokensIfPresent = async (\n authConfigWithDefaults: AuthConfigWithDefaults,\n storage: CookieStorage,\n): Promise<SessionData> => {\n try {\n const authSessionService = await ServerAuthenticationResolver.build(\n {\n ...authConfigWithDefaults,\n redirectUrl: authConfigWithDefaults.callbackUrl,\n },\n storage,\n );\n // validate the existing session and rehydrate and update cookies to the response if necessary\n const existingSession = await authSessionService.validateExistingSession();\n return existingSession;\n } catch (error) {\n logger.error(\"Error validating tokens\", error);\n return { authenticated: false };\n }\n};\n\n// internal - used by all exported functions\n/**\n * Core authentication middleware logic.\n *\n * The Authentication Story:\n * 1. Validate tokens to understand current authentication state\n * 2. Attempt token refresh if needed (the OAuth pipeline approach)\n * 3. Apply route-specific authentication rules based on final state\n */\nconst applyAuth = async (\n authConfig: OptionalAuthConfig,\n request: NextRequest,\n): Promise<NextResponse | undefined> => {\n const authConfigWithDefaults = resolveAuthConfig(authConfig);\n const response = NextResponse.next({\n request, // ensure incoming request headers get propagated\n });\n const cookieConfig = {\n ...authConfigWithDefaults?.cookies?.tokens,\n [UserStorage.USER]: authConfigWithDefaults?.cookies?.user || {},\n };\n logger.debug(\"Incoming request:\", {\n pathName: request.nextUrl.pathname,\n method: request.method,\n });\n // Use HybridAuthStorage to support both bearer tokens and cookies\n const storage =\n authConfigWithDefaults.enableBearerToken !== false\n ? new HybridAuthStorage(request, response, cookieConfig)\n : new NextjsMiddlewareCookieStorage(cookieConfig, request, response);\n // check if the incoming path is a system URL\n const shouldSkipAuthForSystemUrlsCheck = shouldSkipAuthForSystemUrls(\n request.nextUrl.pathname,\n authConfigWithDefaults,\n );\n\n // Skip authentication for system URLs (callback, challenge, logout)\n // we don't want to validate the session in this case as this would\n // auto-hydrate and potential conflict with the logic of the api call\n if (shouldSkipAuthForSystemUrlsCheck) {\n return response;\n }\n\n // Step 1: Understand the current authentication state\n const session = await validateAuthTokensIfPresent(\n authConfigWithDefaults,\n storage,\n );\n\n const shouldSkipAuthForRoutePatternsCheck = shouldSkipAuthForRoutePatterns(\n request.nextUrl.pathname,\n authConfigWithDefaults,\n );\n\n // Normalize loginUrl for comparison (remove basePath if present)\n const loginPathWithoutBasePath = removeBasePathFromPath(\n authConfigWithDefaults.loginUrl,\n authConfigWithDefaults.basePath,\n );\n const pathNameIsLoginUrl =\n request.nextUrl.pathname === loginPathWithoutBasePath;\n\n logger.debug(\"Authentication state:\", {\n authenticated: session.authenticated,\n pathName: request.nextUrl.pathname,\n pathNameIsLoginUrl,\n shouldAttemptRefresh: shouldAttemptRefresh(session),\n shouldSkipAuthForSystemUrls: shouldSkipAuthForSystemUrlsCheck,\n shouldSkipAuthForRoutePatterns: shouldSkipAuthForRoutePatternsCheck,\n });\n\n // Skip authentication for routes not matching include/exclude patterns\n // This must happen BEFORE deep linking to prevent capturing excluded paths\n // (e.g., analytics proxy paths like /ga/*, /gtm/*, /ingest/*) as return URLs\n // BUT we still allow the login URL through, as it needs deep linking support\n if (shouldSkipAuthForRoutePatternsCheck && !pathNameIsLoginUrl) {\n return response;\n }\n\n // Step 2: Handle deep link cookie for unauthenticated users using CivicAuth\n // handleDeepLinking automatically detects if we're at loginUrl or a protected route\n // and applies the appropriate logic for each case\n // Note: handleDeepLinking internally skips non-GET requests (POST/PUT/DELETE etc.)\n if (!session.authenticated) {\n const civicAuth = new CivicAuth(storage, {\n ...authConfigWithDefaults,\n redirectUrl: authConfigWithDefaults.callbackUrl,\n });\n const originUrl = getOriginUrl(request, authConfigWithDefaults);\n const requestUrl = `${originUrl}${request.nextUrl.pathname}${request.nextUrl.search}${request.nextUrl.hash}`;\n\n await civicAuth.handleDeepLinking(\n requestUrl,\n originUrl,\n request.method,\n request.headers,\n );\n }\n\n // Step 3: Handle login URL with special logic\n if (pathNameIsLoginUrl) {\n handleLoginUrl(request.nextUrl.pathname, session, authConfigWithDefaults);\n return response; // Always allow access to login URL\n }\n\n // Handle unauthenticated users on protected routes\n if (!session.authenticated) {\n return handleUnauthenticatedUser(\n session,\n request,\n response,\n storage,\n authConfigWithDefaults,\n );\n }\n\n // Happy ending - authentication passed\n logger.debug(\"→ Authentication successful, allowing access to\", response.url);\n copyCivicCookies(response, request);\n const authenticatedResponse = NextResponse.next({\n request, // ensure that the cookies get added to the request as well as the response\n });\n copyCivicCookies(response, authenticatedResponse);\n return authenticatedResponse;\n};\n\n/**\n *\n * Use this when auth is the only middleware you need.\n * Usage:\n *\n * export default authMiddleware({ loginUrl = '/login' }); // or just authMiddleware();\n *\n */\nexport const authMiddleware =\n (authConfig: OptionalAuthConfig = {}) =>\n async (request: NextRequest): Promise<NextResponse> => {\n const response = await applyAuth(authConfig, request);\n if (response) return response;\n\n // NextJS doesn't do middleware chaining yet, so this does not mean\n // \"call the next middleware\" - it means \"continue to the route handler\"\n return NextResponse.next();\n };\n\n/**\n * Usage:\n *\n * export default withAuth(async (request) => {\n * logger.debug('my middleware');\n * return NextResponse.next();\n * })\n */\n// use this when you have your own middleware to chain\nexport function withAuth(\n middleware: Middleware,\n): (request: NextRequest) => Promise<NextResponse> {\n return auth()(middleware);\n}\n\nconst isRedirectResponse = (response?: NextResponse) =>\n response && response.status === 307;\n\n/**\n * Use this when you want to configure the middleware here (an alternative is to do it in the next.config file)\n *\n * Usage:\n *\n * export default auth(authConfig: AuthConfig ) => {\n * logger.debug('my middleware');\n * return NextResponse.next();\n * })\n *\n */\nexport function auth(authConfig: OptionalAuthConfig = {}) {\n return (\n middleware: Middleware,\n ): ((request: NextRequest) => Promise<NextResponse>) => {\n return async (request: NextRequest): Promise<NextResponse> => {\n const response = await applyAuth(authConfig, request);\n // if the response is redirected it means that the user is not authenticated\n // so we skip the rest of the custom middleware to redirect to the login page\n if (response && isRedirectResponse(response)) {\n logger.debug(\n \"User is unauthenticated, redirecting to \",\n response.headers.get(\"location\"),\n );\n return response;\n }\n // ensure requests get the cookies in case of redirects\n if (response) {\n copyCivicCookies(response, request);\n }\n const middlewareResponse = await middleware(request);\n\n // we need to ensure that the civic cookies that were potentially\n // added by CivicAuth are set on the final response after all\n // middleware has been run\n if (response) {\n copyCivicCookies(response, middlewareResponse);\n }\n return middlewareResponse;\n };\n };\n}\n"]}
|
package/dist/server/session.d.ts
CHANGED
|
@@ -118,7 +118,7 @@ export declare class CivicAuth {
|
|
|
118
118
|
* }
|
|
119
119
|
* ```
|
|
120
120
|
*/
|
|
121
|
-
handleDeepLinking(requestUrl: string, originUrl: string, method: string): Promise<string | null>;
|
|
121
|
+
handleDeepLinking(requestUrl: string, originUrl: string, method: string, headers?: Headers | Record<string, string>): Promise<string | null>;
|
|
122
122
|
/**
|
|
123
123
|
* Internal: Handles deep linking when at a protected route.
|
|
124
124
|
* Sets the cookie to capture the user's intended destination and the auth redirect marker.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"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"}
|
package/dist/server/session.js
CHANGED
|
@@ -266,7 +266,7 @@ export class CivicAuth {
|
|
|
266
266
|
* }
|
|
267
267
|
* ```
|
|
268
268
|
*/
|
|
269
|
-
async handleDeepLinking(requestUrl, originUrl, method) {
|
|
269
|
+
async handleDeepLinking(requestUrl, originUrl, method, headers) {
|
|
270
270
|
const logger = loggers.server;
|
|
271
271
|
// Only handle deep-links for GET requests (user navigations)
|
|
272
272
|
// POST/PUT/DELETE are programmatic actions, not user navigation intent
|
|
@@ -315,7 +315,7 @@ export class CivicAuth {
|
|
|
315
315
|
});
|
|
316
316
|
if (isAtLoginUrl) {
|
|
317
317
|
// At login URL - use the updateDeepLinkCookie logic
|
|
318
|
-
return this.handleDeepLinkingAtLoginUrl(parsedUrl, originUrl);
|
|
318
|
+
return this.handleDeepLinkingAtLoginUrl(parsedUrl, originUrl, headers);
|
|
319
319
|
}
|
|
320
320
|
// At protected route - set the deep link cookie
|
|
321
321
|
return this.handleDeepLinkingAtProtectedRoute(parsedUrl, originUrl);
|
|
@@ -354,8 +354,28 @@ export class CivicAuth {
|
|
|
354
354
|
* Internal: Handles deep linking when at the login URL.
|
|
355
355
|
* Checks the auth redirect marker and handles appropriately.
|
|
356
356
|
*/
|
|
357
|
-
async handleDeepLinkingAtLoginUrl(parsedUrl, originUrl) {
|
|
357
|
+
async handleDeepLinkingAtLoginUrl(parsedUrl, originUrl, headers) {
|
|
358
358
|
const logger = loggers.server;
|
|
359
|
+
// RSC prefetches (Next-Router-Prefetch: 1) and RSC navigations (RSC: 1) are
|
|
360
|
+
// speculative requests issued by the Next.js router. They must not consume the
|
|
361
|
+
// one-shot AUTH_REDIRECT_MARKER or clear the RETURN_URL cookie, because doing
|
|
362
|
+
// so races with the real navigation that immediately follows.
|
|
363
|
+
const getHeader = (name) => {
|
|
364
|
+
if (headers instanceof Headers)
|
|
365
|
+
return headers.get(name);
|
|
366
|
+
if (!headers)
|
|
367
|
+
return null;
|
|
368
|
+
const lower = name.toLowerCase();
|
|
369
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
370
|
+
if (k.toLowerCase() === lower)
|
|
371
|
+
return v;
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
};
|
|
375
|
+
if (getHeader("Next-Router-Prefetch") === "1" || getHeader("RSC") === "1") {
|
|
376
|
+
logger.debug("[handleDeepLinking] Skipping RSC/prefetch request at login URL");
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
359
379
|
const deepLinkHandling = this.authConfig.deepLinkHandling ?? "queryParamsOnly";
|
|
360
380
|
const hasQueryParams = parsedUrl.searchParams.size > 0;
|
|
361
381
|
// Check and clean up the auth redirect marker cookie
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type OAuthTokens,\n type User,\n type EmptyObject,\n type UnknownObject,\n type OIDCTokenResponseBody,\n tokenKeys,\n} from \"@/types.js\";\nimport type { AuthConfig } from \"@/server/config.js\";\nimport {\n getUser as getUserFromShared,\n getTokens as getTokensFromShared,\n} from \"@/shared/lib/session.js\";\nimport { clearTokens as clearTokensUtil } from \"@/shared/lib/util.js\";\nimport { resolveOAuthAccessCode } from \"@/server/login.js\";\nimport { buildLoginUrl } from \"@/server/login.js\";\nimport { buildLogoutRedirectUrl } from \"@/server/logout.js\";\nimport {\n TOKEN_EXCHANGE_SUCCESS_TEXT,\n TOKEN_EXCHANGE_TRIGGER_TEXT,\n} from \"@/constants.js\";\nimport { refreshTokens } from \"@/server/refresh.js\";\nimport { getVersion } from \"@/shared/index.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\nimport {\n DEFAULT_AUTH_SERVER,\n JWT_PAYLOAD_KNOWN_CLAIM_KEYS,\n} from \"@/constants.js\";\nimport type { AuthenticationResolver } from \"@/services/types.js\";\nimport {\n displayModeFromState,\n injectLoginSuccessUrlIntoState,\n loginSuccessUrlFromState,\n} from \"@/lib/oauth.js\";\nimport { decodeJwt, type JWTPayload } from \"jose\";\nimport {\n computeDeepLinkDestination,\n generateOauthLogoutUrl,\n getBackendEndpoints,\n prependBasePath,\n resolveEndpointUrl,\n sanitizeReturnUrl,\n} from \"@/shared/lib/util.js\";\nimport {\n AUTH_REDIRECT_MARKER_MAX_AGE,\n AuthFlowCookie,\n CodeVerifier,\n} from \"@/shared/lib/types.js\";\nimport type { CookieStorage } from \"./index.js\";\nimport { loggers } from \"@/lib/logger.js\";\n\n// Generic request interface for framework-agnostic URL detection\nexport type UrlDetectionRequest = {\n url: string;\n headers: Record<string, string | string[] | undefined>;\n searchParams: {\n get(name: string): string | null;\n };\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\nexport type HandleCallbackRequest = {\n headers: {\n [key: string]: string | string[] | undefined;\n referer?: string;\n origin?: string;\n \"user-agent\"?: string;\n accept?: string;\n \"sec-fetch-dest\"?: string;\n };\n url?: string;\n};\n\nexport type HandleCallbackParams = {\n code: string;\n state: string;\n req: HandleCallbackRequest;\n};\n\n// Function to omit keys from an object\nconst omitKeys = <K extends keyof T, T extends Record<string, unknown>>(\n keys: K[],\n obj: T,\n): Omit<T, K> => {\n const result = { ...obj };\n keys.forEach((key) => {\n delete result[key];\n });\n return result;\n};\n\n/**\n * Helper to detect if this is a same-domain callback request (for iframe workaround)\n */\nconst isSameDomainCallback = (req: HandleCallbackRequest): boolean => {\n if (!req.url) return false;\n return req.url.includes(\"sameDomainCallback=true\");\n};\n\n/**\n * Extract user information directly from OIDC tokens\n * @param tokens The OIDC tokens response\n * @returns The user object or null if no valid ID token\n */\nfunction getUserFromTokens<T extends UnknownObject = EmptyObject>(\n tokens: OIDCTokenResponseBody,\n): User<T> | null {\n if (!tokens.id_token) return null;\n\n const parsedToken = decodeJwt(tokens.id_token) as JWTPayload & T;\n if (!parsedToken.sub) return null;\n\n // set the user ID from the token sub\n const userWithAdditionalTokenFields = {\n ...(parsedToken as T),\n id: parsedToken.sub,\n };\n\n // Remove the token keys from the user object to stop it getting too large\n return omitKeys(\n [...JWT_PAYLOAD_KNOWN_CLAIM_KEYS, ...tokenKeys],\n userWithAdditionalTokenFields,\n ) as User<T>;\n}\n\n/**\n * CivicAuth is the main entry point for server-side authentication operations.\n * It provides a unified interface to all the authentication functions.\n */\nexport class CivicAuth {\n _authResolver: AuthenticationResolver | null = null;\n constructor(\n readonly storage: CookieStorage,\n readonly authConfig: AuthConfig,\n ) {}\n\n get oauthServer(): string {\n return this.authConfig.oauthServer || DEFAULT_AUTH_SERVER;\n }\n\n async getAuthResolver(): Promise<AuthenticationResolver> {\n if (this._authResolver) {\n loggers.server.debug(\"Reusing existing auth resolver\", this.authConfig);\n return Promise.resolve(this._authResolver);\n }\n loggers.server.debug(\"Creating new auth resolver\", this.authConfig);\n this._authResolver = await ServerAuthenticationResolver.build(\n {\n ...this.authConfig,\n oauthServer: this.oauthServer,\n },\n this.storage,\n );\n return this._authResolver;\n }\n /**\n * Gets the authenticated user with token validation\n * @returns The user object if authenticated, null otherwise\n */\n async getUser<\n T extends UnknownObject = EmptyObject,\n >(): Promise<User<T> | null> {\n const resolver = await this.getAuthResolver();\n\n try {\n // Validate the session before returning the user\n const session = await resolver.validateExistingSession();\n if (!session?.authenticated) {\n return null;\n }\n\n // If session is valid, use the shared implementation to get the user\n return getUserFromShared<T>(this.storage);\n } catch (error) {\n console.error(\"Token validation failed during getUser\", error);\n return null;\n }\n }\n\n /**\n * Gets the authentication tokens with token validation\n * @returns The tokens if authenticated, null otherwise\n */\n async getTokens(): Promise<OAuthTokens | null> {\n const resolver = await this.getAuthResolver();\n\n try {\n // Validate the session before returning the tokens\n const session = await resolver.validateExistingSession();\n\n if (!session?.authenticated) {\n return null;\n }\n\n // If session is valid, use the shared implementation to get the tokens\n const tokens = await getTokensFromShared(this.storage);\n return tokens;\n } catch (error) {\n console.error(\"❌ Token validation failed during getTokens\", error);\n return null;\n }\n }\n\n /**\n * Resolve an OAuth access code to a set of OIDC tokens\n * @param code The access code from the query parameter\n * @param state The OAuth state parameter\n * @returns OIDC tokens\n */\n async resolveOAuthAccessCode(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n return resolveOAuthAccessCode(code, state, this.storage, this.authConfig);\n }\n\n /**\n * Check if the user is currently logged in\n * @returns true if logged in, false otherwise\n */\n async isLoggedIn(): Promise<boolean> {\n const resolver = await this.getAuthResolver();\n const session = await resolver.validateExistingSession();\n return session?.authenticated ?? false;\n }\n\n /**\n * Build a login URL to redirect the user to\n * @param options Additional options for building the login URL\n * @returns The login URL\n */\n async buildLoginUrl(options?: {\n scopes?: string[];\n state?: string;\n nonce?: string;\n }): Promise<URL> {\n const logger = loggers.server;\n let finalState = options?.state;\n\n // If deep linking is enabled, read the return URL cookie and inject into state\n if (this.authConfig.deepLinkHandling !== \"disabled\") {\n try {\n const returnUrl = await this.storage.get(AuthFlowCookie.RETURN_URL);\n if (returnUrl) {\n logger.debug(\n \"[buildLoginUrl] Found RETURN_URL cookie, injecting into state\",\n { returnUrl },\n );\n // Inject the return URL into state, preserving any existing state\n finalState = injectLoginSuccessUrlIntoState(\n finalState ?? null,\n returnUrl,\n );\n }\n } catch (error) {\n logger.warn(\"[buildLoginUrl] Failed to read RETURN_URL cookie\", {\n error,\n });\n // Continue without the cookie - don't block login\n }\n }\n\n return buildLoginUrl(\n {\n ...this.authConfig,\n scopes: options?.scopes,\n state: finalState,\n nonce: options?.nonce,\n framework: \"server\",\n sdkVersion: getVersion(),\n },\n this.storage,\n );\n }\n\n /**\n * Build a logout URL to redirect the user to\n * @param options Additional options for building the logout URL\n * @returns The logout URL\n */\n async buildLogoutRedirectUrl(options?: {\n scopes?: string[];\n state?: string;\n }): Promise<URL> {\n // Check if this is backend integration mode (loginUrl provided)\n if (this.authConfig.loginUrl) {\n // Backend integration mode: redirect to backend logout endpoint\n // This matches the vanilla client's logout logic for backend integration\n const backendUrl = new URL(this.authConfig.loginUrl).origin;\n const endpoints = getBackendEndpoints(this.authConfig.backendEndpoints);\n const backendLogoutUrl = resolveEndpointUrl(backendUrl, endpoints.logout);\n\n const logoutUrl = new URL(backendLogoutUrl);\n\n // Include logoutRedirectUrl as query parameter if configured\n if (this.authConfig.postLogoutRedirectUrl) {\n logoutUrl.searchParams.set(\n \"logoutRedirectUrl\",\n this.authConfig.postLogoutRedirectUrl,\n );\n }\n\n // Include state if provided\n if (options?.state) {\n logoutUrl.searchParams.set(\"state\", options.state);\n }\n\n return logoutUrl;\n }\n\n // Standard OAuth flow - redirect to OAuth provider's logout endpoint\n try {\n // Use the shared getTokens function directly - this bypasses session validation\n // since for logout we just need the raw ID token, not validated tokens\n const tokens = await getTokensFromShared(this.storage);\n\n if (tokens?.idToken) {\n // Ensure clientId is present for OAuth operations\n if (!this.authConfig.clientId) {\n throw new Error(\"clientId is required for OAuth logout operations\");\n }\n\n // We have access to the ID token from HTTP-only cookies\n // Build the logout URL manually using the shared utility\n\n const logoutUrl = await generateOauthLogoutUrl({\n clientId: this.authConfig.clientId,\n redirectUrl: this.authConfig.postLogoutRedirectUrl || \"/\",\n idToken: tokens.idToken,\n state: options?.state ?? Math.random().toString(36).substring(2),\n oauthServer: this.oauthServer,\n });\n\n return logoutUrl;\n }\n } catch (error) {\n // If direct token access fails, fall back to the generic function\n console.warn(\n \"❌ Could not get tokens directly from storage, falling back to generic logout method:\",\n error,\n );\n }\n\n // Fallback to the generic function for other storage types or when tokens aren't accessible\n return buildLogoutRedirectUrl(\n {\n ...this.authConfig,\n scopes: options?.scopes,\n state: options?.state,\n },\n this.storage,\n );\n }\n\n /**\n * Refresh the current set of OIDC tokens\n * @returns The refreshed tokens or null for backend flows where tokens are managed in HTTP-only cookies\n */\n async refreshTokens(): Promise<OIDCTokenResponseBody | null> {\n return refreshTokens(this.storage, this.authConfig);\n }\n\n /**\n * Clear all authentication tokens from storage\n */\n async clearTokens(): Promise<void> {\n return clearTokensUtil(this.storage);\n }\n\n /**\n * Handles deep linking by computing the return URL and setting it as a cookie.\n * This method encapsulates the deep-linking logic for use by middleware in any framework.\n *\n * The method automatically detects whether the user is at the login URL or a protected route\n * and applies the appropriate logic:\n *\n * **At login URL:**\n * - Auth redirect (marker present): Preserve existing deep link cookie\n * - Fresh navigation with query params: Set cookie with query params\n * - Fresh navigation without params: Clear stale cookie\n *\n * **At protected route:**\n * - Always set the cookie to capture the deep link destination\n *\n * @param requestUrl - The full URL of the request being made (the page the user tried to access)\n * @param originUrl - The origin URL of the application (e.g., \"https://myapp.com\")\n * @returns The computed deep link destination, or null if deep linking is disabled or the URL is invalid\n *\n * @example\n * ```typescript\n * // In middleware for any framework\n * if (!session.authenticated) {\n * await civicAuth.handleDeepLinking(requestUrl, originUrl, request.method);\n * }\n * ```\n */\n async handleDeepLinking(\n requestUrl: string,\n originUrl: string,\n method: string,\n ): Promise<string | null> {\n const logger = loggers.server;\n\n // Only handle deep-links for GET requests (user navigations)\n // POST/PUT/DELETE are programmatic actions, not user navigation intent\n if (method !== \"GET\") {\n logger.debug(\"[handleDeepLinking] Skipping non-GET request\", { method });\n return null;\n }\n\n const deepLinkHandling =\n this.authConfig.deepLinkHandling ?? \"queryParamsOnly\";\n\n if (deepLinkHandling === \"disabled\") {\n return null;\n }\n\n // Parse the request URL to extract path, search, and hash\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(requestUrl, originUrl);\n } catch (error) {\n logger.warn(\"[handleDeepLinking] Failed to parse request URL:\", {\n requestUrl,\n originUrl,\n error,\n });\n return null;\n }\n\n // Determine if we're at the login URL\n // Note: parsedUrl.pathname may be missing basePath (e.g., Next.js strips it from request.nextUrl.pathname)\n // We need to normalize by prepending basePath before comparing against loginUrl\n const loginUrl = this.authConfig.loginUrl || \"/\";\n const loginPathFromConfig = loginUrl.startsWith(\"/\")\n ? loginUrl\n : new URL(loginUrl, originUrl).pathname;\n\n // Normalize the parsed pathname by prepending basePath if configured\n let normalizedPathname = parsedUrl.pathname;\n if (this.authConfig.basePath) {\n normalizedPathname = prependBasePath(\n parsedUrl.pathname,\n this.authConfig.basePath,\n );\n }\n\n // Normalize trailing slashes for comparison (e.g., \"/directory/\" should match \"/directory\")\n const normalizeTrailingSlash = (path: string) =>\n path.length > 1 ? path.replace(/\\/+$/, \"\") : path;\n const isAtLoginUrl =\n normalizeTrailingSlash(normalizedPathname) ===\n normalizeTrailingSlash(loginPathFromConfig);\n\n logger.debug(\"[handleDeepLinking]:\", {\n pathname: parsedUrl.pathname,\n normalizedPathname,\n isAtLoginUrl,\n loginUrl: loginPathFromConfig,\n });\n\n if (isAtLoginUrl) {\n // At login URL - use the updateDeepLinkCookie logic\n return this.handleDeepLinkingAtLoginUrl(parsedUrl, originUrl);\n }\n\n // At protected route - set the deep link cookie\n return this.handleDeepLinkingAtProtectedRoute(parsedUrl, originUrl);\n }\n\n /**\n * Internal: Handles deep linking when at a protected route.\n * Sets the cookie to capture the user's intended destination and the auth redirect marker.\n */\n private async handleDeepLinkingAtProtectedRoute(\n parsedUrl: URL,\n originUrl: string,\n ): Promise<string | null> {\n const logger = loggers.server;\n const deepLinkHandling =\n this.authConfig.deepLinkHandling ?? \"queryParamsOnly\";\n\n // Prepend basePath if configured and not already present in the pathname.\n // This is needed because some frameworks (e.g., Next.js) strip the basePath\n // from the request URL before it reaches the middleware.\n let pathname = parsedUrl.pathname;\n if (this.authConfig.basePath) {\n pathname = prependBasePath(pathname, this.authConfig.basePath);\n }\n\n const returnTo = computeDeepLinkDestination(\n pathname,\n parsedUrl.search,\n parsedUrl.hash,\n originUrl,\n deepLinkHandling,\n this.authConfig.loginSuccessUrl,\n this.authConfig.basePath,\n );\n\n if (!returnTo) {\n logger.debug(\n \"[handleDeepLinking] No deep link destination computed (disabled, invalid URL, or API path)\",\n );\n return null;\n }\n\n // Set the cookie with the computed return URL\n await this.storage.set(AuthFlowCookie.RETURN_URL, returnTo, {});\n logger.debug(\"[handleDeepLinking] Set RETURN_URL cookie\", { returnTo });\n\n // Also set the auth redirect marker since we're at a protected route\n // and the user will be redirected to login\n await this.storage.set(AuthFlowCookie.AUTH_REDIRECT_MARKER, \"1\", {\n maxAge: AUTH_REDIRECT_MARKER_MAX_AGE,\n });\n logger.debug(\"[handleDeepLinking] Set auth redirect marker\");\n\n return returnTo;\n }\n\n /**\n * Internal: Handles deep linking when at the login URL.\n * Checks the auth redirect marker and handles appropriately.\n */\n private async handleDeepLinkingAtLoginUrl(\n parsedUrl: URL,\n originUrl: string,\n ): Promise<string | null> {\n const logger = loggers.server;\n const deepLinkHandling =\n this.authConfig.deepLinkHandling ?? \"queryParamsOnly\";\n const hasQueryParams = parsedUrl.searchParams.size > 0;\n\n // Check and clean up the auth redirect marker cookie\n const isAuthRedirect = await this.storage.get(\n AuthFlowCookie.AUTH_REDIRECT_MARKER,\n );\n await this.storage.delete(AuthFlowCookie.AUTH_REDIRECT_MARKER);\n\n // Get existing cookie to determine if we need to clear stale data\n const existingCookie = await this.storage.get(AuthFlowCookie.RETURN_URL);\n\n logger.debug(\"[handleDeepLinking] At login URL:\", {\n hasQueryParams,\n isAuthRedirect: !!isAuthRedirect,\n existingCookie,\n });\n\n if (isAuthRedirect) {\n // This is a redirect from auth middleware - preserve the existing cookie\n logger.debug(\n \"[handleDeepLinking] Auth redirect detected - preserving existing cookie\",\n );\n return existingCookie;\n }\n\n // Fresh navigation (not an auth redirect)\n if (hasQueryParams) {\n // Prepend basePath if configured (same reason as handleDeepLinkingAtProtectedRoute)\n let pathname = parsedUrl.pathname;\n if (this.authConfig.basePath) {\n pathname = prependBasePath(pathname, this.authConfig.basePath);\n }\n\n logger.debug(\"[handleDeepLinking] At login URL with query params:\", {\n originalPathname: parsedUrl.pathname,\n pathname,\n search: parsedUrl.search,\n deepLinkHandling,\n basePath: this.authConfig.basePath,\n loginSuccessUrl: this.authConfig.loginSuccessUrl,\n });\n\n // User visited with query params - capture them\n const returnTo = computeDeepLinkDestination(\n pathname,\n parsedUrl.search,\n parsedUrl.hash,\n originUrl,\n deepLinkHandling,\n this.authConfig.loginSuccessUrl,\n this.authConfig.basePath,\n );\n\n logger.debug(\"[handleDeepLinking] computeDeepLinkDestination result:\", {\n returnTo,\n });\n\n if (returnTo) {\n // Set the cookie with the computed return URL\n await this.storage.set(AuthFlowCookie.RETURN_URL, returnTo, {});\n logger.debug(\n `[handleDeepLinking] Fresh visit with params - setting cookie to \"${returnTo}\"`,\n );\n return returnTo;\n }\n } else if (existingCookie) {\n // Fresh visit to login URL without query params - clear any stale cookie\n // This handles the case where a user logs out from an account-specific URL\n // (e.g., /account/123 or /?account=123) and the cookie was re-set during\n // the logout redirect chain. Clearing it ensures the next login redirects\n // to the configured loginSuccessUrl, not the previous account's URL.\n await this.storage.delete(AuthFlowCookie.RETURN_URL);\n logger.debug(\n \"[handleDeepLinking] Fresh visit without params - cleared stale return URL cookie\",\n { clearedCookie: existingCookie },\n );\n }\n\n return null;\n }\n\n /**\n * Sets the auth redirect marker cookie. This marker helps distinguish auth redirects\n * from fresh navigations to the login page, preventing the deep link cookie from being\n * incorrectly overwritten.\n *\n * **Note:** This method is automatically called by `handleDeepLinking` when the user is\n * at a protected route. You typically do NOT need to call this method manually unless\n * you have a specific use case where you're not using `handleDeepLinking`.\n *\n * @example\n * ```typescript\n * // In most cases, just call handleDeepLinking - it sets the marker automatically:\n * if (!isAuthenticated) {\n * await req.civicAuth.handleDeepLinking(requestUrl, originUrl, req.method);\n * return res.redirect('/login');\n * }\n * ```\n */\n async setAuthRedirectMarker(): Promise<void> {\n const logger = loggers.server;\n const deepLinkHandling =\n this.authConfig.deepLinkHandling ?? \"queryParamsOnly\";\n\n if (deepLinkHandling === \"disabled\") {\n return;\n }\n\n await this.storage.set(AuthFlowCookie.AUTH_REDIRECT_MARKER, \"1\", {\n maxAge: AUTH_REDIRECT_MARKER_MAX_AGE,\n });\n logger.debug(\"[setAuthRedirectMarker] Set auth redirect marker cookie\");\n }\n\n /**\n * Framework-agnostic URL detection and resolution helpers\n * These methods handle proxy environments and can be used by any framework\n */\n\n /**\n * Try to URI decode a value, returning the original value on error\n */\n static tryUriDecode(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch (e) {\n console.error(\"Error decoding URI component:\", e);\n return value;\n }\n }\n\n /**\n * Get decoded query parameter from request\n */\n static getDecodedQueryParam(\n request: UrlDetectionRequest,\n paramName: string,\n ): string | null {\n const queryParam = request.searchParams.get(paramName);\n if (queryParam) {\n return CivicAuth.tryUriDecode(queryParam);\n }\n return null;\n }\n\n /**\n * Get value from cookie or query parameter (cookie takes precedence)\n */\n static getCookieOrQueryParam(\n request: UrlDetectionRequest,\n cookieName: string,\n queryName: string,\n ): string | null {\n // First check the cookie as it might have the full path with base directory\n const cookieValue = request.cookies.get(cookieName)?.value;\n if (cookieValue) {\n return CivicAuth.tryUriDecode(cookieValue);\n }\n\n // Fallback to query parameter\n return CivicAuth.getDecodedQueryParam(request, queryName);\n }\n\n /**\n * Get app URL from request (for proxy environment support)\n * Checks cookies first, then query parameters\n */\n static getAppUrl(request: UrlDetectionRequest): string | null {\n return CivicAuth.getCookieOrQueryParam(\n request,\n CodeVerifier.APP_URL,\n \"appUrl\",\n );\n }\n\n /**\n * Get login success URL with proper base URL handling\n * Extracts from state parameter or query parameters, resolves with baseUrl if provided\n */\n static getLoginSuccessUrl(\n request: UrlDetectionRequest,\n baseUrl?: string | null,\n ): string | null {\n const state = request.searchParams.get(\"state\");\n const loginSuccessUrl =\n loginSuccessUrlFromState(state) ||\n CivicAuth.getDecodedQueryParam(request, \"loginSuccessUrl\");\n\n if (!loginSuccessUrl) {\n return null;\n }\n\n return baseUrl ? new URL(loginSuccessUrl, baseUrl).href : loginSuccessUrl;\n }\n\n /**\n * Convert relative URL to absolute URL using appUrl for proxy environments\n */\n static toAbsoluteUrl(\n request: UrlDetectionRequest,\n url: string,\n appUrl?: string | null,\n ): string {\n if (url.startsWith(\"http\")) {\n return url;\n }\n\n // Use appUrl if available (for proxy environments), otherwise fall back to request origin\n const baseUrl = appUrl || new URL(request.url).origin;\n return new URL(url, baseUrl).href;\n }\n\n /**\n * Get post-logout redirect URL with proxy environment support\n */\n getPostLogoutRedirectUrl(request: UrlDetectionRequest): string {\n // Check if we have a target URL in the request (from middleware)\n const targetUrl = request.searchParams.get(\"targetUrl\");\n if (targetUrl) {\n return targetUrl;\n }\n\n const redirectTarget =\n this.authConfig.baseUrl ?? this.authConfig.loginSuccessUrl ?? \"/\";\n\n // If redirectTarget is absolute, use it as-is\n const isAbsoluteRedirect = /^(https?:\\/\\/|www\\.).+/i.test(redirectTarget);\n if (isAbsoluteRedirect) {\n return redirectTarget;\n }\n\n // Use appUrl from client for proxy environments\n const appUrl = CivicAuth.getAppUrl(request);\n if (appUrl) {\n return new URL(redirectTarget, appUrl).href;\n }\n\n // Fallback to request origin\n return new URL(request.url).origin;\n }\n\n /**\n * Smart callback handler that automatically detects frontend vs backend requests\n * and redirects appropriately. Use this instead of resolveOAuthAccessCode + manual redirect.\n *\n * @param params An object containing the authorization code, state, and the incoming request.\n * @param params.code The authorization code from query parameters.\n * @param params.state The OAuth state parameter.\n * @param params.req The incoming request object (e.g., from Express).\n * @param options Configuration options (frontendUrl override, apiResponse flag).\n * @returns Object with redirect information or HTML content for iframe completion.\n *\n * @example\n * ```javascript\n * app.get('/auth/callback', async (req, res) => {\n * const { code, state } = req.query;\n * // The request object 'req' is passed directly\n * const result = await req.civicAuth.handleCallback({ code, state, req });\n *\n * if (result.htmlContent) {\n * res.setHeader('Content-Type', 'text/html');\n * res.send(result.htmlContent);\n * } else if (result.redirectTo) {\n * res.redirect(result.redirectTo);\n * } else {\n * res.json({ success: true, user: result.user });\n * }\n * });\n * ```\n */\n async handleCallback(\n { code, state, req }: HandleCallbackParams,\n options?: {\n frontendUrl?: string;\n apiResponse?: boolean;\n },\n ): Promise<{\n redirectTo?: string;\n content?: string | { success: boolean; user?: User | null };\n }> {\n const logger = loggers.server;\n\n // Helper to clear the deep link cookie after successful auth.\n // This is centralized here so all framework implementations benefit.\n const clearReturnUrlCookie = async () => {\n try {\n await this.storage.delete(AuthFlowCookie.RETURN_URL);\n logger.debug(\n \"[handleCallback] Cleared deep link cookie after successful auth\",\n );\n } catch (error) {\n logger.warn(\"[handleCallback] Failed to clear deep link cookie\", {\n error,\n });\n }\n };\n\n // Handle same-domain callback for iframe workaround\n if (isSameDomainCallback(req)) {\n try {\n // Check if user is already authenticated before attempting token exchange\n const isAlreadyLoggedIn = await this.isLoggedIn();\n let user: User | null = null;\n\n if (isAlreadyLoggedIn) {\n // User is already authenticated, get existing user data\n user = await this.getUser();\n console.log(\n \"User already authenticated in same-domain callback:\",\n !!user,\n );\n } else {\n // For same-domain callbacks, we should have access to cookies\n const tokens = await this.resolveOAuthAccessCode(code, state);\n user = getUserFromTokens(tokens);\n console.log(\n \"Completed token exchange in same-domain callback:\",\n !!user,\n );\n }\n\n // Return JSON response for same-domain callback\n const currentUrl = new URL(req.url || \"\");\n\n // Extract and sanitize loginSuccessUrl (deep link) BEFORE cleaning up search params\n // Sanitization prevents open redirect attacks via malicious URLs in the query param\n const rawLoginSuccessUrl =\n currentUrl.searchParams.get(\"loginSuccessUrl\");\n const loginSuccessUrl = rawLoginSuccessUrl\n ? sanitizeReturnUrl(rawLoginSuccessUrl, currentUrl.origin)\n : null;\n\n const newSearchParams = new URLSearchParams(currentUrl.search);\n newSearchParams.delete(\"sameDomainCallback\");\n newSearchParams.delete(\"appUrl\");\n newSearchParams.delete(\"loginSuccessUrl\");\n\n // Use preserved deep link if available and valid, otherwise fall back to loginSuccessUrl or \"/\"\n // Note: Do NOT fall back to currentUrl.pathname as that's the callback URL, which would cause a loop\n let redirectUrl =\n loginSuccessUrl || this.authConfig.loginSuccessUrl || \"/\";\n\n // Apply basePath if configured\n if (this.authConfig.basePath) {\n redirectUrl = prependBasePath(redirectUrl, this.authConfig.basePath);\n }\n\n // Clear the deep link cookie after successful same-domain callback\n await clearReturnUrlCookie();\n\n return {\n content: {\n success: true,\n redirectUrl,\n } as { success: boolean; redirectUrl: string },\n };\n } catch (error) {\n console.error(\"Same-domain callback failed:\", error);\n throw error;\n }\n }\n\n // Try to resolve the OAuth code and create session\n let tokens: OIDCTokenResponseBody;\n let user: User | null;\n\n try {\n tokens = await this.resolveOAuthAccessCode(code, state);\n user = getUserFromTokens(tokens);\n\n // Clear the deep link cookie after successful token exchange\n await clearReturnUrlCookie();\n } catch (error) {\n // Check if this is a code verifier error and we're in iframe mode\n const isCodeVerifierError =\n error instanceof Error &&\n error.message.includes(\"Code verifier not found in storage\");\n\n if (isCodeVerifierError) {\n // First check if user is already authenticated before trying iframe workaround\n try {\n const isAlreadyLoggedIn = await this.isLoggedIn();\n\n if (isAlreadyLoggedIn) {\n // \"User already authenticated, skipping iframe workaround\",\n const user = await this.getUser();\n const loginSuccessUrlFromStateValue =\n loginSuccessUrlFromState(state);\n let frontendUrl =\n options?.frontendUrl ||\n loginSuccessUrlFromStateValue ||\n this.authConfig.loginSuccessUrl;\n\n // Apply basePath to frontendUrl if configured\n if (frontendUrl && this.authConfig.basePath) {\n frontendUrl = prependBasePath(\n frontendUrl,\n this.authConfig.basePath,\n );\n }\n\n // Check if this is an iframe context - if so, generate iframe completion HTML\n const stateDisplayMode = displayModeFromState(state, undefined);\n const isConfiguredForIframe = stateDisplayMode === \"iframe\";\n\n if (\n isConfiguredForIframe &&\n !this.authConfig.disableIframeDetection &&\n user &&\n frontendUrl\n ) {\n // Generating iframe completion HTML for already authenticated user\",\n const completionHtml = this.generateIframeCompletionHtml(\n user,\n frontendUrl,\n );\n return { content: completionHtml };\n }\n\n if (frontendUrl) {\n return { redirectTo: frontendUrl };\n } else {\n // Return JSON when no redirect destination is available.\n // This is intentional for API-style callbacks and cross-origin scenarios\n // where cookies aren't accessible to read config. The JSON response allows\n // clients (e.g., vanilla JS SDK) to handle the success case programmatically.\n return { content: { success: true, user } };\n }\n }\n } catch (authCheckError) {\n console.warn(\n \"Failed to check authentication status:\",\n authCheckError,\n );\n // Continue with iframe workaround if auth check fails\n }\n\n const stateDisplayMode = displayModeFromState(state, undefined);\n const isConfiguredForIframe = stateDisplayMode === \"iframe\";\n\n if (isConfiguredForIframe && !this.authConfig.disableIframeDetection) {\n // Generate HTML that will trigger same-domain callback\n const loginSuccessUrlFromStateValue = loginSuccessUrlFromState(state);\n let frontendUrl =\n options?.frontendUrl ||\n loginSuccessUrlFromStateValue ||\n this.authConfig.loginSuccessUrl;\n\n // Apply basePath to frontendUrl if configured\n if (frontendUrl && this.authConfig.basePath) {\n frontendUrl = prependBasePath(\n frontendUrl,\n this.authConfig.basePath,\n );\n }\n\n const callbackUrl = req.url || \"\";\n const sameDomainHtml = this.generateSameDomainCallbackHtml(\n callbackUrl,\n frontendUrl,\n );\n return { content: sameDomainHtml };\n }\n\n // For non-iframe mode or when iframe detection is disabled, return trigger text\n return {\n content: `<html lang=\"en\"><body><span style=\"display:none\">${TOKEN_EXCHANGE_TRIGGER_TEXT}</span></body></html>`,\n };\n }\n\n // Re-throw other errors\n throw error;\n }\n\n // Extract loginSuccessUrl from state if present\n const loginSuccessUrlFromStateValue = loginSuccessUrlFromState(state);\n\n // Priority: options.frontendUrl > loginSuccessUrl from state > config loginSuccessUrl\n let frontendUrl =\n options?.frontendUrl ||\n loginSuccessUrlFromStateValue ||\n this.authConfig.loginSuccessUrl;\n\n // Apply basePath to frontendUrl if configured\n if (frontendUrl && this.authConfig.basePath) {\n frontendUrl = prependBasePath(frontendUrl, this.authConfig.basePath);\n }\n\n // Priority 1: Check state for display mode configuration\n const stateDisplayMode = displayModeFromState(state, undefined);\n const isConfiguredForIframe = stateDisplayMode === \"iframe\";\n\n // Determine if this should be treated as an iframe request\n // Configuration (from state) takes precedence over auto-detection\n const shouldTreatAsIframe =\n isConfiguredForIframe && !this.authConfig.disableIframeDetection;\n\n const isTopLevelRedirect = req.headers[\"sec-fetch-dest\"] === \"document\";\n const isIframeRequest = req.headers[\"sec-fetch-dest\"] === \"iframe\";\n const isApiRequest =\n options?.apiResponse || req.headers.accept?.includes(\"application/json\");\n\n // Detect Safari or other browsers where iframe postMessage may fail due to cross-origin restrictions\n //TODO: Find a better way to detect this\n const userAgent = req.headers[\"user-agent\"] || \"\";\n const isSafari =\n userAgent.includes(\"Safari\") && !userAgent.includes(\"Chrome\");\n const isLikelyCrossOriginIframe =\n isSafari ||\n (userAgent.includes(\"WebKit\") && !userAgent.includes(\"Chrome\"));\n\n // Case 1: The request should be treated as iframe. Return HTML content.\n // Unless iframe detection is disabled via configuration OR we detect cross-origin issues\n if (\n shouldTreatAsIframe &&\n user &&\n frontendUrl &&\n !isLikelyCrossOriginIframe\n ) {\n const completionHtml = this.generateIframeCompletionHtml(\n user,\n frontendUrl,\n );\n return { content: completionHtml };\n }\n\n // Case 1b: Safari/cross-origin iframe case - redirect instead of HTML\n if (\n shouldTreatAsIframe &&\n user &&\n frontendUrl &&\n isLikelyCrossOriginIframe\n ) {\n return { redirectTo: frontendUrl };\n }\n\n // Case 2: The request is a top-level navigation. Return redirect URL.\n if (isTopLevelRedirect && frontendUrl) {\n return { redirectTo: frontendUrl };\n }\n\n // Case 2a: The request is from an iframe (detected by sec-fetch-dest)\n // Even if not configured for iframe in state, we should still generate iframe completion HTML\n if (isIframeRequest && user && frontendUrl) {\n if (isLikelyCrossOriginIframe) {\n console.log(\n \"Iframe request detected but cross-origin issues likely - redirecting\",\n );\n return { redirectTo: frontendUrl };\n } else {\n console.log(\n \"Generating iframe completion HTML for iframe request (detected by sec-fetch-dest)\",\n );\n const completionHtml = this.generateIframeCompletionHtml(\n user,\n frontendUrl,\n );\n return { content: completionHtml };\n }\n }\n\n // Case 3: The request is an API call. Return JSON content.\n if (isApiRequest) {\n return {\n content: {\n success: true,\n user,\n },\n };\n }\n\n // Fallback for older browsers or other contexts: if a frontend URL is configured,\n // assume a redirect to it.\n if (frontendUrl) {\n return { redirectTo: frontendUrl };\n }\n\n // Absolute fallback: redirect to loginSuccessUrl or \"/\"\n // Never return JSON for browser navigation - that would display raw JSON to the user\n const fallbackUrl = this.authConfig.loginSuccessUrl || \"/\";\n return {\n redirectTo: this.authConfig.basePath\n ? prependBasePath(fallbackUrl, this.authConfig.basePath)\n : fallbackUrl,\n };\n }\n\n /**\n * Generate HTML content for iframe completion that sends postMessage to parent\n */\n private generateIframeCompletionHtml(\n user: User,\n frontendUrl?: string,\n ): string {\n const escapedUser = JSON.stringify(user).replace(/'/g, \"\\\\'\");\n const clientId = this.authConfig.clientId;\n\n // Determine fallback redirect URL\n // Note: redirectUrl is the OAuth callback URL - it should NEVER be used as a post-login\n // destination as it would cause an infinite redirect loop in iframe mode.\n // postLogoutRedirectUrl is semantically incorrect for login success scenarios.\n const fallbackUrl = frontendUrl || this.authConfig.loginSuccessUrl || \"/\";\n\n return `\n <!DOCTYPE html>\n <html>\n <head>\n <title>Authentication Complete</title>\n <meta charset=\"utf-8\">\n </head>\n <body> \n <!-- Success signal for SignalObserver -->\n <div id=\"civic-auth-success-signal\" style=\"display: none;\" data-user-info='${escapedUser}'>\n ${TOKEN_EXCHANGE_SUCCESS_TEXT}\n </div>\n \n <script>\n // Send postMessage to parent to resolve authentication promise\n if (window.parent && window.parent !== window) {\n try {\n window.parent.postMessage({\n type: 'auth_success',\n detail: 'Authentication successful',\n data: {\n user: ${escapedUser},\n redirectUrl: ${JSON.stringify(fallbackUrl)}\n }\n }, '*');\n } catch (error) {\n console.error('❌ Failed to send postMessage:', error);\n }\n\n // Also send civicloginApp format message for compatibility\n try {\n window.parent.postMessage({\n source: 'civicloginApp',\n type: 'auth_success',\n clientId: ${JSON.stringify(clientId)},\n data: {\n user: ${escapedUser},\n redirectUrl: ${JSON.stringify(fallbackUrl)}\n }\n }, '*');\n } catch (error) {\n console.error('❌ Failed to send civicloginApp message:', error);\n }\n } else {\n console.log('❌ Not in iframe context or no parent window');\n }\n \n // Fallback redirect after 500ms delay to handle cases where:\n // 1. postMessage fails or parent doesn't respond\n // 2. Not in iframe context\n // 3. Any other edge cases where the user gets stuck\n setTimeout(function() {\n var redirectTarget = ${JSON.stringify(fallbackUrl)};\n console.log('🔄 Executing fallback redirect to:', redirectTarget);\n window.location.href = redirectTarget;\n }, 500);\n </script>\n </body>\n </html>\n `;\n }\n\n /**\n * Generate HTML response that triggers same-domain callback for iframe workaround\n */\n private generateSameDomainCallbackHtml = (\n callbackUrl: string,\n frontendUrl?: string,\n ): string => {\n const loginSuccessSegment = frontendUrl\n ? `&loginSuccessUrl=${encodeURIComponent(frontendUrl)}`\n : \"\";\n\n return `<html lang=\"en\">\n <body>\n <span style=\"display:none\">\n <script>\n window.onload = function () {\n // Get the complete URL including origin and path\n // This ensures we capture any base path like /directory\n const appUrl = window.location.href.substring(\n 0,\n window.location.href.indexOf(\"/api/auth\")\n );\n fetch('${callbackUrl}&sameDomainCallback=true&appUrl=' + encodeURIComponent(appUrl) + '${loginSuccessSegment}').then((response) => {\n response.json().then((jsonResponse) => {\n // For login: Redirect back to the callback route, so Case 2 in handleTokenExchangeComplete will be triggered\n // For logout: Redirect to the postLogoutRedirectUrl\n if(jsonResponse.redirectUrl) {\n window.location.href = jsonResponse.redirectUrl;\n }\n });\n });\n };\n </script>\n </span>\n </body>\n</html>\n`;\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type OAuthTokens,\n type User,\n type EmptyObject,\n type UnknownObject,\n type OIDCTokenResponseBody,\n tokenKeys,\n} from \"@/types.js\";\nimport type { AuthConfig } from \"@/server/config.js\";\nimport {\n getUser as getUserFromShared,\n getTokens as getTokensFromShared,\n} from \"@/shared/lib/session.js\";\nimport { clearTokens as clearTokensUtil } from \"@/shared/lib/util.js\";\nimport { resolveOAuthAccessCode } from \"@/server/login.js\";\nimport { buildLoginUrl } from \"@/server/login.js\";\nimport { buildLogoutRedirectUrl } from \"@/server/logout.js\";\nimport {\n TOKEN_EXCHANGE_SUCCESS_TEXT,\n TOKEN_EXCHANGE_TRIGGER_TEXT,\n} from \"@/constants.js\";\nimport { refreshTokens } from \"@/server/refresh.js\";\nimport { getVersion } from \"@/shared/index.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\nimport {\n DEFAULT_AUTH_SERVER,\n JWT_PAYLOAD_KNOWN_CLAIM_KEYS,\n} from \"@/constants.js\";\nimport type { AuthenticationResolver } from \"@/services/types.js\";\nimport {\n displayModeFromState,\n injectLoginSuccessUrlIntoState,\n loginSuccessUrlFromState,\n} from \"@/lib/oauth.js\";\nimport { decodeJwt, type JWTPayload } from \"jose\";\nimport {\n computeDeepLinkDestination,\n generateOauthLogoutUrl,\n getBackendEndpoints,\n prependBasePath,\n resolveEndpointUrl,\n sanitizeReturnUrl,\n} from \"@/shared/lib/util.js\";\nimport {\n AUTH_REDIRECT_MARKER_MAX_AGE,\n AuthFlowCookie,\n CodeVerifier,\n} from \"@/shared/lib/types.js\";\nimport type { CookieStorage } from \"./index.js\";\nimport { loggers } from \"@/lib/logger.js\";\n\n// Generic request interface for framework-agnostic URL detection\nexport type UrlDetectionRequest = {\n url: string;\n headers: Record<string, string | string[] | undefined>;\n searchParams: {\n get(name: string): string | null;\n };\n cookies: {\n get(name: string): { value: string } | undefined;\n };\n};\n\nexport type HandleCallbackRequest = {\n headers: {\n [key: string]: string | string[] | undefined;\n referer?: string;\n origin?: string;\n \"user-agent\"?: string;\n accept?: string;\n \"sec-fetch-dest\"?: string;\n };\n url?: string;\n};\n\nexport type HandleCallbackParams = {\n code: string;\n state: string;\n req: HandleCallbackRequest;\n};\n\n// Function to omit keys from an object\nconst omitKeys = <K extends keyof T, T extends Record<string, unknown>>(\n keys: K[],\n obj: T,\n): Omit<T, K> => {\n const result = { ...obj };\n keys.forEach((key) => {\n delete result[key];\n });\n return result;\n};\n\n/**\n * Helper to detect if this is a same-domain callback request (for iframe workaround)\n */\nconst isSameDomainCallback = (req: HandleCallbackRequest): boolean => {\n if (!req.url) return false;\n return req.url.includes(\"sameDomainCallback=true\");\n};\n\n/**\n * Extract user information directly from OIDC tokens\n * @param tokens The OIDC tokens response\n * @returns The user object or null if no valid ID token\n */\nfunction getUserFromTokens<T extends UnknownObject = EmptyObject>(\n tokens: OIDCTokenResponseBody,\n): User<T> | null {\n if (!tokens.id_token) return null;\n\n const parsedToken = decodeJwt(tokens.id_token) as JWTPayload & T;\n if (!parsedToken.sub) return null;\n\n // set the user ID from the token sub\n const userWithAdditionalTokenFields = {\n ...(parsedToken as T),\n id: parsedToken.sub,\n };\n\n // Remove the token keys from the user object to stop it getting too large\n return omitKeys(\n [...JWT_PAYLOAD_KNOWN_CLAIM_KEYS, ...tokenKeys],\n userWithAdditionalTokenFields,\n ) as User<T>;\n}\n\n/**\n * CivicAuth is the main entry point for server-side authentication operations.\n * It provides a unified interface to all the authentication functions.\n */\nexport class CivicAuth {\n _authResolver: AuthenticationResolver | null = null;\n constructor(\n readonly storage: CookieStorage,\n readonly authConfig: AuthConfig,\n ) {}\n\n get oauthServer(): string {\n return this.authConfig.oauthServer || DEFAULT_AUTH_SERVER;\n }\n\n async getAuthResolver(): Promise<AuthenticationResolver> {\n if (this._authResolver) {\n loggers.server.debug(\"Reusing existing auth resolver\", this.authConfig);\n return Promise.resolve(this._authResolver);\n }\n loggers.server.debug(\"Creating new auth resolver\", this.authConfig);\n this._authResolver = await ServerAuthenticationResolver.build(\n {\n ...this.authConfig,\n oauthServer: this.oauthServer,\n },\n this.storage,\n );\n return this._authResolver;\n }\n /**\n * Gets the authenticated user with token validation\n * @returns The user object if authenticated, null otherwise\n */\n async getUser<\n T extends UnknownObject = EmptyObject,\n >(): Promise<User<T> | null> {\n const resolver = await this.getAuthResolver();\n\n try {\n // Validate the session before returning the user\n const session = await resolver.validateExistingSession();\n if (!session?.authenticated) {\n return null;\n }\n\n // If session is valid, use the shared implementation to get the user\n return getUserFromShared<T>(this.storage);\n } catch (error) {\n console.error(\"Token validation failed during getUser\", error);\n return null;\n }\n }\n\n /**\n * Gets the authentication tokens with token validation\n * @returns The tokens if authenticated, null otherwise\n */\n async getTokens(): Promise<OAuthTokens | null> {\n const resolver = await this.getAuthResolver();\n\n try {\n // Validate the session before returning the tokens\n const session = await resolver.validateExistingSession();\n\n if (!session?.authenticated) {\n return null;\n }\n\n // If session is valid, use the shared implementation to get the tokens\n const tokens = await getTokensFromShared(this.storage);\n return tokens;\n } catch (error) {\n console.error(\"❌ Token validation failed during getTokens\", error);\n return null;\n }\n }\n\n /**\n * Resolve an OAuth access code to a set of OIDC tokens\n * @param code The access code from the query parameter\n * @param state The OAuth state parameter\n * @returns OIDC tokens\n */\n async resolveOAuthAccessCode(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n return resolveOAuthAccessCode(code, state, this.storage, this.authConfig);\n }\n\n /**\n * Check if the user is currently logged in\n * @returns true if logged in, false otherwise\n */\n async isLoggedIn(): Promise<boolean> {\n const resolver = await this.getAuthResolver();\n const session = await resolver.validateExistingSession();\n return session?.authenticated ?? false;\n }\n\n /**\n * Build a login URL to redirect the user to\n * @param options Additional options for building the login URL\n * @returns The login URL\n */\n async buildLoginUrl(options?: {\n scopes?: string[];\n state?: string;\n nonce?: string;\n }): Promise<URL> {\n const logger = loggers.server;\n let finalState = options?.state;\n\n // If deep linking is enabled, read the return URL cookie and inject into state\n if (this.authConfig.deepLinkHandling !== \"disabled\") {\n try {\n const returnUrl = await this.storage.get(AuthFlowCookie.RETURN_URL);\n if (returnUrl) {\n logger.debug(\n \"[buildLoginUrl] Found RETURN_URL cookie, injecting into state\",\n { returnUrl },\n );\n // Inject the return URL into state, preserving any existing state\n finalState = injectLoginSuccessUrlIntoState(\n finalState ?? null,\n returnUrl,\n );\n }\n } catch (error) {\n logger.warn(\"[buildLoginUrl] Failed to read RETURN_URL cookie\", {\n error,\n });\n // Continue without the cookie - don't block login\n }\n }\n\n return buildLoginUrl(\n {\n ...this.authConfig,\n scopes: options?.scopes,\n state: finalState,\n nonce: options?.nonce,\n framework: \"server\",\n sdkVersion: getVersion(),\n },\n this.storage,\n );\n }\n\n /**\n * Build a logout URL to redirect the user to\n * @param options Additional options for building the logout URL\n * @returns The logout URL\n */\n async buildLogoutRedirectUrl(options?: {\n scopes?: string[];\n state?: string;\n }): Promise<URL> {\n // Check if this is backend integration mode (loginUrl provided)\n if (this.authConfig.loginUrl) {\n // Backend integration mode: redirect to backend logout endpoint\n // This matches the vanilla client's logout logic for backend integration\n const backendUrl = new URL(this.authConfig.loginUrl).origin;\n const endpoints = getBackendEndpoints(this.authConfig.backendEndpoints);\n const backendLogoutUrl = resolveEndpointUrl(backendUrl, endpoints.logout);\n\n const logoutUrl = new URL(backendLogoutUrl);\n\n // Include logoutRedirectUrl as query parameter if configured\n if (this.authConfig.postLogoutRedirectUrl) {\n logoutUrl.searchParams.set(\n \"logoutRedirectUrl\",\n this.authConfig.postLogoutRedirectUrl,\n );\n }\n\n // Include state if provided\n if (options?.state) {\n logoutUrl.searchParams.set(\"state\", options.state);\n }\n\n return logoutUrl;\n }\n\n // Standard OAuth flow - redirect to OAuth provider's logout endpoint\n try {\n // Use the shared getTokens function directly - this bypasses session validation\n // since for logout we just need the raw ID token, not validated tokens\n const tokens = await getTokensFromShared(this.storage);\n\n if (tokens?.idToken) {\n // Ensure clientId is present for OAuth operations\n if (!this.authConfig.clientId) {\n throw new Error(\"clientId is required for OAuth logout operations\");\n }\n\n // We have access to the ID token from HTTP-only cookies\n // Build the logout URL manually using the shared utility\n\n const logoutUrl = await generateOauthLogoutUrl({\n clientId: this.authConfig.clientId,\n redirectUrl: this.authConfig.postLogoutRedirectUrl || \"/\",\n idToken: tokens.idToken,\n state: options?.state ?? Math.random().toString(36).substring(2),\n oauthServer: this.oauthServer,\n });\n\n return logoutUrl;\n }\n } catch (error) {\n // If direct token access fails, fall back to the generic function\n console.warn(\n \"❌ Could not get tokens directly from storage, falling back to generic logout method:\",\n error,\n );\n }\n\n // Fallback to the generic function for other storage types or when tokens aren't accessible\n return buildLogoutRedirectUrl(\n {\n ...this.authConfig,\n scopes: options?.scopes,\n state: options?.state,\n },\n this.storage,\n );\n }\n\n /**\n * Refresh the current set of OIDC tokens\n * @returns The refreshed tokens or null for backend flows where tokens are managed in HTTP-only cookies\n */\n async refreshTokens(): Promise<OIDCTokenResponseBody | null> {\n return refreshTokens(this.storage, this.authConfig);\n }\n\n /**\n * Clear all authentication tokens from storage\n */\n async clearTokens(): Promise<void> {\n return clearTokensUtil(this.storage);\n }\n\n /**\n * Handles deep linking by computing the return URL and setting it as a cookie.\n * This method encapsulates the deep-linking logic for use by middleware in any framework.\n *\n * The method automatically detects whether the user is at the login URL or a protected route\n * and applies the appropriate logic:\n *\n * **At login URL:**\n * - Auth redirect (marker present): Preserve existing deep link cookie\n * - Fresh navigation with query params: Set cookie with query params\n * - Fresh navigation without params: Clear stale cookie\n *\n * **At protected route:**\n * - Always set the cookie to capture the deep link destination\n *\n * @param requestUrl - The full URL of the request being made (the page the user tried to access)\n * @param originUrl - The origin URL of the application (e.g., \"https://myapp.com\")\n * @returns The computed deep link destination, or null if deep linking is disabled or the URL is invalid\n *\n * @example\n * ```typescript\n * // In middleware for any framework\n * if (!session.authenticated) {\n * await civicAuth.handleDeepLinking(requestUrl, originUrl, request.method);\n * }\n * ```\n */\n async handleDeepLinking(\n requestUrl: string,\n originUrl: string,\n method: string,\n headers?: Headers | Record<string, string>,\n ): Promise<string | null> {\n const logger = loggers.server;\n\n // Only handle deep-links for GET requests (user navigations)\n // POST/PUT/DELETE are programmatic actions, not user navigation intent\n if (method !== \"GET\") {\n logger.debug(\"[handleDeepLinking] Skipping non-GET request\", { method });\n return null;\n }\n\n const deepLinkHandling =\n this.authConfig.deepLinkHandling ?? \"queryParamsOnly\";\n\n if (deepLinkHandling === \"disabled\") {\n return null;\n }\n\n // Parse the request URL to extract path, search, and hash\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(requestUrl, originUrl);\n } catch (error) {\n logger.warn(\"[handleDeepLinking] Failed to parse request URL:\", {\n requestUrl,\n originUrl,\n error,\n });\n return null;\n }\n\n // Determine if we're at the login URL\n // Note: parsedUrl.pathname may be missing basePath (e.g., Next.js strips it from request.nextUrl.pathname)\n // We need to normalize by prepending basePath before comparing against loginUrl\n const loginUrl = this.authConfig.loginUrl || \"/\";\n const loginPathFromConfig = loginUrl.startsWith(\"/\")\n ? loginUrl\n : new URL(loginUrl, originUrl).pathname;\n\n // Normalize the parsed pathname by prepending basePath if configured\n let normalizedPathname = parsedUrl.pathname;\n if (this.authConfig.basePath) {\n normalizedPathname = prependBasePath(\n parsedUrl.pathname,\n this.authConfig.basePath,\n );\n }\n\n // Normalize trailing slashes for comparison (e.g., \"/directory/\" should match \"/directory\")\n const normalizeTrailingSlash = (path: string) =>\n path.length > 1 ? path.replace(/\\/+$/, \"\") : path;\n const isAtLoginUrl =\n normalizeTrailingSlash(normalizedPathname) ===\n normalizeTrailingSlash(loginPathFromConfig);\n\n logger.debug(\"[handleDeepLinking]:\", {\n pathname: parsedUrl.pathname,\n normalizedPathname,\n isAtLoginUrl,\n loginUrl: loginPathFromConfig,\n });\n\n if (isAtLoginUrl) {\n // At login URL - use the updateDeepLinkCookie logic\n return this.handleDeepLinkingAtLoginUrl(parsedUrl, originUrl, headers);\n }\n\n // At protected route - set the deep link cookie\n return this.handleDeepLinkingAtProtectedRoute(parsedUrl, originUrl);\n }\n\n /**\n * Internal: Handles deep linking when at a protected route.\n * Sets the cookie to capture the user's intended destination and the auth redirect marker.\n */\n private async handleDeepLinkingAtProtectedRoute(\n parsedUrl: URL,\n originUrl: string,\n ): Promise<string | null> {\n const logger = loggers.server;\n const deepLinkHandling =\n this.authConfig.deepLinkHandling ?? \"queryParamsOnly\";\n\n // Prepend basePath if configured and not already present in the pathname.\n // This is needed because some frameworks (e.g., Next.js) strip the basePath\n // from the request URL before it reaches the middleware.\n let pathname = parsedUrl.pathname;\n if (this.authConfig.basePath) {\n pathname = prependBasePath(pathname, this.authConfig.basePath);\n }\n\n const returnTo = computeDeepLinkDestination(\n pathname,\n parsedUrl.search,\n parsedUrl.hash,\n originUrl,\n deepLinkHandling,\n this.authConfig.loginSuccessUrl,\n this.authConfig.basePath,\n );\n\n if (!returnTo) {\n logger.debug(\n \"[handleDeepLinking] No deep link destination computed (disabled, invalid URL, or API path)\",\n );\n return null;\n }\n\n // Set the cookie with the computed return URL\n await this.storage.set(AuthFlowCookie.RETURN_URL, returnTo, {});\n logger.debug(\"[handleDeepLinking] Set RETURN_URL cookie\", { returnTo });\n\n // Also set the auth redirect marker since we're at a protected route\n // and the user will be redirected to login\n await this.storage.set(AuthFlowCookie.AUTH_REDIRECT_MARKER, \"1\", {\n maxAge: AUTH_REDIRECT_MARKER_MAX_AGE,\n });\n logger.debug(\"[handleDeepLinking] Set auth redirect marker\");\n\n return returnTo;\n }\n\n /**\n * Internal: Handles deep linking when at the login URL.\n * Checks the auth redirect marker and handles appropriately.\n */\n private async handleDeepLinkingAtLoginUrl(\n parsedUrl: URL,\n originUrl: string,\n headers?: Headers | Record<string, string>,\n ): Promise<string | null> {\n const logger = loggers.server;\n\n // RSC prefetches (Next-Router-Prefetch: 1) and RSC navigations (RSC: 1) are\n // speculative requests issued by the Next.js router. They must not consume the\n // one-shot AUTH_REDIRECT_MARKER or clear the RETURN_URL cookie, because doing\n // so races with the real navigation that immediately follows.\n const getHeader = (name: string) => {\n if (headers instanceof Headers) return headers.get(name);\n if (!headers) return null;\n const lower = name.toLowerCase();\n for (const [k, v] of Object.entries(headers)) {\n if (k.toLowerCase() === lower) return v;\n }\n return null;\n };\n if (getHeader(\"Next-Router-Prefetch\") === \"1\" || getHeader(\"RSC\") === \"1\") {\n logger.debug(\n \"[handleDeepLinking] Skipping RSC/prefetch request at login URL\",\n );\n return null;\n }\n\n const deepLinkHandling =\n this.authConfig.deepLinkHandling ?? \"queryParamsOnly\";\n const hasQueryParams = parsedUrl.searchParams.size > 0;\n\n // Check and clean up the auth redirect marker cookie\n const isAuthRedirect = await this.storage.get(\n AuthFlowCookie.AUTH_REDIRECT_MARKER,\n );\n await this.storage.delete(AuthFlowCookie.AUTH_REDIRECT_MARKER);\n\n // Get existing cookie to determine if we need to clear stale data\n const existingCookie = await this.storage.get(AuthFlowCookie.RETURN_URL);\n\n logger.debug(\"[handleDeepLinking] At login URL:\", {\n hasQueryParams,\n isAuthRedirect: !!isAuthRedirect,\n existingCookie,\n });\n\n if (isAuthRedirect) {\n // This is a redirect from auth middleware - preserve the existing cookie\n logger.debug(\n \"[handleDeepLinking] Auth redirect detected - preserving existing cookie\",\n );\n return existingCookie;\n }\n\n // Fresh navigation (not an auth redirect)\n if (hasQueryParams) {\n // Prepend basePath if configured (same reason as handleDeepLinkingAtProtectedRoute)\n let pathname = parsedUrl.pathname;\n if (this.authConfig.basePath) {\n pathname = prependBasePath(pathname, this.authConfig.basePath);\n }\n\n logger.debug(\"[handleDeepLinking] At login URL with query params:\", {\n originalPathname: parsedUrl.pathname,\n pathname,\n search: parsedUrl.search,\n deepLinkHandling,\n basePath: this.authConfig.basePath,\n loginSuccessUrl: this.authConfig.loginSuccessUrl,\n });\n\n // User visited with query params - capture them\n const returnTo = computeDeepLinkDestination(\n pathname,\n parsedUrl.search,\n parsedUrl.hash,\n originUrl,\n deepLinkHandling,\n this.authConfig.loginSuccessUrl,\n this.authConfig.basePath,\n );\n\n logger.debug(\"[handleDeepLinking] computeDeepLinkDestination result:\", {\n returnTo,\n });\n\n if (returnTo) {\n // Set the cookie with the computed return URL\n await this.storage.set(AuthFlowCookie.RETURN_URL, returnTo, {});\n logger.debug(\n `[handleDeepLinking] Fresh visit with params - setting cookie to \"${returnTo}\"`,\n );\n return returnTo;\n }\n } else if (existingCookie) {\n // Fresh visit to login URL without query params - clear any stale cookie\n // This handles the case where a user logs out from an account-specific URL\n // (e.g., /account/123 or /?account=123) and the cookie was re-set during\n // the logout redirect chain. Clearing it ensures the next login redirects\n // to the configured loginSuccessUrl, not the previous account's URL.\n await this.storage.delete(AuthFlowCookie.RETURN_URL);\n logger.debug(\n \"[handleDeepLinking] Fresh visit without params - cleared stale return URL cookie\",\n { clearedCookie: existingCookie },\n );\n }\n\n return null;\n }\n\n /**\n * Sets the auth redirect marker cookie. This marker helps distinguish auth redirects\n * from fresh navigations to the login page, preventing the deep link cookie from being\n * incorrectly overwritten.\n *\n * **Note:** This method is automatically called by `handleDeepLinking` when the user is\n * at a protected route. You typically do NOT need to call this method manually unless\n * you have a specific use case where you're not using `handleDeepLinking`.\n *\n * @example\n * ```typescript\n * // In most cases, just call handleDeepLinking - it sets the marker automatically:\n * if (!isAuthenticated) {\n * await req.civicAuth.handleDeepLinking(requestUrl, originUrl, req.method);\n * return res.redirect('/login');\n * }\n * ```\n */\n async setAuthRedirectMarker(): Promise<void> {\n const logger = loggers.server;\n const deepLinkHandling =\n this.authConfig.deepLinkHandling ?? \"queryParamsOnly\";\n\n if (deepLinkHandling === \"disabled\") {\n return;\n }\n\n await this.storage.set(AuthFlowCookie.AUTH_REDIRECT_MARKER, \"1\", {\n maxAge: AUTH_REDIRECT_MARKER_MAX_AGE,\n });\n logger.debug(\"[setAuthRedirectMarker] Set auth redirect marker cookie\");\n }\n\n /**\n * Framework-agnostic URL detection and resolution helpers\n * These methods handle proxy environments and can be used by any framework\n */\n\n /**\n * Try to URI decode a value, returning the original value on error\n */\n static tryUriDecode(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch (e) {\n console.error(\"Error decoding URI component:\", e);\n return value;\n }\n }\n\n /**\n * Get decoded query parameter from request\n */\n static getDecodedQueryParam(\n request: UrlDetectionRequest,\n paramName: string,\n ): string | null {\n const queryParam = request.searchParams.get(paramName);\n if (queryParam) {\n return CivicAuth.tryUriDecode(queryParam);\n }\n return null;\n }\n\n /**\n * Get value from cookie or query parameter (cookie takes precedence)\n */\n static getCookieOrQueryParam(\n request: UrlDetectionRequest,\n cookieName: string,\n queryName: string,\n ): string | null {\n // First check the cookie as it might have the full path with base directory\n const cookieValue = request.cookies.get(cookieName)?.value;\n if (cookieValue) {\n return CivicAuth.tryUriDecode(cookieValue);\n }\n\n // Fallback to query parameter\n return CivicAuth.getDecodedQueryParam(request, queryName);\n }\n\n /**\n * Get app URL from request (for proxy environment support)\n * Checks cookies first, then query parameters\n */\n static getAppUrl(request: UrlDetectionRequest): string | null {\n return CivicAuth.getCookieOrQueryParam(\n request,\n CodeVerifier.APP_URL,\n \"appUrl\",\n );\n }\n\n /**\n * Get login success URL with proper base URL handling\n * Extracts from state parameter or query parameters, resolves with baseUrl if provided\n */\n static getLoginSuccessUrl(\n request: UrlDetectionRequest,\n baseUrl?: string | null,\n ): string | null {\n const state = request.searchParams.get(\"state\");\n const loginSuccessUrl =\n loginSuccessUrlFromState(state) ||\n CivicAuth.getDecodedQueryParam(request, \"loginSuccessUrl\");\n\n if (!loginSuccessUrl) {\n return null;\n }\n\n return baseUrl ? new URL(loginSuccessUrl, baseUrl).href : loginSuccessUrl;\n }\n\n /**\n * Convert relative URL to absolute URL using appUrl for proxy environments\n */\n static toAbsoluteUrl(\n request: UrlDetectionRequest,\n url: string,\n appUrl?: string | null,\n ): string {\n if (url.startsWith(\"http\")) {\n return url;\n }\n\n // Use appUrl if available (for proxy environments), otherwise fall back to request origin\n const baseUrl = appUrl || new URL(request.url).origin;\n return new URL(url, baseUrl).href;\n }\n\n /**\n * Get post-logout redirect URL with proxy environment support\n */\n getPostLogoutRedirectUrl(request: UrlDetectionRequest): string {\n // Check if we have a target URL in the request (from middleware)\n const targetUrl = request.searchParams.get(\"targetUrl\");\n if (targetUrl) {\n return targetUrl;\n }\n\n const redirectTarget =\n this.authConfig.baseUrl ?? this.authConfig.loginSuccessUrl ?? \"/\";\n\n // If redirectTarget is absolute, use it as-is\n const isAbsoluteRedirect = /^(https?:\\/\\/|www\\.).+/i.test(redirectTarget);\n if (isAbsoluteRedirect) {\n return redirectTarget;\n }\n\n // Use appUrl from client for proxy environments\n const appUrl = CivicAuth.getAppUrl(request);\n if (appUrl) {\n return new URL(redirectTarget, appUrl).href;\n }\n\n // Fallback to request origin\n return new URL(request.url).origin;\n }\n\n /**\n * Smart callback handler that automatically detects frontend vs backend requests\n * and redirects appropriately. Use this instead of resolveOAuthAccessCode + manual redirect.\n *\n * @param params An object containing the authorization code, state, and the incoming request.\n * @param params.code The authorization code from query parameters.\n * @param params.state The OAuth state parameter.\n * @param params.req The incoming request object (e.g., from Express).\n * @param options Configuration options (frontendUrl override, apiResponse flag).\n * @returns Object with redirect information or HTML content for iframe completion.\n *\n * @example\n * ```javascript\n * app.get('/auth/callback', async (req, res) => {\n * const { code, state } = req.query;\n * // The request object 'req' is passed directly\n * const result = await req.civicAuth.handleCallback({ code, state, req });\n *\n * if (result.htmlContent) {\n * res.setHeader('Content-Type', 'text/html');\n * res.send(result.htmlContent);\n * } else if (result.redirectTo) {\n * res.redirect(result.redirectTo);\n * } else {\n * res.json({ success: true, user: result.user });\n * }\n * });\n * ```\n */\n async handleCallback(\n { code, state, req }: HandleCallbackParams,\n options?: {\n frontendUrl?: string;\n apiResponse?: boolean;\n },\n ): Promise<{\n redirectTo?: string;\n content?: string | { success: boolean; user?: User | null };\n }> {\n const logger = loggers.server;\n\n // Helper to clear the deep link cookie after successful auth.\n // This is centralized here so all framework implementations benefit.\n const clearReturnUrlCookie = async () => {\n try {\n await this.storage.delete(AuthFlowCookie.RETURN_URL);\n logger.debug(\n \"[handleCallback] Cleared deep link cookie after successful auth\",\n );\n } catch (error) {\n logger.warn(\"[handleCallback] Failed to clear deep link cookie\", {\n error,\n });\n }\n };\n\n // Handle same-domain callback for iframe workaround\n if (isSameDomainCallback(req)) {\n try {\n // Check if user is already authenticated before attempting token exchange\n const isAlreadyLoggedIn = await this.isLoggedIn();\n let user: User | null = null;\n\n if (isAlreadyLoggedIn) {\n // User is already authenticated, get existing user data\n user = await this.getUser();\n console.log(\n \"User already authenticated in same-domain callback:\",\n !!user,\n );\n } else {\n // For same-domain callbacks, we should have access to cookies\n const tokens = await this.resolveOAuthAccessCode(code, state);\n user = getUserFromTokens(tokens);\n console.log(\n \"Completed token exchange in same-domain callback:\",\n !!user,\n );\n }\n\n // Return JSON response for same-domain callback\n const currentUrl = new URL(req.url || \"\");\n\n // Extract and sanitize loginSuccessUrl (deep link) BEFORE cleaning up search params\n // Sanitization prevents open redirect attacks via malicious URLs in the query param\n const rawLoginSuccessUrl =\n currentUrl.searchParams.get(\"loginSuccessUrl\");\n const loginSuccessUrl = rawLoginSuccessUrl\n ? sanitizeReturnUrl(rawLoginSuccessUrl, currentUrl.origin)\n : null;\n\n const newSearchParams = new URLSearchParams(currentUrl.search);\n newSearchParams.delete(\"sameDomainCallback\");\n newSearchParams.delete(\"appUrl\");\n newSearchParams.delete(\"loginSuccessUrl\");\n\n // Use preserved deep link if available and valid, otherwise fall back to loginSuccessUrl or \"/\"\n // Note: Do NOT fall back to currentUrl.pathname as that's the callback URL, which would cause a loop\n let redirectUrl =\n loginSuccessUrl || this.authConfig.loginSuccessUrl || \"/\";\n\n // Apply basePath if configured\n if (this.authConfig.basePath) {\n redirectUrl = prependBasePath(redirectUrl, this.authConfig.basePath);\n }\n\n // Clear the deep link cookie after successful same-domain callback\n await clearReturnUrlCookie();\n\n return {\n content: {\n success: true,\n redirectUrl,\n } as { success: boolean; redirectUrl: string },\n };\n } catch (error) {\n console.error(\"Same-domain callback failed:\", error);\n throw error;\n }\n }\n\n // Try to resolve the OAuth code and create session\n let tokens: OIDCTokenResponseBody;\n let user: User | null;\n\n try {\n tokens = await this.resolveOAuthAccessCode(code, state);\n user = getUserFromTokens(tokens);\n\n // Clear the deep link cookie after successful token exchange\n await clearReturnUrlCookie();\n } catch (error) {\n // Check if this is a code verifier error and we're in iframe mode\n const isCodeVerifierError =\n error instanceof Error &&\n error.message.includes(\"Code verifier not found in storage\");\n\n if (isCodeVerifierError) {\n // First check if user is already authenticated before trying iframe workaround\n try {\n const isAlreadyLoggedIn = await this.isLoggedIn();\n\n if (isAlreadyLoggedIn) {\n // \"User already authenticated, skipping iframe workaround\",\n const user = await this.getUser();\n const loginSuccessUrlFromStateValue =\n loginSuccessUrlFromState(state);\n let frontendUrl =\n options?.frontendUrl ||\n loginSuccessUrlFromStateValue ||\n this.authConfig.loginSuccessUrl;\n\n // Apply basePath to frontendUrl if configured\n if (frontendUrl && this.authConfig.basePath) {\n frontendUrl = prependBasePath(\n frontendUrl,\n this.authConfig.basePath,\n );\n }\n\n // Check if this is an iframe context - if so, generate iframe completion HTML\n const stateDisplayMode = displayModeFromState(state, undefined);\n const isConfiguredForIframe = stateDisplayMode === \"iframe\";\n\n if (\n isConfiguredForIframe &&\n !this.authConfig.disableIframeDetection &&\n user &&\n frontendUrl\n ) {\n // Generating iframe completion HTML for already authenticated user\",\n const completionHtml = this.generateIframeCompletionHtml(\n user,\n frontendUrl,\n );\n return { content: completionHtml };\n }\n\n if (frontendUrl) {\n return { redirectTo: frontendUrl };\n } else {\n // Return JSON when no redirect destination is available.\n // This is intentional for API-style callbacks and cross-origin scenarios\n // where cookies aren't accessible to read config. The JSON response allows\n // clients (e.g., vanilla JS SDK) to handle the success case programmatically.\n return { content: { success: true, user } };\n }\n }\n } catch (authCheckError) {\n console.warn(\n \"Failed to check authentication status:\",\n authCheckError,\n );\n // Continue with iframe workaround if auth check fails\n }\n\n const stateDisplayMode = displayModeFromState(state, undefined);\n const isConfiguredForIframe = stateDisplayMode === \"iframe\";\n\n if (isConfiguredForIframe && !this.authConfig.disableIframeDetection) {\n // Generate HTML that will trigger same-domain callback\n const loginSuccessUrlFromStateValue = loginSuccessUrlFromState(state);\n let frontendUrl =\n options?.frontendUrl ||\n loginSuccessUrlFromStateValue ||\n this.authConfig.loginSuccessUrl;\n\n // Apply basePath to frontendUrl if configured\n if (frontendUrl && this.authConfig.basePath) {\n frontendUrl = prependBasePath(\n frontendUrl,\n this.authConfig.basePath,\n );\n }\n\n const callbackUrl = req.url || \"\";\n const sameDomainHtml = this.generateSameDomainCallbackHtml(\n callbackUrl,\n frontendUrl,\n );\n return { content: sameDomainHtml };\n }\n\n // For non-iframe mode or when iframe detection is disabled, return trigger text\n return {\n content: `<html lang=\"en\"><body><span style=\"display:none\">${TOKEN_EXCHANGE_TRIGGER_TEXT}</span></body></html>`,\n };\n }\n\n // Re-throw other errors\n throw error;\n }\n\n // Extract loginSuccessUrl from state if present\n const loginSuccessUrlFromStateValue = loginSuccessUrlFromState(state);\n\n // Priority: options.frontendUrl > loginSuccessUrl from state > config loginSuccessUrl\n let frontendUrl =\n options?.frontendUrl ||\n loginSuccessUrlFromStateValue ||\n this.authConfig.loginSuccessUrl;\n\n // Apply basePath to frontendUrl if configured\n if (frontendUrl && this.authConfig.basePath) {\n frontendUrl = prependBasePath(frontendUrl, this.authConfig.basePath);\n }\n\n // Priority 1: Check state for display mode configuration\n const stateDisplayMode = displayModeFromState(state, undefined);\n const isConfiguredForIframe = stateDisplayMode === \"iframe\";\n\n // Determine if this should be treated as an iframe request\n // Configuration (from state) takes precedence over auto-detection\n const shouldTreatAsIframe =\n isConfiguredForIframe && !this.authConfig.disableIframeDetection;\n\n const isTopLevelRedirect = req.headers[\"sec-fetch-dest\"] === \"document\";\n const isIframeRequest = req.headers[\"sec-fetch-dest\"] === \"iframe\";\n const isApiRequest =\n options?.apiResponse || req.headers.accept?.includes(\"application/json\");\n\n // Detect Safari or other browsers where iframe postMessage may fail due to cross-origin restrictions\n //TODO: Find a better way to detect this\n const userAgent = req.headers[\"user-agent\"] || \"\";\n const isSafari =\n userAgent.includes(\"Safari\") && !userAgent.includes(\"Chrome\");\n const isLikelyCrossOriginIframe =\n isSafari ||\n (userAgent.includes(\"WebKit\") && !userAgent.includes(\"Chrome\"));\n\n // Case 1: The request should be treated as iframe. Return HTML content.\n // Unless iframe detection is disabled via configuration OR we detect cross-origin issues\n if (\n shouldTreatAsIframe &&\n user &&\n frontendUrl &&\n !isLikelyCrossOriginIframe\n ) {\n const completionHtml = this.generateIframeCompletionHtml(\n user,\n frontendUrl,\n );\n return { content: completionHtml };\n }\n\n // Case 1b: Safari/cross-origin iframe case - redirect instead of HTML\n if (\n shouldTreatAsIframe &&\n user &&\n frontendUrl &&\n isLikelyCrossOriginIframe\n ) {\n return { redirectTo: frontendUrl };\n }\n\n // Case 2: The request is a top-level navigation. Return redirect URL.\n if (isTopLevelRedirect && frontendUrl) {\n return { redirectTo: frontendUrl };\n }\n\n // Case 2a: The request is from an iframe (detected by sec-fetch-dest)\n // Even if not configured for iframe in state, we should still generate iframe completion HTML\n if (isIframeRequest && user && frontendUrl) {\n if (isLikelyCrossOriginIframe) {\n console.log(\n \"Iframe request detected but cross-origin issues likely - redirecting\",\n );\n return { redirectTo: frontendUrl };\n } else {\n console.log(\n \"Generating iframe completion HTML for iframe request (detected by sec-fetch-dest)\",\n );\n const completionHtml = this.generateIframeCompletionHtml(\n user,\n frontendUrl,\n );\n return { content: completionHtml };\n }\n }\n\n // Case 3: The request is an API call. Return JSON content.\n if (isApiRequest) {\n return {\n content: {\n success: true,\n user,\n },\n };\n }\n\n // Fallback for older browsers or other contexts: if a frontend URL is configured,\n // assume a redirect to it.\n if (frontendUrl) {\n return { redirectTo: frontendUrl };\n }\n\n // Absolute fallback: redirect to loginSuccessUrl or \"/\"\n // Never return JSON for browser navigation - that would display raw JSON to the user\n const fallbackUrl = this.authConfig.loginSuccessUrl || \"/\";\n return {\n redirectTo: this.authConfig.basePath\n ? prependBasePath(fallbackUrl, this.authConfig.basePath)\n : fallbackUrl,\n };\n }\n\n /**\n * Generate HTML content for iframe completion that sends postMessage to parent\n */\n private generateIframeCompletionHtml(\n user: User,\n frontendUrl?: string,\n ): string {\n const escapedUser = JSON.stringify(user).replace(/'/g, \"\\\\'\");\n const clientId = this.authConfig.clientId;\n\n // Determine fallback redirect URL\n // Note: redirectUrl is the OAuth callback URL - it should NEVER be used as a post-login\n // destination as it would cause an infinite redirect loop in iframe mode.\n // postLogoutRedirectUrl is semantically incorrect for login success scenarios.\n const fallbackUrl = frontendUrl || this.authConfig.loginSuccessUrl || \"/\";\n\n return `\n <!DOCTYPE html>\n <html>\n <head>\n <title>Authentication Complete</title>\n <meta charset=\"utf-8\">\n </head>\n <body> \n <!-- Success signal for SignalObserver -->\n <div id=\"civic-auth-success-signal\" style=\"display: none;\" data-user-info='${escapedUser}'>\n ${TOKEN_EXCHANGE_SUCCESS_TEXT}\n </div>\n \n <script>\n // Send postMessage to parent to resolve authentication promise\n if (window.parent && window.parent !== window) {\n try {\n window.parent.postMessage({\n type: 'auth_success',\n detail: 'Authentication successful',\n data: {\n user: ${escapedUser},\n redirectUrl: ${JSON.stringify(fallbackUrl)}\n }\n }, '*');\n } catch (error) {\n console.error('❌ Failed to send postMessage:', error);\n }\n\n // Also send civicloginApp format message for compatibility\n try {\n window.parent.postMessage({\n source: 'civicloginApp',\n type: 'auth_success',\n clientId: ${JSON.stringify(clientId)},\n data: {\n user: ${escapedUser},\n redirectUrl: ${JSON.stringify(fallbackUrl)}\n }\n }, '*');\n } catch (error) {\n console.error('❌ Failed to send civicloginApp message:', error);\n }\n } else {\n console.log('❌ Not in iframe context or no parent window');\n }\n \n // Fallback redirect after 500ms delay to handle cases where:\n // 1. postMessage fails or parent doesn't respond\n // 2. Not in iframe context\n // 3. Any other edge cases where the user gets stuck\n setTimeout(function() {\n var redirectTarget = ${JSON.stringify(fallbackUrl)};\n console.log('🔄 Executing fallback redirect to:', redirectTarget);\n window.location.href = redirectTarget;\n }, 500);\n </script>\n </body>\n </html>\n `;\n }\n\n /**\n * Generate HTML response that triggers same-domain callback for iframe workaround\n */\n private generateSameDomainCallbackHtml = (\n callbackUrl: string,\n frontendUrl?: string,\n ): string => {\n const loginSuccessSegment = frontendUrl\n ? `&loginSuccessUrl=${encodeURIComponent(frontendUrl)}`\n : \"\";\n\n return `<html lang=\"en\">\n <body>\n <span style=\"display:none\">\n <script>\n window.onload = function () {\n // Get the complete URL including origin and path\n // This ensures we capture any base path like /directory\n const appUrl = window.location.href.substring(\n 0,\n window.location.href.indexOf(\"/api/auth\")\n );\n fetch('${callbackUrl}&sameDomainCallback=true&appUrl=' + encodeURIComponent(appUrl) + '${loginSuccessSegment}').then((response) => {\n response.json().then((jsonResponse) => {\n // For login: Redirect back to the callback route, so Case 2 in handleTokenExchangeComplete will be triggered\n // For logout: Redirect to the postLogoutRedirectUrl\n if(jsonResponse.redirectUrl) {\n window.location.href = jsonResponse.redirectUrl;\n }\n });\n });\n };\n </script>\n </span>\n </body>\n</html>\n`;\n };\n}\n"]}
|
package/dist/shared/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "@civic/auth:0.15.
|
|
1
|
+
export declare const VERSION = "@civic/auth:0.15.5";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/shared/version.js
CHANGED
|
@@ -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,oBAAoB,CAAC","sourcesContent":["// This is an auto-generated file. Do not edit.\n\nexport const VERSION = \"@civic/auth:0.15.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/shared/version.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAE/C,MAAM,CAAC,MAAM,OAAO,GAAG,oBAAoB,CAAC","sourcesContent":["// This is an auto-generated file. Do not edit.\n\nexport const VERSION = \"@civic/auth:0.15.5\";\n"]}
|