@civic/auth 0.10.0-beta.6 → 0.10.0-beta.7

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +1 -0
  3. package/dist/lib/logger.d.ts +1 -0
  4. package/dist/lib/logger.d.ts.map +1 -1
  5. package/dist/lib/logger.js +1 -0
  6. package/dist/lib/logger.js.map +1 -1
  7. package/dist/nextjs/hooks/useInitialAuthConfig.d.ts.map +1 -1
  8. package/dist/nextjs/hooks/useInitialAuthConfig.js +3 -0
  9. package/dist/nextjs/hooks/useInitialAuthConfig.js.map +1 -1
  10. package/dist/nextjs/middleware.d.ts +2 -1
  11. package/dist/nextjs/middleware.d.ts.map +1 -1
  12. package/dist/nextjs/middleware.js +33 -51
  13. package/dist/nextjs/middleware.js.map +1 -1
  14. package/dist/nextjs/routeHandler.d.ts +3 -0
  15. package/dist/nextjs/routeHandler.d.ts.map +1 -1
  16. package/dist/nextjs/routeHandler.js +3 -0
  17. package/dist/nextjs/routeHandler.js.map +1 -1
  18. package/dist/nextjs/utils.d.ts +16 -1
  19. package/dist/nextjs/utils.d.ts.map +1 -1
  20. package/dist/nextjs/utils.js +78 -10
  21. package/dist/nextjs/utils.js.map +1 -1
  22. package/dist/reactjs/core/GlobalAuthManager.d.ts +1 -0
  23. package/dist/reactjs/core/GlobalAuthManager.d.ts.map +1 -1
  24. package/dist/reactjs/core/GlobalAuthManager.js +5 -1
  25. package/dist/reactjs/core/GlobalAuthManager.js.map +1 -1
  26. package/dist/server/ServerAuthenticationResolver.d.ts +3 -2
  27. package/dist/server/ServerAuthenticationResolver.d.ts.map +1 -1
  28. package/dist/server/ServerAuthenticationResolver.js +23 -6
  29. package/dist/server/ServerAuthenticationResolver.js.map +1 -1
  30. package/dist/server/index.d.ts +1 -0
  31. package/dist/server/index.d.ts.map +1 -1
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/server/login.d.ts +2 -1
  34. package/dist/server/login.d.ts.map +1 -1
  35. package/dist/server/login.js.map +1 -1
  36. package/dist/server/session.d.ts +4 -3
  37. package/dist/server/session.d.ts.map +1 -1
  38. package/dist/server/session.js.map +1 -1
  39. package/dist/server/users.d.ts +4 -3
  40. package/dist/server/users.d.ts.map +1 -1
  41. package/dist/server/users.js.map +1 -1
  42. package/dist/services/types.d.ts +1 -1
  43. package/dist/services/types.d.ts.map +1 -1
  44. package/dist/services/types.js.map +1 -1
  45. package/dist/shared/lib/BrowserAuthenticationRefresher.d.ts.map +1 -1
  46. package/dist/shared/lib/BrowserAuthenticationRefresher.js +6 -6
  47. package/dist/shared/lib/BrowserAuthenticationRefresher.js.map +1 -1
  48. package/dist/shared/lib/session.d.ts +2 -1
  49. package/dist/shared/lib/session.d.ts.map +1 -1
  50. package/dist/shared/lib/session.js +5 -2
  51. package/dist/shared/lib/session.js.map +1 -1
  52. package/dist/shared/lib/util.d.ts +2 -2
  53. package/dist/shared/lib/util.d.ts.map +1 -1
  54. package/dist/shared/lib/util.js +4 -4
  55. package/dist/shared/lib/util.js.map +1 -1
  56. package/dist/shared/version.d.ts +1 -1
  57. package/dist/shared/version.js +1 -1
  58. package/dist/shared/version.js.map +1 -1
  59. package/dist/types.d.ts +1 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/dist/types.js.map +1 -1
  62. package/dist/vanillajs/auth/BackendAuthenticationRefresher.d.ts +4 -3
  63. package/dist/vanillajs/auth/BackendAuthenticationRefresher.d.ts.map +1 -1
  64. package/dist/vanillajs/auth/BackendAuthenticationRefresher.js +34 -21
  65. package/dist/vanillajs/auth/BackendAuthenticationRefresher.js.map +1 -1
  66. package/dist/vanillajs/auth/TokenRefresher.d.ts.map +1 -1
  67. package/dist/vanillajs/auth/TokenRefresher.js +3 -3
  68. package/dist/vanillajs/auth/TokenRefresher.js.map +1 -1
  69. package/package.json +1 -1
@@ -1,6 +1,9 @@
1
1
  import { NextResponse } from "next/server.js";
2
2
  import { loggers } from "../lib/logger.js";
3
3
  import picomatch from "picomatch";
4
+ import { CodeVerifier, OAuthTokenTypes, UserStorage, } from "../shared/lib/types.js";
5
+ import { clearTokens, getCookieConfiguration } from "../shared/lib/util.js";
6
+ import { CookieStorage } from "../server/index.js";
4
7
  const logger = loggers.nextjs.middleware;
5
8
  export const resolveCallbackUrl = (config, baseUrl) => {
6
9
  const callbackUrl = new URL(config?.callbackUrl, baseUrl).toString();
@@ -96,6 +99,55 @@ export const shouldSkipAuthForSystemUrls = (pathname, authConfig, method) => {
96
99
  }
97
100
  return isSystemUrl;
98
101
  };
102
+ /**
103
+ * CookieStorage implementation for NextJS middleware context that works with NextRequest
104
+ */
105
+ export class NextjsMiddlewareCookieStorage extends CookieStorage {
106
+ config;
107
+ request;
108
+ response;
109
+ constructor(config = {}, request, response) {
110
+ super({
111
+ secure: true,
112
+ httpOnly: true,
113
+ });
114
+ this.config = config;
115
+ this.request = request;
116
+ this.response = response;
117
+ }
118
+ async get(key) {
119
+ return this.request.cookies.get(key)?.value || null;
120
+ }
121
+ async set(key, value, cookieConfigOverride) {
122
+ const cookieSettings = this.config?.[key] || {
123
+ ...this.settings,
124
+ };
125
+ const dynamicConfig = getCookieConfiguration(this.request);
126
+ const useCookieSettings = {
127
+ ...cookieSettings,
128
+ ...cookieConfigOverride,
129
+ // Apply dynamic configuration for secure and sameSite
130
+ secure: dynamicConfig.secure,
131
+ sameSite: dynamicConfig.sameSite,
132
+ };
133
+ // Respect the httpOnly setting from configuration instead of hardcoding it
134
+ this.response.cookies.set(key, value, useCookieSettings);
135
+ }
136
+ async delete(key) {
137
+ // Get cookie configuration for this key to respect the path setting
138
+ const cookieSettings = this.config?.[key] || {};
139
+ // If we have a path configured, use it when deleting the cookie
140
+ if (cookieSettings.path) {
141
+ this.response.cookies.set(key, "", {
142
+ expires: new Date(0), // Expire in the past
143
+ path: cookieSettings.path,
144
+ });
145
+ }
146
+ else {
147
+ this.response.cookies.delete(key);
148
+ }
149
+ }
150
+ }
99
151
  /**
100
152
  * Handles authentication logic specifically for the login URL
101
153
  * Provides logging for login URL access patterns
@@ -128,17 +180,32 @@ export const shouldSkipAuthForRoutePatterns = (pathname, authConfig) => {
128
180
  }
129
181
  return false;
130
182
  };
183
+ export const copyCivicCookies = (sourceResponse, targetResponse) => {
184
+ const civicCookieNames = [
185
+ ...Object.values(OAuthTokenTypes),
186
+ ...Object.values(UserStorage),
187
+ ...Object.values(CodeVerifier),
188
+ ];
189
+ logger.debug("Copying Civic cookies:", {
190
+ src: sourceResponse.url,
191
+ target: targetResponse.url,
192
+ });
193
+ sourceResponse?.cookies
194
+ .getAll()
195
+ .filter((cookie) => civicCookieNames.includes(cookie.name))
196
+ .forEach((cookie) => {
197
+ logger.debug("Setting middlewareResponse cookie:", cookie);
198
+ targetResponse.cookies.set(cookie);
199
+ });
200
+ };
131
201
  /**
132
202
  * Handles final authentication logic for unauthenticated users on protected routes
133
203
  */
134
- export const handleUnauthenticatedUser = (session, request, authConfig) => {
135
- const targetUrl = getOriginUrl(request, authConfig) + request.nextUrl.pathname;
136
- // Clear expired/invalid tokens if they exist without refresh token
137
- if (session.accessToken || session.idToken) {
138
- const clearBadTokensUrl = new URL(authConfig.logoutCallbackUrl, getOriginUrl(request, authConfig));
139
- clearBadTokensUrl.searchParams.set("targetUrl", targetUrl);
140
- logger.debug(`→ Invalid tokens found without refresh - clearing via "${clearBadTokensUrl}"`);
141
- return redirectWithBasePath(authConfig, clearBadTokensUrl.toString());
204
+ export const handleUnauthenticatedUser = async (session, request, response, storage, authConfig) => {
205
+ // Clear expired/invalid tokens if they exist
206
+ if (session.accessToken || session.idToken || session.refreshToken) {
207
+ logger.debug(`→ Clearing expired/invalid tokens`);
208
+ await clearTokens(storage);
142
209
  }
143
210
  // Final fallback: redirect to login unless we're already there.
144
211
  const loginUrl = new URL(authConfig.loginUrl, getOriginUrl(request, authConfig));
@@ -149,9 +216,10 @@ export const handleUnauthenticatedUser = (session, request, authConfig) => {
149
216
  // The loginUrl from getOriginUrl already includes the basePath, but request.nextUrl.pathname does not. So we strip it off to enable comparison.
150
217
  if (request.nextUrl.pathname !== loginPathWithoutBasePath) {
151
218
  logger.debug(`→ No valid tokens found - redirecting to login "${redirectUrl}"`);
152
- return redirectWithBasePath(authConfig, redirectUrl);
219
+ const redirectedResponse = redirectWithBasePath(authConfig, redirectUrl);
220
+ return redirectedResponse;
153
221
  }
154
- return undefined;
222
+ return response;
155
223
  };
156
224
  /**
157
225
  * Prepends the basePath onto a given URL if it's not already there. Works for both relative and absolute URLs.
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/nextjs/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AuthConfig, AuthConfigWithDefaults } from \"@/nextjs/config.js\";\nimport type { NextRequest } from \"next/server.js\";\nimport { NextResponse } from \"next/server.js\";\nimport type { SessionData } from \"@/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport picomatch from \"picomatch\";\n\nconst logger = loggers.nextjs.middleware;\n\nexport const resolveCallbackUrl = (\n config: AuthConfigWithDefaults,\n baseUrl?: string,\n): string => {\n const callbackUrl = new URL(config?.callbackUrl, baseUrl).toString();\n return callbackUrl.toString();\n};\n\nexport function sanitizeBasePath(path: string): string {\n if (!path || path === \"/\") return \"\";\n\n // Ensure it starts with a slash\n const withLeadingSlash = path.startsWith(\"/\") ? path : `/${path}`;\n\n // Remove all trailing slashes (not just one)\n return withLeadingSlash.replace(/\\/+$/, \"\");\n}\n\n/**\n * Removes the basePath prefix from a pathname, properly handling edge cases\n * This is the inverse operation of adding basePath to a URL\n */\nexport function removeBasePathFromPath(\n pathname: string,\n basePath?: string,\n): string {\n if (!basePath || basePath === \"\" || basePath === \"/\") {\n return pathname;\n }\n\n // Sanitize the basePath to ensure consistent format\n const sanitizedBasePath = sanitizeBasePath(basePath);\n\n // Check if pathname starts with the basePath followed by a slash or end of string\n // This prevents partial matches like \"/app\" matching \"/application\"\n if (pathname === sanitizedBasePath) {\n // Exact match - return root\n return \"/\";\n } else if (pathname.startsWith(sanitizedBasePath + \"/\")) {\n // basePath followed by slash - remove basePath but keep the slash\n return pathname.slice(sanitizedBasePath.length);\n }\n\n // If basePath doesn't match as a complete path segment, return pathname as-is\n return pathname;\n}\n\nconst getOriginUrl = (\n request: NextRequest,\n authConfig: AuthConfigWithDefaults,\n): string => {\n // Use configured baseUrl if provided (for reverse proxy scenarios)\n if (authConfig.baseUrl) {\n return authConfig.baseUrl;\n }\n\n // Fallback to nextUrl.origin (includes port automatically)\n return request.nextUrl.origin;\n};\n\n// Matches globs:\n// Examples:\n// /user\n// /user/*\n// /user/**/info\nconst matchGlob = (pathname: string, globPattern: string) => {\n const matches = picomatch(globPattern);\n return matches(pathname);\n};\n\nconst matchesGlobs = (pathname: string, patterns: string[]) =>\n patterns.some((pattern) => {\n if (!pattern) return false;\n return matchGlob(pathname, pattern);\n });\n\n/**\n * Determines if we should attempt token refresh based on session state\n */\nexport const shouldAttemptRefresh = (session: SessionData): boolean => {\n return !session.authenticated && !!session.refreshToken;\n};\n\n/**\n * Creates a redirect response to the refresh endpoint\n */\nexport const createRefreshResponse = (\n request: NextRequest,\n authConfig: AuthConfigWithDefaults,\n): NextResponse | null => {\n const targetUrl = prependBasePath(\n getOriginUrl(request, authConfig) + request.nextUrl.pathname,\n authConfig.basePath || \"\",\n );\n\n // Check if we're already in a redirect flow\n if (request.nextUrl.searchParams.has(\"targetUrl\")) {\n return null;\n }\n\n // Create refresh redirect\n const refreshUrl = new URL(\n authConfig.refreshUrl,\n getOriginUrl(request, authConfig),\n );\n refreshUrl.searchParams.set(\"targetUrl\", targetUrl);\n\n logger.debug(\"→ Refresh token found - redirecting to refresh endpoint\");\n\n return redirectWithBasePath(authConfig, refreshUrl.toString());\n};\n\n/**\n * Checks if the current path is a system URL that should skip auth\n */\nexport const shouldSkipAuthForSystemUrls = (\n pathname: string,\n authConfig: AuthConfigWithDefaults,\n method: string,\n): boolean => {\n const systemUrls = [\n authConfig.callbackUrl,\n authConfig.challengeUrl,\n authConfig.logoutCallbackUrl,\n authConfig.logoutUrl,\n ];\n\n const isSystemUrl = systemUrls.includes(pathname) && method === \"GET\";\n\n if (isSystemUrl) {\n logger.debug(\n \"→ Skipping auth check - this a URL defined in authConfig\",\n pathname,\n );\n }\n\n return isSystemUrl;\n};\n\n/**\n * Handles authentication logic specifically for the login URL\n * Provides logging for login URL access patterns\n */\nexport const handleLoginUrl = (\n pathname: string,\n session: SessionData,\n authConfig: AuthConfigWithDefaults,\n): void => {\n if (pathname !== authConfig.loginUrl) {\n return;\n }\n\n // We are on the login URL - log the access pattern\n if (session.authenticated) {\n logger.debug(`→ Authenticated user accessing login page`);\n } else {\n logger.debug(`→ Unauthenticated user accessing login page`);\n }\n};\n\n/**\n * Checks if the current path should skip auth based on include/exclude patterns\n */\nexport const shouldSkipAuthForRoutePatterns = (\n pathname: string,\n authConfig: AuthConfigWithDefaults,\n): boolean => {\n // Check include patterns\n if (!matchesGlobs(pathname, authConfig.include)) {\n logger.debug(\n \"→ Skipping auth check - path not in include patterns\",\n pathname,\n );\n return true;\n }\n\n // Check exclude patterns\n if (matchesGlobs(pathname, authConfig.exclude)) {\n logger.debug(\"→ Skipping auth check - path in exclude patterns\", pathname);\n return true;\n }\n\n return false;\n};\n\n/**\n * Handles final authentication logic for unauthenticated users on protected routes\n */\nexport const handleUnauthenticatedUser = (\n session: SessionData,\n request: NextRequest,\n authConfig: AuthConfigWithDefaults,\n): NextResponse | undefined => {\n const targetUrl =\n getOriginUrl(request, authConfig) + request.nextUrl.pathname;\n\n // Clear expired/invalid tokens if they exist without refresh token\n if (session.accessToken || session.idToken) {\n const clearBadTokensUrl = new URL(\n authConfig.logoutCallbackUrl,\n getOriginUrl(request, authConfig),\n );\n clearBadTokensUrl.searchParams.set(\"targetUrl\", targetUrl);\n logger.debug(\n `→ Invalid tokens found without refresh - clearing via \"${clearBadTokensUrl}\"`,\n );\n return redirectWithBasePath(authConfig, clearBadTokensUrl.toString());\n }\n\n // Final fallback: redirect to login unless we're already there.\n const loginUrl = new URL(\n authConfig.loginUrl,\n getOriginUrl(request, authConfig),\n );\n const redirectUrl = `${loginUrl.toString()}`;\n const loginPathWithoutBasePath = removeBasePathFromPath(\n loginUrl.pathname,\n authConfig.basePath,\n );\n\n // If we're already at the login URL, the middleware will just return undefined.\n // This is to prevent an infinite redirect loop if middleware is applied to the login route itself.\n // The loginUrl from getOriginUrl already includes the basePath, but request.nextUrl.pathname does not. So we strip it off to enable comparison.\n if (request.nextUrl.pathname !== loginPathWithoutBasePath) {\n logger.debug(\n `→ No valid tokens found - redirecting to login \"${redirectUrl}\"`,\n );\n return redirectWithBasePath(authConfig, redirectUrl);\n }\n\n return undefined;\n};\n\n/**\n * Prepends the basePath onto a given URL if it's not already there. Works for both relative and absolute URLs.\n * @param url\n * @param basePath\n * @returns\n */\nexport const prependBasePath = (url: string, basePath: string) => {\n basePath = \"/\" + basePath.replace(/^\\/|\\/$/g, \"\"); // normalize basePath\n\n const isAbsolute = /^https?:\\/\\//.test(url);\n\n if (isAbsolute) {\n const u = new URL(url);\n if (!u.pathname.startsWith(basePath)) {\n u.pathname =\n basePath + (u.pathname.startsWith(\"/\") ? \"\" : \"/\") + u.pathname;\n }\n return u.toString();\n }\n\n return url.startsWith(basePath)\n ? url\n : basePath + (url.startsWith(\"/\") ? \"\" : \"/\") + url;\n};\n\nexport const redirectWithBasePath = (\n config: AuthConfig,\n targetUrl: string,\n): NextResponse =>\n NextResponse.redirect(prependBasePath(targetUrl, config.basePath || \"\"));\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/nextjs/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AuthConfig, AuthConfigWithDefaults } from \"@/nextjs/config.js\";\nimport type { NextRequest } from \"next/server.js\";\nimport { NextResponse } from \"next/server.js\";\nimport type { SessionData } from \"@/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport picomatch from \"picomatch\";\nimport {\n CodeVerifier,\n OAuthTokenTypes,\n UserStorage,\n type CookieConfig,\n type KeySetter,\n} from \"@/shared/lib/types.js\";\nimport { clearTokens, getCookieConfiguration } from \"@/shared/lib/util.js\";\nimport { CookieStorage } from \"@/server/index.js\";\n\nconst logger = loggers.nextjs.middleware;\n\nexport const resolveCallbackUrl = (\n config: AuthConfigWithDefaults,\n baseUrl?: string,\n): string => {\n const callbackUrl = new URL(config?.callbackUrl, baseUrl).toString();\n return callbackUrl.toString();\n};\n\nexport function sanitizeBasePath(path: string): string {\n if (!path || path === \"/\") return \"\";\n\n // Ensure it starts with a slash\n const withLeadingSlash = path.startsWith(\"/\") ? path : `/${path}`;\n\n // Remove all trailing slashes (not just one)\n return withLeadingSlash.replace(/\\/+$/, \"\");\n}\n\n/**\n * Removes the basePath prefix from a pathname, properly handling edge cases\n * This is the inverse operation of adding basePath to a URL\n */\nexport function removeBasePathFromPath(\n pathname: string,\n basePath?: string,\n): string {\n if (!basePath || basePath === \"\" || basePath === \"/\") {\n return pathname;\n }\n\n // Sanitize the basePath to ensure consistent format\n const sanitizedBasePath = sanitizeBasePath(basePath);\n\n // Check if pathname starts with the basePath followed by a slash or end of string\n // This prevents partial matches like \"/app\" matching \"/application\"\n if (pathname === sanitizedBasePath) {\n // Exact match - return root\n return \"/\";\n } else if (pathname.startsWith(sanitizedBasePath + \"/\")) {\n // basePath followed by slash - remove basePath but keep the slash\n return pathname.slice(sanitizedBasePath.length);\n }\n\n // If basePath doesn't match as a complete path segment, return pathname as-is\n return pathname;\n}\n\nconst getOriginUrl = (\n request: NextRequest,\n authConfig: AuthConfigWithDefaults,\n): string => {\n // Use configured baseUrl if provided (for reverse proxy scenarios)\n if (authConfig.baseUrl) {\n return authConfig.baseUrl;\n }\n\n // Fallback to nextUrl.origin (includes port automatically)\n return request.nextUrl.origin;\n};\n\n// Matches globs:\n// Examples:\n// /user\n// /user/*\n// /user/**/info\nconst matchGlob = (pathname: string, globPattern: string) => {\n const matches = picomatch(globPattern);\n return matches(pathname);\n};\n\nconst matchesGlobs = (pathname: string, patterns: string[]) =>\n patterns.some((pattern) => {\n if (!pattern) return false;\n return matchGlob(pathname, pattern);\n });\n\n/**\n * Determines if we should attempt token refresh based on session state\n */\nexport const shouldAttemptRefresh = (session: SessionData): boolean => {\n return !session.authenticated && !!session.refreshToken;\n};\n\n/**\n * Creates a redirect response to the refresh endpoint\n */\nexport const createRefreshResponse = (\n request: NextRequest,\n authConfig: AuthConfigWithDefaults,\n): NextResponse | null => {\n const targetUrl = prependBasePath(\n getOriginUrl(request, authConfig) + request.nextUrl.pathname,\n authConfig.basePath || \"\",\n );\n\n // Check if we're already in a redirect flow\n if (request.nextUrl.searchParams.has(\"targetUrl\")) {\n return null;\n }\n\n // Create refresh redirect\n const refreshUrl = new URL(\n authConfig.refreshUrl,\n getOriginUrl(request, authConfig),\n );\n refreshUrl.searchParams.set(\"targetUrl\", targetUrl);\n\n logger.debug(\"→ Refresh token found - redirecting to refresh endpoint\");\n\n return redirectWithBasePath(authConfig, refreshUrl.toString());\n};\n\n/**\n * Checks if the current path is a system URL that should skip auth\n */\nexport const shouldSkipAuthForSystemUrls = (\n pathname: string,\n authConfig: AuthConfigWithDefaults,\n method: string,\n): boolean => {\n const systemUrls = [\n authConfig.callbackUrl,\n authConfig.challengeUrl,\n authConfig.logoutCallbackUrl,\n authConfig.logoutUrl,\n ];\n\n const isSystemUrl = systemUrls.includes(pathname) && method === \"GET\";\n\n if (isSystemUrl) {\n logger.debug(\n \"→ Skipping auth check - this a URL defined in authConfig\",\n pathname,\n );\n }\n\n return isSystemUrl;\n};\n\n/**\n * CookieStorage implementation for NextJS middleware context that works with NextRequest\n */\nexport class NextjsMiddlewareCookieStorage extends CookieStorage {\n constructor(\n public config: Partial<Record<KeySetter, CookieConfig>> = {},\n private request: NextRequest,\n private response: NextResponse,\n ) {\n super({\n secure: true,\n httpOnly: true,\n });\n }\n\n async get(key: string): Promise<string | null> {\n return this.request.cookies.get(key)?.value || null;\n }\n\n async set(\n key: string,\n value: string,\n cookieConfigOverride: CookieConfig,\n ): Promise<void> {\n const cookieSettings = this.config?.[key as KeySetter] || {\n ...this.settings,\n };\n const dynamicConfig = getCookieConfiguration(this.request);\n\n const useCookieSettings = {\n ...cookieSettings,\n ...cookieConfigOverride,\n // Apply dynamic configuration for secure and sameSite\n secure: dynamicConfig.secure,\n sameSite: dynamicConfig.sameSite,\n };\n\n // Respect the httpOnly setting from configuration instead of hardcoding it\n this.response.cookies.set(key, value, useCookieSettings);\n }\n\n async delete(key: string): Promise<void> {\n // Get cookie configuration for this key to respect the path setting\n const cookieSettings = this.config?.[key as KeySetter] || {};\n\n // If we have a path configured, use it when deleting the cookie\n if (cookieSettings.path) {\n this.response.cookies.set(key, \"\", {\n expires: new Date(0), // Expire in the past\n path: cookieSettings.path,\n });\n } else {\n this.response.cookies.delete(key);\n }\n }\n}\n\n/**\n * Handles authentication logic specifically for the login URL\n * Provides logging for login URL access patterns\n */\nexport const handleLoginUrl = (\n pathname: string,\n session: SessionData,\n authConfig: AuthConfigWithDefaults,\n): void => {\n if (pathname !== authConfig.loginUrl) {\n return;\n }\n\n // We are on the login URL - log the access pattern\n if (session.authenticated) {\n logger.debug(`→ Authenticated user accessing login page`);\n } else {\n logger.debug(`→ Unauthenticated user accessing login page`);\n }\n};\n\n/**\n * Checks if the current path should skip auth based on include/exclude patterns\n */\nexport const shouldSkipAuthForRoutePatterns = (\n pathname: string,\n authConfig: AuthConfigWithDefaults,\n): boolean => {\n // Check include patterns\n if (!matchesGlobs(pathname, authConfig.include)) {\n logger.debug(\n \"→ Skipping auth check - path not in include patterns\",\n pathname,\n );\n return true;\n }\n\n // Check exclude patterns\n if (matchesGlobs(pathname, authConfig.exclude)) {\n logger.debug(\"→ Skipping auth check - path in exclude patterns\", pathname);\n return true;\n }\n\n return false;\n};\n\nexport const copyCivicCookies = (\n sourceResponse: NextResponse,\n targetResponse: NextResponse,\n) => {\n const civicCookieNames = [\n ...Object.values(OAuthTokenTypes),\n ...Object.values(UserStorage),\n ...Object.values(CodeVerifier),\n ];\n logger.debug(\"Copying Civic cookies:\", {\n src: sourceResponse.url,\n target: targetResponse.url,\n });\n sourceResponse?.cookies\n .getAll()\n .filter((cookie) =>\n civicCookieNames.includes(cookie.name as OAuthTokenTypes),\n )\n .forEach((cookie) => {\n logger.debug(\"Setting middlewareResponse cookie:\", cookie);\n targetResponse.cookies.set(cookie);\n });\n};\n/**\n * Handles final authentication logic for unauthenticated users on protected routes\n */\nexport const handleUnauthenticatedUser = async (\n session: SessionData,\n request: NextRequest,\n response: NextResponse,\n storage: NextjsMiddlewareCookieStorage,\n authConfig: AuthConfigWithDefaults,\n): Promise<NextResponse | undefined> => {\n // Clear expired/invalid tokens if they exist\n if (session.accessToken || session.idToken || session.refreshToken) {\n logger.debug(`→ Clearing expired/invalid tokens`);\n await clearTokens(storage);\n }\n\n // Final fallback: redirect to login unless we're already there.\n const loginUrl = new URL(\n authConfig.loginUrl,\n getOriginUrl(request, authConfig),\n );\n const redirectUrl = `${loginUrl.toString()}`;\n const loginPathWithoutBasePath = removeBasePathFromPath(\n loginUrl.pathname,\n authConfig.basePath,\n );\n\n // If we're already at the login URL, the middleware will just return undefined.\n // This is to prevent an infinite redirect loop if middleware is applied to the login route itself.\n // The loginUrl from getOriginUrl already includes the basePath, but request.nextUrl.pathname does not. So we strip it off to enable comparison.\n if (request.nextUrl.pathname !== loginPathWithoutBasePath) {\n logger.debug(\n `→ No valid tokens found - redirecting to login \"${redirectUrl}\"`,\n );\n const redirectedResponse = redirectWithBasePath(authConfig, redirectUrl);\n return redirectedResponse;\n }\n\n return response;\n};\n\n/**\n * Prepends the basePath onto a given URL if it's not already there. Works for both relative and absolute URLs.\n * @param url\n * @param basePath\n * @returns\n */\nexport const prependBasePath = (url: string, basePath: string) => {\n basePath = \"/\" + basePath.replace(/^\\/|\\/$/g, \"\"); // normalize basePath\n\n const isAbsolute = /^https?:\\/\\//.test(url);\n\n if (isAbsolute) {\n const u = new URL(url);\n if (!u.pathname.startsWith(basePath)) {\n u.pathname =\n basePath + (u.pathname.startsWith(\"/\") ? \"\" : \"/\") + u.pathname;\n }\n return u.toString();\n }\n\n return url.startsWith(basePath)\n ? url\n : basePath + (url.startsWith(\"/\") ? \"\" : \"/\") + url;\n};\n\nexport const redirectWithBasePath = (\n config: AuthConfig,\n targetUrl: string,\n): NextResponse =>\n NextResponse.redirect(prependBasePath(targetUrl, config.basePath || \"\"));\n"]}
@@ -87,6 +87,7 @@ declare class GlobalAuthManager {
87
87
  * Returns the same promise for concurrent calls with same config
88
88
  */
89
89
  initialize(config: GlobalAuthConfig): Promise<void>;
90
+ private defaultOnUrlChange;
90
91
  /**
91
92
  * Private method that does the actual initialization work
92
93
  */
@@ -1 +1 @@
1
- {"version":3,"file":"GlobalAuthManager.d.ts","sourceRoot":"","sources":["../../../src/reactjs/core/GlobalAuthManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GlobalAuthManager.d.ts","sourceRoot":"","sources":["../../../src/reactjs/core/GlobalAuthManager.ts"],"names":[],"mappings":"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"}
@@ -79,6 +79,10 @@ class GlobalAuthManager {
79
79
  }
80
80
  return this.initializationPromise;
81
81
  }
82
+ defaultOnUrlChange(url, source) {
83
+ console.log("onUrlChange triggering window redirect", { url, source });
84
+ window.location.href = url;
85
+ }
82
86
  /**
83
87
  * Private method that does the actual initialization work
84
88
  */
@@ -87,7 +91,7 @@ class GlobalAuthManager {
87
91
  this.callbacks = {
88
92
  onSignIn: config.onSignIn,
89
93
  onSignOut: config.onSignOut,
90
- onUrlChange: config.onUrlChange,
94
+ onUrlChange: config.onUrlChange || this.defaultOnUrlChange,
91
95
  };
92
96
  this.setState({ isLoading: true, error: null });
93
97
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"GlobalAuthManager.js","sourceRoot":"","sources":["../../../src/reactjs/core/GlobalAuthManager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * GlobalAuthManager - Singleton Authentication State Manager\n *\n * This module provides a global singleton that manages authentication state across a React application\n * without requiring React Context providers or prop drilling. It acts as a centralized authentication\n * manager that wraps the CivicAuth vanilla JavaScript library and provides a React-friendly interface.\n *\n * Key Features:\n * - Singleton pattern ensures single auth instance across the entire app\n * - Eliminates need for React Context providers and reduces bundle size\n * - Manages authentication state (user, session, loading, errors)\n * - Handles sign-in/sign-out flows with event-driven updates\n * - Provides idempotent initialization (safe to call multiple times)\n * - Supports multiple React hooks subscribing to the same auth state\n * - Automatic session refresh and user data synchronization\n *\n * Usage:\n * - Initialize once in your app with authentication configuration\n * - Use React hooks (useCivicAuth, useAuth, etc.) to access auth state\n * - The manager handles all underlying CivicAuth SDK interactions\n * - State updates are automatically propagated to all subscribed components\n *\n * This design pattern is particularly useful for:\n * - Large applications with many components needing auth state\n * - Avoiding provider wrapper hell in React component trees\n * - Ensuring consistent auth state across disconnected component hierarchies\n * - Server-side rendering scenarios where providers might be problematic\n */\n\nimport {\n CivicAuth,\n AuthenticationEvents,\n AuthEvent,\n} from \"../../vanillajs/index.js\";\nimport type {\n User,\n Session,\n CivicAuthClientConfig,\n} from \"../../vanillajs/index.js\";\nimport type { FrameworkType } from \"../../types.js\";\nimport type { VanillaJSDisplayMode } from \"../../vanillajs/auth/types/AuthTypes.js\";\nimport type { LoggingConfig } from \"@/vanillajs/auth/types/AuthTypes.js\";\nimport { DEFAULT_AUTH_PROCESS_TIMEOUT } from \"../../vanillajs/auth/types/AuthTypes.js\";\nimport type { BackendEndpoints } from \"@/server/config.js\";\n\n// Event payload interfaces\ninterface SignInErrorEvent {\n detail: string;\n}\n\nexport interface GlobalAuthConfig {\n clientId?: string;\n redirectUrl?: string;\n loginUrl?: string;\n config?: {\n oauthServer?: string;\n };\n displayMode?: VanillaJSDisplayMode;\n iframeMode?: \"modal\" | \"embedded\";\n nonce?: string;\n logoutRedirectUrl?: string;\n scopes?: string[];\n authProcessTimeout?: number;\n onSignIn?: (error?: Error) => void;\n onSignOut?: () => void;\n onUrlChange?: (url: string, source?: string) => void;\n logging?: LoggingConfig;\n preloadIframe?: boolean;\n autoRedirect?: boolean;\n /** Framework being used (for analytics) - internal use only */\n framework?: FrameworkType;\n backendEndpoints?: BackendEndpoints;\n targetContainerElement?: HTMLElement | string;\n loginSuccessUrl?: string;\n}\n\nexport type AuthStatus =\n | \"authenticated\"\n | \"unauthenticated\"\n | \"authenticating\"\n | \"error\"\n | \"signing_out\";\n\nexport interface GlobalAuthState {\n user: User | null;\n session: Session | null;\n isLoading: boolean;\n authStatus: AuthStatus;\n error: Error | null;\n displayMode?: VanillaJSDisplayMode;\n isPreloaded?: boolean;\n}\n\ntype StateListener = (state: GlobalAuthState) => void;\n\n/**\n * Global singleton that manages CivicAuth instance for React hooks\n * Eliminates the need for React providers\n */\nclass GlobalAuthManager {\n private static instance: GlobalAuthManager | null = null;\n private auth: CivicAuth | null = null;\n private events: AuthenticationEvents | null = null;\n private config: GlobalAuthConfig | null = null;\n private listeners = new Set<StateListener>();\n private callbacks: {\n onSignIn?: (error?: Error) => void;\n onSignOut?: () => void;\n onUrlChange?: (url: string, source?: string) => void;\n } = {};\n private initializationPromise: Promise<void> | null = null;\n\n private state: GlobalAuthState = {\n user: null,\n session: null,\n isLoading: false,\n authStatus: \"unauthenticated\",\n error: null,\n displayMode: undefined,\n };\n\n private constructor() {\n // Private constructor for singleton\n }\n\n static getInstance(): GlobalAuthManager {\n if (!GlobalAuthManager.instance) {\n GlobalAuthManager.instance = new GlobalAuthManager();\n }\n return GlobalAuthManager.instance;\n }\n\n /**\n * Initialize auth with config (idempotent)\n * Returns the same promise for concurrent calls with same config\n */\n async initialize(config: GlobalAuthConfig): Promise<void> {\n // If we have a promise and same config, return existing promise\n if (\n this.initializationPromise &&\n this.config &&\n this.isSameConfig(config)\n ) {\n return this.initializationPromise;\n }\n\n // If different config, reset everything\n if (this.config && !this.isSameConfig(config)) {\n this.initializationPromise = null;\n await this.cleanup();\n }\n\n // Create new initialization promise if needed\n if (!this.initializationPromise) {\n this.initializationPromise = this._doInitialize(config);\n }\n\n return this.initializationPromise;\n }\n\n /**\n * Private method that does the actual initialization work\n */\n private async _doInitialize(config: GlobalAuthConfig): Promise<void> {\n this.config = config;\n this.callbacks = {\n onSignIn: config.onSignIn,\n onSignOut: config.onSignOut,\n onUrlChange: config.onUrlChange,\n };\n this.setState({ isLoading: true, error: null });\n\n try {\n this.events = new AuthenticationEvents();\n this.setupEventListeners();\n\n // Build base config common to both branches\n const baseConfig = {\n ...config,\n oauthServerBaseUrl: config.config?.oauthServer,\n scopes: config.scopes || [\n \"openid\",\n \"profile\",\n \"email\",\n \"offline_access\",\n ],\n displayMode: config.displayMode || \"iframe\",\n iframeDisplayMode: config.iframeMode || \"modal\",\n nonce: config.nonce,\n authProcessTimeout:\n config.authProcessTimeout || DEFAULT_AUTH_PROCESS_TIMEOUT,\n preloadIframe: config.preloadIframe,\n autoRedirect: config.autoRedirect,\n events: this.events,\n framework: config.framework,\n };\n\n // Build auth config based on discriminated union requirements\n const authConfig: CivicAuthClientConfig = config.loginUrl\n ? {\n // Backend integration mode: loginUrl required, clientId optional\n ...baseConfig,\n loginUrl: config.loginUrl,\n clientId: config.clientId,\n }\n : {\n // Standard OAuth mode: clientId required, loginUrl optional\n ...baseConfig,\n clientId: config.clientId!,\n loginUrl: config.loginUrl,\n };\n\n // Only add redirectUrl if provided\n if (config.redirectUrl) {\n authConfig.redirectUrl = config.redirectUrl;\n }\n\n // Only add logoutRedirectUrl if provided\n if (config.logoutRedirectUrl) {\n authConfig.logoutRedirectUrl = config.logoutRedirectUrl;\n }\n\n // Only add loginSuccessUrl if provided\n if (config.loginSuccessUrl) {\n authConfig.loginSuccessUrl = config.loginSuccessUrl;\n }\n\n this.auth = await CivicAuth.create(authConfig);\n\n // Check initial auth state\n const isAuthenticated = await this.auth.isAuthenticated();\n\n if (isAuthenticated) {\n await this.refreshUserAndSession();\n this.setState({ authStatus: \"authenticated\" });\n } else {\n this.setState({ authStatus: \"unauthenticated\" });\n\n // Note: Preloading is now handled automatically by CivicAuth based on config.preloadIframe\n }\n\n this.setState({\n isLoading: false,\n displayMode: config.displayMode,\n });\n } catch (error) {\n // Reset promise on error so it can be retried\n this.initializationPromise = null;\n\n const authError =\n error instanceof Error\n ? error\n : new Error(\"Auth initialization failed\");\n this.setState({\n error: authError,\n authStatus: \"error\",\n isLoading: false,\n });\n throw authError;\n }\n }\n\n /**\n * Subscribe to state changes\n */\n subscribe(listener: StateListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Get current state\n */\n getState(): GlobalAuthState {\n return { ...this.state };\n }\n\n /**\n * Get current configuration\n * Returns the current config or null if not initialized\n */\n getConfig(): GlobalAuthConfig | null {\n return this.config ? { ...this.config } : null;\n }\n\n /**\n * Sign in\n */\n async signIn(): Promise<{ user: User }> {\n if (!this.auth) {\n throw new Error(\"Auth not initialized\");\n }\n\n const authResult = await this.auth.startAuthentication();\n\n // If authentication returned a user, use it directly\n if (authResult.user) {\n this.setState({\n user: authResult.user,\n authStatus: \"authenticated\",\n error: null,\n });\n return { user: authResult.user };\n }\n\n // Fallback: try to refresh user and session data\n await this.refreshUserAndSession();\n\n // Check the current state for the user after refresh\n const currentState = this.getState();\n\n if (!currentState.user) {\n throw new Error(\"Authentication succeeded but no user was returned\");\n }\n\n // Return the user object from current state\n return { user: currentState.user };\n }\n\n /**\n * Sign out\n */\n async signOut(): Promise<void> {\n if (!this.auth) {\n throw new Error(\"Auth not initialized\");\n }\n\n await this.auth.logout();\n this.setState({\n user: null,\n session: null,\n authStatus: \"unauthenticated\",\n });\n }\n\n /**\n * Check if authentication is preloaded and ready for instant sign-in\n * @returns True if an iframe is preloaded and ready\n */\n isAuthenticationPreloaded(): boolean {\n return this.auth?.isAuthenticationPreloaded() ?? false;\n }\n\n /**\n * Enable or disable iframe preloading\n */\n setPreloadEnabled(enabled: boolean): void {\n if (!this.auth) {\n throw new Error(\"Auth not initialized\");\n }\n this.auth.setPreloadEnabled(enabled);\n }\n\n /**\n * Check if iframe preloading is enabled\n */\n getPreloadEnabled(): boolean {\n return this.auth?.getPreloadEnabled() ?? true;\n }\n\n /**\n * Reloads embedded authentication interface\n *\n * This method is specifically designed for embedded mode scenarios where the iframe\n * needs to be recreated after navigation or DOM changes. It delegates to the\n * underlying CivicAuth instance's reloadEmbedded method.\n *\n * @returns Promise that resolves when the embedded iframe is created\n * @throws {Error} If not in embedded mode or if auth is not initialized\n */\n async reloadEmbedded(): Promise<void> {\n if (!this.auth) {\n throw new Error(\"Auth not initialized. Call initialize() first.\");\n }\n\n return this.auth.reloadEmbedded();\n }\n\n /**\n * Reset authentication state when detected from back-forward cache (bfcache)\n * This fixes issues where sign-in button becomes unclickable after back navigation\n */\n resetOnBfcache(): void {\n if (!this.auth) return;\n\n // Reset internal CivicAuth state first\n this.auth.resetOnBfcache();\n\n // Reset auth state to unauthenticated to allow new sign-in attempts\n this.setState({\n authStatus: \"unauthenticated\",\n isLoading: false,\n error: null,\n user: null,\n session: null,\n });\n\n // Ensure iframe preloading state is refreshed if needed\n if (this.getPreloadEnabled()) {\n this.auth.setPreloadEnabled(true);\n }\n }\n\n /**\n * Check if config is the same (for idempotent initialization)\n */\n private isSameConfig(newConfig: GlobalAuthConfig): boolean {\n if (!this.config) return false;\n return (\n this.config.clientId === newConfig.clientId &&\n this.config.config?.oauthServer === newConfig.config?.oauthServer &&\n this.config.displayMode === newConfig.displayMode &&\n this.config.nonce === newConfig.nonce &&\n this.config.preloadIframe === newConfig.preloadIframe &&\n this.config.autoRedirect === newConfig.autoRedirect\n );\n }\n\n /**\n * Setup event listeners for auth state changes\n */\n private setupEventListeners(): void {\n if (!this.events) return;\n\n this.events.on(AuthEvent.SIGN_IN_STARTED, () => {\n this.setState({\n isLoading: true,\n authStatus: \"authenticating\",\n error: null,\n });\n });\n\n this.events.on(AuthEvent.SIGN_IN_COMPLETE, () => {\n this.setState({\n isLoading: false,\n authStatus: \"authenticated\",\n error: null,\n });\n this.refreshUserAndSession();\n this.callbacks.onSignIn?.();\n });\n\n this.events.on(AuthEvent.SIGN_IN_ERROR, (event?: SignInErrorEvent) => {\n const errorDetail = event?.detail || \"Authentication failed\";\n const authError = new Error(errorDetail);\n this.setState({\n isLoading: false,\n authStatus: \"error\",\n error: authError,\n });\n this.callbacks.onSignIn?.(authError);\n });\n\n this.events.on(AuthEvent.SIGN_OUT_STARTED, () => {\n this.setState({\n isLoading: true,\n authStatus: \"signing_out\",\n error: null,\n });\n });\n\n this.events.on(AuthEvent.SIGN_OUT_COMPLETE, () => {\n this.setState({\n isLoading: false,\n authStatus: \"unauthenticated\",\n user: null,\n session: null,\n error: null,\n });\n this.callbacks.onSignOut?.();\n });\n\n this.events.on(AuthEvent.USER_SESSION_CHANGED, () => {\n this.refreshUserAndSession();\n });\n\n this.events.on(\n AuthEvent.URL_CHANGE,\n (event?: { detail?: string; url?: string; source?: string }) => {\n this.setState({\n authStatus: \"authenticating\", // Keep as authenticating since we're redirecting\n error: null,\n });\n // Call custom URL change handler if provided\n if (event?.url && this.callbacks.onUrlChange) {\n this.callbacks.onUrlChange(event.url, event.source);\n }\n },\n );\n }\n\n /**\n * Refresh user and session data\n */\n private async refreshUserAndSession(): Promise<void> {\n if (!this.auth) return;\n\n try {\n const [session, user] = await Promise.all([\n this.auth.getCurrentSession(),\n this.auth.getCurrentUser(),\n ]);\n\n this.setState({ session, user });\n } catch (error) {\n const sessionError =\n error instanceof Error ? error : new Error(\"Failed to get session\");\n this.setState({ error: sessionError });\n }\n }\n\n /**\n * Update state and notify listeners\n */\n private setState(updates: Partial<GlobalAuthState>): void {\n this.state = { ...this.state, ...updates };\n this.listeners.forEach((listener) => listener(this.state));\n }\n\n /**\n * Cleanup auth instance\n */\n private async cleanup(): Promise<void> {\n if (this.auth) {\n await this.auth.destroy();\n this.auth = null;\n }\n this.events = null;\n this.config = null;\n this.initializationPromise = null; // Reset promise for clean re-initialization\n }\n}\n\nexport { GlobalAuthManager };\n"]}
1
+ {"version":3,"file":"GlobalAuthManager.js","sourceRoot":"","sources":["../../../src/reactjs/core/GlobalAuthManager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * GlobalAuthManager - Singleton Authentication State Manager\n *\n * This module provides a global singleton that manages authentication state across a React application\n * without requiring React Context providers or prop drilling. It acts as a centralized authentication\n * manager that wraps the CivicAuth vanilla JavaScript library and provides a React-friendly interface.\n *\n * Key Features:\n * - Singleton pattern ensures single auth instance across the entire app\n * - Eliminates need for React Context providers and reduces bundle size\n * - Manages authentication state (user, session, loading, errors)\n * - Handles sign-in/sign-out flows with event-driven updates\n * - Provides idempotent initialization (safe to call multiple times)\n * - Supports multiple React hooks subscribing to the same auth state\n * - Automatic session refresh and user data synchronization\n *\n * Usage:\n * - Initialize once in your app with authentication configuration\n * - Use React hooks (useCivicAuth, useAuth, etc.) to access auth state\n * - The manager handles all underlying CivicAuth SDK interactions\n * - State updates are automatically propagated to all subscribed components\n *\n * This design pattern is particularly useful for:\n * - Large applications with many components needing auth state\n * - Avoiding provider wrapper hell in React component trees\n * - Ensuring consistent auth state across disconnected component hierarchies\n * - Server-side rendering scenarios where providers might be problematic\n */\n\nimport {\n CivicAuth,\n AuthenticationEvents,\n AuthEvent,\n} from \"../../vanillajs/index.js\";\nimport type {\n User,\n Session,\n CivicAuthClientConfig,\n} from \"../../vanillajs/index.js\";\nimport type { FrameworkType } from \"../../types.js\";\nimport type { VanillaJSDisplayMode } from \"../../vanillajs/auth/types/AuthTypes.js\";\nimport type { LoggingConfig } from \"@/vanillajs/auth/types/AuthTypes.js\";\nimport { DEFAULT_AUTH_PROCESS_TIMEOUT } from \"../../vanillajs/auth/types/AuthTypes.js\";\nimport type { BackendEndpoints } from \"@/server/config.js\";\n\n// Event payload interfaces\ninterface SignInErrorEvent {\n detail: string;\n}\n\nexport interface GlobalAuthConfig {\n clientId?: string;\n redirectUrl?: string;\n loginUrl?: string;\n config?: {\n oauthServer?: string;\n };\n displayMode?: VanillaJSDisplayMode;\n iframeMode?: \"modal\" | \"embedded\";\n nonce?: string;\n logoutRedirectUrl?: string;\n scopes?: string[];\n authProcessTimeout?: number;\n onSignIn?: (error?: Error) => void;\n onSignOut?: () => void;\n onUrlChange?: (url: string, source?: string) => void;\n logging?: LoggingConfig;\n preloadIframe?: boolean;\n autoRedirect?: boolean;\n /** Framework being used (for analytics) - internal use only */\n framework?: FrameworkType;\n backendEndpoints?: BackendEndpoints;\n targetContainerElement?: HTMLElement | string;\n loginSuccessUrl?: string;\n}\n\nexport type AuthStatus =\n | \"authenticated\"\n | \"unauthenticated\"\n | \"authenticating\"\n | \"error\"\n | \"signing_out\";\n\nexport interface GlobalAuthState {\n user: User | null;\n session: Session | null;\n isLoading: boolean;\n authStatus: AuthStatus;\n error: Error | null;\n displayMode?: VanillaJSDisplayMode;\n isPreloaded?: boolean;\n}\n\ntype StateListener = (state: GlobalAuthState) => void;\n\n/**\n * Global singleton that manages CivicAuth instance for React hooks\n * Eliminates the need for React providers\n */\nclass GlobalAuthManager {\n private static instance: GlobalAuthManager | null = null;\n private auth: CivicAuth | null = null;\n private events: AuthenticationEvents | null = null;\n private config: GlobalAuthConfig | null = null;\n private listeners = new Set<StateListener>();\n private callbacks: {\n onSignIn?: (error?: Error) => void;\n onSignOut?: () => void;\n onUrlChange?: (url: string, source?: string) => void;\n } = {};\n private initializationPromise: Promise<void> | null = null;\n\n private state: GlobalAuthState = {\n user: null,\n session: null,\n isLoading: false,\n authStatus: \"unauthenticated\",\n error: null,\n displayMode: undefined,\n };\n\n private constructor() {\n // Private constructor for singleton\n }\n\n static getInstance(): GlobalAuthManager {\n if (!GlobalAuthManager.instance) {\n GlobalAuthManager.instance = new GlobalAuthManager();\n }\n return GlobalAuthManager.instance;\n }\n\n /**\n * Initialize auth with config (idempotent)\n * Returns the same promise for concurrent calls with same config\n */\n async initialize(config: GlobalAuthConfig): Promise<void> {\n // If we have a promise and same config, return existing promise\n if (\n this.initializationPromise &&\n this.config &&\n this.isSameConfig(config)\n ) {\n return this.initializationPromise;\n }\n\n // If different config, reset everything\n if (this.config && !this.isSameConfig(config)) {\n this.initializationPromise = null;\n await this.cleanup();\n }\n\n // Create new initialization promise if needed\n if (!this.initializationPromise) {\n this.initializationPromise = this._doInitialize(config);\n }\n\n return this.initializationPromise;\n }\n\n private defaultOnUrlChange(url: string, source?: string) {\n console.log(\"onUrlChange triggering window redirect\", { url, source });\n window.location.href = url;\n }\n /**\n * Private method that does the actual initialization work\n */\n private async _doInitialize(config: GlobalAuthConfig): Promise<void> {\n this.config = config;\n this.callbacks = {\n onSignIn: config.onSignIn,\n onSignOut: config.onSignOut,\n onUrlChange: config.onUrlChange || this.defaultOnUrlChange,\n };\n this.setState({ isLoading: true, error: null });\n\n try {\n this.events = new AuthenticationEvents();\n this.setupEventListeners();\n\n // Build base config common to both branches\n const baseConfig = {\n ...config,\n oauthServerBaseUrl: config.config?.oauthServer,\n scopes: config.scopes || [\n \"openid\",\n \"profile\",\n \"email\",\n \"offline_access\",\n ],\n displayMode: config.displayMode || \"iframe\",\n iframeDisplayMode: config.iframeMode || \"modal\",\n nonce: config.nonce,\n authProcessTimeout:\n config.authProcessTimeout || DEFAULT_AUTH_PROCESS_TIMEOUT,\n preloadIframe: config.preloadIframe,\n autoRedirect: config.autoRedirect,\n events: this.events,\n framework: config.framework,\n };\n\n // Build auth config based on discriminated union requirements\n const authConfig: CivicAuthClientConfig = config.loginUrl\n ? {\n // Backend integration mode: loginUrl required, clientId optional\n ...baseConfig,\n loginUrl: config.loginUrl,\n clientId: config.clientId,\n }\n : {\n // Standard OAuth mode: clientId required, loginUrl optional\n ...baseConfig,\n clientId: config.clientId!,\n loginUrl: config.loginUrl,\n };\n\n // Only add redirectUrl if provided\n if (config.redirectUrl) {\n authConfig.redirectUrl = config.redirectUrl;\n }\n\n // Only add logoutRedirectUrl if provided\n if (config.logoutRedirectUrl) {\n authConfig.logoutRedirectUrl = config.logoutRedirectUrl;\n }\n\n // Only add loginSuccessUrl if provided\n if (config.loginSuccessUrl) {\n authConfig.loginSuccessUrl = config.loginSuccessUrl;\n }\n\n this.auth = await CivicAuth.create(authConfig);\n\n // Check initial auth state\n const isAuthenticated = await this.auth.isAuthenticated();\n\n if (isAuthenticated) {\n await this.refreshUserAndSession();\n this.setState({ authStatus: \"authenticated\" });\n } else {\n this.setState({ authStatus: \"unauthenticated\" });\n\n // Note: Preloading is now handled automatically by CivicAuth based on config.preloadIframe\n }\n\n this.setState({\n isLoading: false,\n displayMode: config.displayMode,\n });\n } catch (error) {\n // Reset promise on error so it can be retried\n this.initializationPromise = null;\n\n const authError =\n error instanceof Error\n ? error\n : new Error(\"Auth initialization failed\");\n this.setState({\n error: authError,\n authStatus: \"error\",\n isLoading: false,\n });\n throw authError;\n }\n }\n\n /**\n * Subscribe to state changes\n */\n subscribe(listener: StateListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Get current state\n */\n getState(): GlobalAuthState {\n return { ...this.state };\n }\n\n /**\n * Get current configuration\n * Returns the current config or null if not initialized\n */\n getConfig(): GlobalAuthConfig | null {\n return this.config ? { ...this.config } : null;\n }\n\n /**\n * Sign in\n */\n async signIn(): Promise<{ user: User }> {\n if (!this.auth) {\n throw new Error(\"Auth not initialized\");\n }\n\n const authResult = await this.auth.startAuthentication();\n\n // If authentication returned a user, use it directly\n if (authResult.user) {\n this.setState({\n user: authResult.user,\n authStatus: \"authenticated\",\n error: null,\n });\n return { user: authResult.user };\n }\n\n // Fallback: try to refresh user and session data\n await this.refreshUserAndSession();\n\n // Check the current state for the user after refresh\n const currentState = this.getState();\n\n if (!currentState.user) {\n throw new Error(\"Authentication succeeded but no user was returned\");\n }\n\n // Return the user object from current state\n return { user: currentState.user };\n }\n\n /**\n * Sign out\n */\n async signOut(): Promise<void> {\n if (!this.auth) {\n throw new Error(\"Auth not initialized\");\n }\n\n await this.auth.logout();\n this.setState({\n user: null,\n session: null,\n authStatus: \"unauthenticated\",\n });\n }\n\n /**\n * Check if authentication is preloaded and ready for instant sign-in\n * @returns True if an iframe is preloaded and ready\n */\n isAuthenticationPreloaded(): boolean {\n return this.auth?.isAuthenticationPreloaded() ?? false;\n }\n\n /**\n * Enable or disable iframe preloading\n */\n setPreloadEnabled(enabled: boolean): void {\n if (!this.auth) {\n throw new Error(\"Auth not initialized\");\n }\n this.auth.setPreloadEnabled(enabled);\n }\n\n /**\n * Check if iframe preloading is enabled\n */\n getPreloadEnabled(): boolean {\n return this.auth?.getPreloadEnabled() ?? true;\n }\n\n /**\n * Reloads embedded authentication interface\n *\n * This method is specifically designed for embedded mode scenarios where the iframe\n * needs to be recreated after navigation or DOM changes. It delegates to the\n * underlying CivicAuth instance's reloadEmbedded method.\n *\n * @returns Promise that resolves when the embedded iframe is created\n * @throws {Error} If not in embedded mode or if auth is not initialized\n */\n async reloadEmbedded(): Promise<void> {\n if (!this.auth) {\n throw new Error(\"Auth not initialized. Call initialize() first.\");\n }\n\n return this.auth.reloadEmbedded();\n }\n\n /**\n * Reset authentication state when detected from back-forward cache (bfcache)\n * This fixes issues where sign-in button becomes unclickable after back navigation\n */\n resetOnBfcache(): void {\n if (!this.auth) return;\n\n // Reset internal CivicAuth state first\n this.auth.resetOnBfcache();\n\n // Reset auth state to unauthenticated to allow new sign-in attempts\n this.setState({\n authStatus: \"unauthenticated\",\n isLoading: false,\n error: null,\n user: null,\n session: null,\n });\n\n // Ensure iframe preloading state is refreshed if needed\n if (this.getPreloadEnabled()) {\n this.auth.setPreloadEnabled(true);\n }\n }\n\n /**\n * Check if config is the same (for idempotent initialization)\n */\n private isSameConfig(newConfig: GlobalAuthConfig): boolean {\n if (!this.config) return false;\n return (\n this.config.clientId === newConfig.clientId &&\n this.config.config?.oauthServer === newConfig.config?.oauthServer &&\n this.config.displayMode === newConfig.displayMode &&\n this.config.nonce === newConfig.nonce &&\n this.config.preloadIframe === newConfig.preloadIframe &&\n this.config.autoRedirect === newConfig.autoRedirect\n );\n }\n\n /**\n * Setup event listeners for auth state changes\n */\n private setupEventListeners(): void {\n if (!this.events) return;\n\n this.events.on(AuthEvent.SIGN_IN_STARTED, () => {\n this.setState({\n isLoading: true,\n authStatus: \"authenticating\",\n error: null,\n });\n });\n\n this.events.on(AuthEvent.SIGN_IN_COMPLETE, () => {\n this.setState({\n isLoading: false,\n authStatus: \"authenticated\",\n error: null,\n });\n this.refreshUserAndSession();\n this.callbacks.onSignIn?.();\n });\n\n this.events.on(AuthEvent.SIGN_IN_ERROR, (event?: SignInErrorEvent) => {\n const errorDetail = event?.detail || \"Authentication failed\";\n const authError = new Error(errorDetail);\n this.setState({\n isLoading: false,\n authStatus: \"error\",\n error: authError,\n });\n this.callbacks.onSignIn?.(authError);\n });\n\n this.events.on(AuthEvent.SIGN_OUT_STARTED, () => {\n this.setState({\n isLoading: true,\n authStatus: \"signing_out\",\n error: null,\n });\n });\n\n this.events.on(AuthEvent.SIGN_OUT_COMPLETE, () => {\n this.setState({\n isLoading: false,\n authStatus: \"unauthenticated\",\n user: null,\n session: null,\n error: null,\n });\n this.callbacks.onSignOut?.();\n });\n\n this.events.on(AuthEvent.USER_SESSION_CHANGED, () => {\n this.refreshUserAndSession();\n });\n\n this.events.on(\n AuthEvent.URL_CHANGE,\n (event?: { detail?: string; url?: string; source?: string }) => {\n this.setState({\n authStatus: \"authenticating\", // Keep as authenticating since we're redirecting\n error: null,\n });\n // Call custom URL change handler if provided\n if (event?.url && this.callbacks.onUrlChange) {\n this.callbacks.onUrlChange(event.url, event.source);\n }\n },\n );\n }\n\n /**\n * Refresh user and session data\n */\n private async refreshUserAndSession(): Promise<void> {\n if (!this.auth) return;\n\n try {\n const [session, user] = await Promise.all([\n this.auth.getCurrentSession(),\n this.auth.getCurrentUser(),\n ]);\n\n this.setState({ session, user });\n } catch (error) {\n const sessionError =\n error instanceof Error ? error : new Error(\"Failed to get session\");\n this.setState({ error: sessionError });\n }\n }\n\n /**\n * Update state and notify listeners\n */\n private setState(updates: Partial<GlobalAuthState>): void {\n this.state = { ...this.state, ...updates };\n this.listeners.forEach((listener) => listener(this.state));\n }\n\n /**\n * Cleanup auth instance\n */\n private async cleanup(): Promise<void> {\n if (this.auth) {\n await this.auth.destroy();\n this.auth = null;\n }\n this.events = null;\n this.config = null;\n this.initializationPromise = null; // Reset promise for clean re-initialization\n }\n}\n\nexport { GlobalAuthManager };\n"]}
@@ -1,6 +1,7 @@
1
1
  import type { AuthStorage, Endpoints, OIDCTokenResponseBody, SessionData } from "../types.js";
2
2
  import type { AuthConfig } from "../server/config.js";
3
3
  import type { AuthenticationResolver } from "../services/types.ts";
4
+ import type { CookieStorage } from "../shared/lib/storage.js";
4
5
  export declare class ServerAuthenticationResolver implements AuthenticationResolver {
5
6
  readonly authConfig: AuthConfig;
6
7
  readonly storage: AuthStorage;
@@ -19,12 +20,12 @@ export declare class ServerAuthenticationResolver implements AuthenticationResol
19
20
  * returns The session data if the session is valid, otherwise an unauthenticated session
20
21
  * @returns {Promise<SessionData>}
21
22
  */
22
- validateExistingSession(autoRefresh?: boolean): Promise<SessionData>;
23
+ validateExistingSession(attemptRehydration?: boolean): Promise<SessionData>;
23
24
  get oauthServer(): string;
24
25
  init(): Promise<this>;
25
26
  tokenExchange(code: string, state: string): Promise<OIDCTokenResponseBody>;
26
27
  getSessionData(): Promise<SessionData | null>;
27
28
  getEndSessionEndpoint(): Promise<string | null>;
28
- static build(authConfig: AuthConfig, storage: AuthStorage, endpointOverrides?: Partial<Endpoints>): Promise<AuthenticationResolver>;
29
+ static build(authConfig: AuthConfig, storage: CookieStorage, endpointOverrides?: Partial<Endpoints>): Promise<AuthenticationResolver>;
29
30
  }
30
31
  //# sourceMappingURL=ServerAuthenticationResolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ServerAuthenticationResolver.d.ts","sourceRoot":"","sources":["../../src/server/ServerAuthenticationResolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,qBAAqB,EACrB,WAAW,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAUrD,OAAO,KAAK,EAAE,sBAAsB,EAAgB,MAAM,qBAAqB,CAAC;AAShF,qBAAa,4BAA6B,YAAW,sBAAsB;IAMvE,QAAQ,CAAC,UAAU,EAAE,UAAU;IAC/B,QAAQ,CAAC,OAAO,EAAE,WAAW;IAC7B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC;IAPjD,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,SAAS,CAAwB;IAEzC,OAAO;IAeP;;;;OAIG;IACG,gBAAgB,CACpB,WAAW,EAAE,WAAW,GAAG,IAAI,GAC9B,OAAO,CAAC,WAAW,CAAC;IA+DvB;;;OAGG;IACG,uBAAuB,CAAC,WAAW,UAAO,GAAG,OAAO,CAAC,WAAW,CAAC;IAmDvE,IAAI,WAAW,IAAI,MAAM,CAExB;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBrB,aAAa,CACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,qBAAqB,CAAC;IA4C3B,cAAc,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAc7C,qBAAqB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;WAOxC,KAAK,CAChB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,WAAW,EACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GACrC,OAAO,CAAC,sBAAsB,CAAC;CAUnC"}
1
+ {"version":3,"file":"ServerAuthenticationResolver.d.ts","sourceRoot":"","sources":["../../src/server/ServerAuthenticationResolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,qBAAqB,EACrB,WAAW,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAUrD,OAAO,KAAK,EAAE,sBAAsB,EAAgB,MAAM,qBAAqB,CAAC;AAMhF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAI7D,qBAAa,4BAA6B,YAAW,sBAAsB;IAMvE,QAAQ,CAAC,UAAU,EAAE,UAAU;IAC/B,QAAQ,CAAC,OAAO,EAAE,WAAW;IAC7B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC;IAPjD,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,SAAS,CAAwB;IAEzC,OAAO;IAeP;;;;OAIG;IACG,gBAAgB,CACpB,WAAW,EAAE,WAAW,GAAG,IAAI,GAC9B,OAAO,CAAC,WAAW,CAAC;IA8EvB;;;OAGG;IACG,uBAAuB,CAC3B,kBAAkB,UAAO,GACxB,OAAO,CAAC,WAAW,CAAC;IAqDvB,IAAI,WAAW,IAAI,MAAM,CAExB;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBrB,aAAa,CACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,qBAAqB,CAAC;IA4C3B,cAAc,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAc7C,qBAAqB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;WAOxC,KAAK,CAChB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,aAAa,EACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GACrC,OAAO,CAAC,sBAAsB,CAAC;CAUnC"}
@@ -4,7 +4,7 @@ import { clearTokens, clearUser, exchangeTokens, getEndpointsWithOverrides, retr
4
4
  import { DEFAULT_AUTH_SERVER } from "../constants.js";
5
5
  import { CodeVerifier } from "../shared/lib/types.js";
6
6
  import { loggers } from "../lib/logger.js";
7
- import { getUser } from "../shared/lib/session.js";
7
+ import { getUser, getUserFromTokens } from "../shared/lib/session.js";
8
8
  import { GenericUserSession } from "../shared/lib/UserSession.js";
9
9
  const logger = loggers.services.validation;
10
10
  export class ServerAuthenticationResolver {
@@ -55,8 +55,24 @@ export class ServerAuthenticationResolver {
55
55
  }
56
56
  // Validate the refreshed tokens
57
57
  await validateOauth2Tokens(tokenResponseBody, this.endpoints.jwks, this.oauth2client, this.oauthServer);
58
- // Store the refreshed tokens
59
58
  await storeServerTokens(this.storage, tokenResponseBody);
59
+ // Store user data if a user can be derived from the tokens
60
+ try {
61
+ const user = await getUserFromTokens(tokenResponseBody);
62
+ if (user) {
63
+ const userSession = new GenericUserSession(this.storage);
64
+ await userSession.set(user);
65
+ logger.debug("User cookie stored successfully", {
66
+ hasUser: !!user,
67
+ });
68
+ }
69
+ else {
70
+ logger.warn("No user found after token exchange");
71
+ }
72
+ }
73
+ catch (error) {
74
+ logger.error("Failed to store user cookie:", error);
75
+ }
60
76
  // Construct a refreshed session with the new tokens
61
77
  return {
62
78
  authenticated: true,
@@ -64,6 +80,7 @@ export class ServerAuthenticationResolver {
64
80
  accessToken: tokenResponseBody.access_token,
65
81
  refreshToken: tokenResponseBody.refresh_token,
66
82
  oidcSessionExpiresAt: tokenResponseBody.oidc_session_expires_at,
83
+ wasRehydrated: true,
67
84
  };
68
85
  }
69
86
  catch (error) {
@@ -80,14 +97,14 @@ export class ServerAuthenticationResolver {
80
97
  * returns The session data if the session is valid, otherwise an unauthenticated session
81
98
  * @returns {Promise<SessionData>}
82
99
  */
83
- async validateExistingSession(autoRefresh = true) {
100
+ async validateExistingSession(attemptRehydration = true) {
84
101
  // TODO: investigate a more peformant way to validate a server session
85
102
  // other than using JWKS and JWT verification which is what validateOauth2Tokens uses
86
103
  const sessionData = await this.getSessionData();
87
104
  // If we don't have an ID token, try to refresh if we have a refresh token
88
105
  // Access token is no longer required for authentication
89
106
  if (!sessionData?.idToken) {
90
- if (autoRefresh) {
107
+ if (attemptRehydration) {
91
108
  const refreshedSessionData = await this.tryRefreshTokens(sessionData);
92
109
  if (refreshedSessionData.authenticated) {
93
110
  return refreshedSessionData;
@@ -112,8 +129,8 @@ export class ServerAuthenticationResolver {
112
129
  return sessionData;
113
130
  }
114
131
  catch (error) {
115
- logger.warn("Error validating tokens", { error, autoRefresh });
116
- if (autoRefresh) {
132
+ logger.warn("Error validating tokens", { error });
133
+ if (attemptRehydration) {
117
134
  // If token validation fails, try to refresh tokens
118
135
  const refreshedSessionData = await this.tryRefreshTokens(sessionData);
119
136
  if (refreshedSessionData.authenticated) {
@@ -1 +1 @@
1
- {"version":3,"file":"ServerAuthenticationResolver.js","sourceRoot":"","sources":["../../src/server/ServerAuthenticationResolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { GenericPublicClientPKCEProducer } from \"@/services/PKCE.js\";\nimport { OAuth2Client } from \"oslo/oauth2\";\nimport type {\n AuthStorage,\n Endpoints,\n OIDCTokenResponseBody,\n SessionData,\n} from \"@/types.js\";\nimport type { AuthConfig } from \"@/server/config.js\";\nimport {\n clearTokens,\n clearUser,\n exchangeTokens,\n getEndpointsWithOverrides,\n retrieveTokens,\n storeServerTokens,\n validateOauth2Tokens,\n} from \"@/shared/lib/util.js\";\nimport type { AuthenticationResolver, PKCEProducer } from \"@/services/types.ts\";\nimport { DEFAULT_AUTH_SERVER } from \"@/constants.js\";\nimport { CodeVerifier } from \"@/shared/lib/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { getUser } from \"@/shared/lib/session.js\";\nimport { GenericUserSession } from \"@/shared/lib/UserSession.js\";\n\nconst logger = loggers.services.validation;\n\nexport class ServerAuthenticationResolver implements AuthenticationResolver {\n private pkceProducer: PKCEProducer | null;\n private oauth2client: OAuth2Client | undefined;\n private endpoints: Endpoints | undefined;\n\n private constructor(\n readonly authConfig: AuthConfig,\n readonly storage: AuthStorage,\n readonly endpointOverrides?: Partial<Endpoints>,\n ) {\n // Determine if PKCE should be used based on config\n // Default to true\n const usePkce = authConfig.pkce !== false;\n\n // Only create PKCE producer if we're using PKCE\n this.pkceProducer = usePkce\n ? new GenericPublicClientPKCEProducer(storage)\n : null;\n }\n\n /**\n * Attempts to refresh tokens if a refresh token is available\n * @param sessionData Current session data\n * @returns Updated session data\n */\n async tryRefreshTokens(\n sessionData: SessionData | null,\n ): Promise<SessionData> {\n logger.debug(\"tryRefreshTokens\", { sessionData });\n // If there's a refresh token, attempt to refresh tokens\n if (sessionData?.refreshToken) {\n try {\n // Only attempt refresh if we have necessary components\n if (!this.oauth2client || !this.endpoints?.jwks) {\n await this.init();\n }\n\n if (!this.oauth2client || !this.endpoints?.jwks) {\n throw new Error(\n \"Failed to initialize OAuth client for token refresh\",\n );\n }\n\n // Use the oauth2client to refresh the access token\n const refreshOptions: Record<string, string> = {};\n if (this.authConfig.clientSecret) {\n refreshOptions.credentials = this.authConfig.clientSecret;\n refreshOptions.authenticateWith = \"request_body\";\n }\n\n const tokenResponseBody = (await this.oauth2client.refreshAccessToken(\n sessionData.refreshToken,\n refreshOptions,\n )) as OIDCTokenResponseBody;\n\n if (!tokenResponseBody) {\n throw new Error(\"Failed to get token response from refresh\");\n }\n\n // Validate the refreshed tokens\n await validateOauth2Tokens(\n tokenResponseBody,\n this.endpoints.jwks,\n this.oauth2client,\n this.oauthServer,\n );\n\n // Store the refreshed tokens\n await storeServerTokens(this.storage, tokenResponseBody);\n\n // Construct a refreshed session with the new tokens\n return {\n authenticated: true,\n idToken: tokenResponseBody.id_token,\n accessToken: tokenResponseBody.access_token,\n refreshToken: tokenResponseBody.refresh_token,\n oidcSessionExpiresAt: tokenResponseBody.oidc_session_expires_at,\n };\n } catch (error) {\n logger.warn(\"Failed to refresh tokens\", error);\n await clearTokens(this.storage);\n await clearUser(this.storage);\n return { ...sessionData, authenticated: false };\n }\n }\n\n // No refresh token available\n return { ...sessionData, authenticated: false };\n }\n\n /**\n * returns The session data if the session is valid, otherwise an unauthenticated session\n * @returns {Promise<SessionData>}\n */\n async validateExistingSession(autoRefresh = true): Promise<SessionData> {\n // TODO: investigate a more peformant way to validate a server session\n // other than using JWKS and JWT verification which is what validateOauth2Tokens uses\n const sessionData = await this.getSessionData();\n\n // If we don't have an ID token, try to refresh if we have a refresh token\n // Access token is no longer required for authentication\n if (!sessionData?.idToken) {\n if (autoRefresh) {\n const refreshedSessionData = await this.tryRefreshTokens(sessionData);\n if (refreshedSessionData.authenticated) {\n return refreshedSessionData;\n }\n }\n return { ...sessionData, authenticated: false };\n }\n\n // Initialize if needed\n if (!this.endpoints?.jwks || !this.oauth2client) await this.init();\n\n if (!this.endpoints?.jwks) {\n throw new Error(\"JWKS endpoint not found\");\n }\n\n try {\n // Validate existing tokens - access token validation happens only if it exists\n await validateOauth2Tokens(\n {\n access_token: sessionData.accessToken, // May be undefined\n id_token: sessionData.idToken, // Always required\n refresh_token: sessionData.refreshToken,\n oidc_session_expires_at: sessionData.oidcSessionExpiresAt,\n },\n this.endpoints.jwks,\n this.oauth2client!,\n this.oauthServer,\n );\n return sessionData;\n } catch (error) {\n logger.warn(\"Error validating tokens\", { error, autoRefresh });\n if (autoRefresh) {\n // If token validation fails, try to refresh tokens\n const refreshedSessionData = await this.tryRefreshTokens(sessionData);\n if (refreshedSessionData.authenticated) {\n return refreshedSessionData;\n }\n }\n return { ...sessionData, authenticated: false };\n }\n }\n\n get oauthServer(): string {\n return this.authConfig.oauthServer || DEFAULT_AUTH_SERVER;\n }\n\n async init(): Promise<this> {\n // Ensure clientId is present for OAuth operations\n if (!this.authConfig.clientId) {\n throw new Error(\"clientId is required for OAuth server operations\");\n }\n\n // resolve oauth config\n this.endpoints = await getEndpointsWithOverrides(\n this.oauthServer,\n this.endpointOverrides,\n );\n this.oauth2client = new OAuth2Client(\n this.authConfig.clientId,\n this.endpoints.auth,\n this.endpoints.token,\n {\n redirectURI: this.authConfig.redirectUrl,\n },\n );\n\n return this;\n }\n\n async tokenExchange(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n if (!this.oauth2client) await this.init();\n\n // Check if we're using PKCE and validate code verifier if needed\n if (this.pkceProducer) {\n const codeVerifier = await this.pkceProducer.getCodeVerifier();\n if (!codeVerifier) throw new Error(\"Code verifier not found in storage\");\n }\n\n // exchange auth code for tokens\n const tokens = await exchangeTokens(\n code,\n state,\n this.pkceProducer,\n this.oauth2client!, // clean up types here to avoid the ! operator\n this.oauthServer,\n this.endpoints!, // clean up types here to avoid the ! operator\n this.authConfig.clientSecret,\n );\n\n await storeServerTokens(this.storage, tokens);\n\n // Store user data in cookie (like VanillaJS implementation does)\n try {\n const user = await getUser(this.storage);\n if (user) {\n const userSession = new GenericUserSession(this.storage);\n await userSession.set(user);\n logger.debug(\"User cookie stored successfully\", { hasUser: !!user });\n } else {\n logger.warn(\"No user found after token exchange\");\n }\n } catch (error) {\n logger.error(\"Failed to store user cookie:\", error);\n // Don't throw - tokens are already stored, this is just for client-side convenience\n }\n\n // the code verifier should be single-use, so we delete it if using PKCE\n if (this.pkceProducer) {\n await this.storage.delete(CodeVerifier.COOKIE_NAME);\n }\n return tokens;\n }\n\n async getSessionData(): Promise<SessionData | null> {\n const storageData = await retrieveTokens(this.storage);\n\n if (!storageData) return null;\n\n return {\n authenticated: !!storageData.id_token, // User is authenticated if they have an ID token\n idToken: storageData.id_token,\n accessToken: storageData.access_token, // Optional\n refreshToken: storageData.refresh_token,\n oidcSessionExpiresAt: storageData.oidc_session_expires_at,\n };\n }\n\n async getEndSessionEndpoint(): Promise<string | null> {\n if (!this.endpoints) {\n return null;\n }\n return this.endpoints.endsession;\n }\n\n static async build(\n authConfig: AuthConfig,\n storage: AuthStorage,\n endpointOverrides?: Partial<Endpoints>,\n ): Promise<AuthenticationResolver> {\n const resolver = new ServerAuthenticationResolver(\n authConfig,\n storage,\n endpointOverrides,\n );\n await resolver.init();\n\n return resolver;\n }\n}\n"]}
1
+ {"version":3,"file":"ServerAuthenticationResolver.js","sourceRoot":"","sources":["../../src/server/ServerAuthenticationResolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { GenericPublicClientPKCEProducer } from \"@/services/PKCE.js\";\nimport { OAuth2Client } from \"oslo/oauth2\";\nimport type {\n AuthStorage,\n Endpoints,\n OIDCTokenResponseBody,\n SessionData,\n} from \"@/types.js\";\nimport type { AuthConfig } from \"@/server/config.js\";\nimport {\n clearTokens,\n clearUser,\n exchangeTokens,\n getEndpointsWithOverrides,\n retrieveTokens,\n storeServerTokens,\n validateOauth2Tokens,\n} from \"@/shared/lib/util.js\";\nimport type { AuthenticationResolver, PKCEProducer } from \"@/services/types.ts\";\nimport { DEFAULT_AUTH_SERVER } from \"@/constants.js\";\nimport { CodeVerifier } from \"@/shared/lib/types.js\";\nimport { loggers } from \"@/lib/logger.js\";\nimport { getUser, getUserFromTokens } from \"@/shared/lib/session.js\";\nimport { GenericUserSession } from \"@/shared/lib/UserSession.js\";\nimport type { CookieStorage } from \"@/shared/lib/storage.js\";\n\nconst logger = loggers.services.validation;\n\nexport class ServerAuthenticationResolver implements AuthenticationResolver {\n private pkceProducer: PKCEProducer | null;\n private oauth2client: OAuth2Client | undefined;\n private endpoints: Endpoints | undefined;\n\n private constructor(\n readonly authConfig: AuthConfig,\n readonly storage: AuthStorage,\n readonly endpointOverrides?: Partial<Endpoints>,\n ) {\n // Determine if PKCE should be used based on config\n // Default to true\n const usePkce = authConfig.pkce !== false;\n\n // Only create PKCE producer if we're using PKCE\n this.pkceProducer = usePkce\n ? new GenericPublicClientPKCEProducer(storage)\n : null;\n }\n\n /**\n * Attempts to refresh tokens if a refresh token is available\n * @param sessionData Current session data\n * @returns Updated session data\n */\n async tryRefreshTokens(\n sessionData: SessionData | null,\n ): Promise<SessionData> {\n logger.debug(\"tryRefreshTokens\", { sessionData });\n // If there's a refresh token, attempt to refresh tokens\n if (sessionData?.refreshToken) {\n try {\n // Only attempt refresh if we have necessary components\n if (!this.oauth2client || !this.endpoints?.jwks) {\n await this.init();\n }\n\n if (!this.oauth2client || !this.endpoints?.jwks) {\n throw new Error(\n \"Failed to initialize OAuth client for token refresh\",\n );\n }\n\n // Use the oauth2client to refresh the access token\n const refreshOptions: Record<string, string> = {};\n if (this.authConfig.clientSecret) {\n refreshOptions.credentials = this.authConfig.clientSecret;\n refreshOptions.authenticateWith = \"request_body\";\n }\n\n const tokenResponseBody = (await this.oauth2client.refreshAccessToken(\n sessionData.refreshToken,\n refreshOptions,\n )) as OIDCTokenResponseBody;\n\n if (!tokenResponseBody) {\n throw new Error(\"Failed to get token response from refresh\");\n }\n\n // Validate the refreshed tokens\n await validateOauth2Tokens(\n tokenResponseBody,\n this.endpoints.jwks,\n this.oauth2client,\n this.oauthServer,\n );\n\n await storeServerTokens(this.storage, tokenResponseBody);\n\n // Store user data if a user can be derived from the tokens\n try {\n const user = await getUserFromTokens(tokenResponseBody);\n if (user) {\n const userSession = new GenericUserSession(this.storage);\n await userSession.set(user);\n logger.debug(\"User cookie stored successfully\", {\n hasUser: !!user,\n });\n } else {\n logger.warn(\"No user found after token exchange\");\n }\n } catch (error) {\n logger.error(\"Failed to store user cookie:\", error);\n }\n // Construct a refreshed session with the new tokens\n return {\n authenticated: true,\n idToken: tokenResponseBody.id_token,\n accessToken: tokenResponseBody.access_token,\n refreshToken: tokenResponseBody.refresh_token,\n oidcSessionExpiresAt: tokenResponseBody.oidc_session_expires_at,\n wasRehydrated: true,\n };\n } catch (error) {\n logger.warn(\"Failed to refresh tokens\", error);\n await clearTokens(this.storage);\n await clearUser(this.storage);\n return { ...sessionData, authenticated: false };\n }\n }\n\n // No refresh token available\n return { ...sessionData, authenticated: false };\n }\n\n /**\n * returns The session data if the session is valid, otherwise an unauthenticated session\n * @returns {Promise<SessionData>}\n */\n async validateExistingSession(\n attemptRehydration = true,\n ): Promise<SessionData> {\n // TODO: investigate a more peformant way to validate a server session\n // other than using JWKS and JWT verification which is what validateOauth2Tokens uses\n const sessionData = await this.getSessionData();\n\n // If we don't have an ID token, try to refresh if we have a refresh token\n // Access token is no longer required for authentication\n if (!sessionData?.idToken) {\n if (attemptRehydration) {\n const refreshedSessionData = await this.tryRefreshTokens(sessionData);\n if (refreshedSessionData.authenticated) {\n return refreshedSessionData;\n }\n }\n\n return { ...sessionData, authenticated: false };\n }\n\n // Initialize if needed\n if (!this.endpoints?.jwks || !this.oauth2client) await this.init();\n\n if (!this.endpoints?.jwks) {\n throw new Error(\"JWKS endpoint not found\");\n }\n\n try {\n // Validate existing tokens - access token validation happens only if it exists\n await validateOauth2Tokens(\n {\n access_token: sessionData.accessToken, // May be undefined\n id_token: sessionData.idToken, // Always required\n refresh_token: sessionData.refreshToken,\n oidc_session_expires_at: sessionData.oidcSessionExpiresAt,\n },\n this.endpoints.jwks,\n this.oauth2client!,\n this.oauthServer,\n );\n return sessionData;\n } catch (error) {\n logger.warn(\"Error validating tokens\", { error });\n if (attemptRehydration) {\n // If token validation fails, try to refresh tokens\n const refreshedSessionData = await this.tryRefreshTokens(sessionData);\n if (refreshedSessionData.authenticated) {\n return refreshedSessionData;\n }\n }\n\n return { ...sessionData, authenticated: false };\n }\n }\n\n get oauthServer(): string {\n return this.authConfig.oauthServer || DEFAULT_AUTH_SERVER;\n }\n\n async init(): Promise<this> {\n // Ensure clientId is present for OAuth operations\n if (!this.authConfig.clientId) {\n throw new Error(\"clientId is required for OAuth server operations\");\n }\n\n // resolve oauth config\n this.endpoints = await getEndpointsWithOverrides(\n this.oauthServer,\n this.endpointOverrides,\n );\n this.oauth2client = new OAuth2Client(\n this.authConfig.clientId,\n this.endpoints.auth,\n this.endpoints.token,\n {\n redirectURI: this.authConfig.redirectUrl,\n },\n );\n\n return this;\n }\n\n async tokenExchange(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n if (!this.oauth2client) await this.init();\n\n // Check if we're using PKCE and validate code verifier if needed\n if (this.pkceProducer) {\n const codeVerifier = await this.pkceProducer.getCodeVerifier();\n if (!codeVerifier) throw new Error(\"Code verifier not found in storage\");\n }\n\n // exchange auth code for tokens\n const tokens = await exchangeTokens(\n code,\n state,\n this.pkceProducer,\n this.oauth2client!, // clean up types here to avoid the ! operator\n this.oauthServer,\n this.endpoints!, // clean up types here to avoid the ! operator\n this.authConfig.clientSecret,\n );\n\n await storeServerTokens(this.storage, tokens);\n\n // Store user data in cookie (like VanillaJS implementation does)\n try {\n const user = await getUser(this.storage);\n if (user) {\n const userSession = new GenericUserSession(this.storage);\n await userSession.set(user);\n logger.debug(\"User cookie stored successfully\", { hasUser: !!user });\n } else {\n logger.warn(\"No user found after token exchange\");\n }\n } catch (error) {\n logger.error(\"Failed to store user cookie:\", error);\n // Don't throw - tokens are already stored, this is just for client-side convenience\n }\n\n // the code verifier should be single-use, so we delete it if using PKCE\n if (this.pkceProducer) {\n await this.storage.delete(CodeVerifier.COOKIE_NAME);\n }\n return tokens;\n }\n\n async getSessionData(): Promise<SessionData | null> {\n const storageData = await retrieveTokens(this.storage);\n\n if (!storageData) return null;\n\n return {\n authenticated: !!storageData.id_token, // User is authenticated if they have an ID token\n idToken: storageData.id_token,\n accessToken: storageData.access_token, // Optional\n refreshToken: storageData.refresh_token,\n oidcSessionExpiresAt: storageData.oidc_session_expires_at,\n };\n }\n\n async getEndSessionEndpoint(): Promise<string | null> {\n if (!this.endpoints) {\n return null;\n }\n return this.endpoints.endsession;\n }\n\n static async build(\n authConfig: AuthConfig,\n storage: CookieStorage,\n endpointOverrides?: Partial<Endpoints>,\n ): Promise<AuthenticationResolver> {\n const resolver = new ServerAuthenticationResolver(\n authConfig,\n storage,\n endpointOverrides,\n );\n await resolver.init();\n\n return resolver;\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  export { CookieStorage } from "../shared/lib/storage.js";
2
+ export type { AuthStorage } from "../types.js";
2
3
  export type { SessionStorage, CookieStorageSettings, } from "../shared/lib/storage.js";
3
4
  export type { AuthConfig } from "./config.js";
4
5
  export { CivicAuth } from "./session.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,YAAY,EACV,cAAc,EACd,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAExE,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EACV,cAAc,EACd,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAExE,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,CAAC;AAEf,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAMxD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAuB,MAAM,YAAY,CAAC;AASjD,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC","sourcesContent":["import { printVersion } from \"@/shared/index.js\";\nprintVersion();\n\nexport { CookieStorage } from \"@/shared/lib/storage.js\";\nexport type {\n SessionStorage,\n CookieStorageSettings,\n} from \"@/shared/lib/storage.js\";\nexport type { AuthConfig } from \"./config.js\";\nexport { CivicAuth } from \"./session.js\";\nexport { type RequestContext } from \"./login.js\";\n\nexport type { CivicAuthServerOptions } from \"@/server/types/express.js\";\n\nexport type {\n HandleCallbackRequest,\n HandleCallbackParams,\n UrlDetectionRequest,\n} from \"./session.js\";\nexport { buildLoginUrl, resolveOAuthAccessCode, isLoggedIn } from \"./login.js\";\nexport { buildLogoutRedirectUrl } from \"./logout.js\";\nexport { refreshTokens } from \"./refresh.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,CAAC;AAEf,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAOxD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAuB,MAAM,YAAY,CAAC;AASjD,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC","sourcesContent":["import { printVersion } from \"@/shared/index.js\";\nprintVersion();\n\nexport { CookieStorage } from \"@/shared/lib/storage.js\";\nexport type { AuthStorage } from \"@/types.js\";\nexport type {\n SessionStorage,\n CookieStorageSettings,\n} from \"@/shared/lib/storage.js\";\nexport type { AuthConfig } from \"./config.js\";\nexport { CivicAuth } from \"./session.js\";\nexport { type RequestContext } from \"./login.js\";\n\nexport type { CivicAuthServerOptions } from \"@/server/types/express.js\";\n\nexport type {\n HandleCallbackRequest,\n HandleCallbackParams,\n UrlDetectionRequest,\n} from \"./session.js\";\nexport { buildLoginUrl, resolveOAuthAccessCode, isLoggedIn } from \"./login.js\";\nexport { buildLogoutRedirectUrl } from \"./logout.js\";\nexport { refreshTokens } from \"./refresh.js\";\n"]}
@@ -1,5 +1,6 @@
1
1
  import type { AuthStorage, OIDCTokenResponseBody, FrameworkType } from "../types.js";
2
2
  import type { AuthConfig } from "../server/config.ts";
3
+ import type { CookieStorage } from "./index.js";
3
4
  /**
4
5
  * Context interface for detecting frontend vs backend requests
5
6
  */
@@ -16,7 +17,7 @@ export interface RequestContext {
16
17
  * @param storage The place that this server uses to store session data (e.g. a cookie store)
17
18
  * @param config Oauth Server configuration
18
19
  */
19
- export declare function resolveOAuthAccessCode(code: string, state: string, storage: AuthStorage, config: AuthConfig): Promise<OIDCTokenResponseBody>;
20
+ export declare function resolveOAuthAccessCode(code: string, state: string, storage: CookieStorage, config: AuthConfig): Promise<OIDCTokenResponseBody>;
20
21
  export declare function isLoggedIn(storage: AuthStorage): Promise<boolean>;
21
22
  export declare function buildLoginUrl(config: Pick<AuthConfig, "clientId" | "redirectUrl"> & Partial<Pick<AuthConfig, "oauthServer" | "pkce" | "clientSecret">> & {
22
23
  scopes?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/server/login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,qBAAqB,EACrB,aAAa,EACd,MAAM,YAAY,CAAC;AAKpB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,qBAAqB,CAAC,CAWhC;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,CAAC,GAClD,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,GAAG,cAAc,CAAC,CAAC,GAAG;IACnE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,EACH,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,GAAG,CAAC,CA4Cd"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/server/login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,qBAAqB,EACrB,aAAa,EACd,MAAM,YAAY,CAAC;AAKpB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,qBAAqB,CAAC,CAWhC;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,CAAC,GAClD,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,GAAG,MAAM,GAAG,cAAc,CAAC,CAAC,GAAG;IACnE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,EACH,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,GAAG,CAAC,CA4Cd"}