@civic/auth 0.9.1-beta.1 → 0.9.1-beta.4

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 (89) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/lib/logger.d.ts +6 -0
  3. package/dist/lib/logger.d.ts.map +1 -1
  4. package/dist/lib/logger.js +7 -0
  5. package/dist/lib/logger.js.map +1 -1
  6. package/dist/lib/oauth.d.ts +2 -1
  7. package/dist/lib/oauth.d.ts.map +1 -1
  8. package/dist/lib/oauth.js +1 -1
  9. package/dist/lib/oauth.js.map +1 -1
  10. package/dist/nextjs/config.d.ts +2 -4
  11. package/dist/nextjs/config.d.ts.map +1 -1
  12. package/dist/nextjs/config.js +4 -57
  13. package/dist/nextjs/config.js.map +1 -1
  14. package/dist/react-router-7/components/UserButton.d.ts +15 -0
  15. package/dist/react-router-7/components/UserButton.d.ts.map +1 -0
  16. package/dist/react-router-7/components/UserButton.js +112 -0
  17. package/dist/react-router-7/components/UserButton.js.map +1 -0
  18. package/dist/react-router-7/components/UserButtonPresentation.d.ts +10 -0
  19. package/dist/react-router-7/components/UserButtonPresentation.d.ts.map +1 -0
  20. package/dist/react-router-7/components/UserButtonPresentation.js +19 -0
  21. package/dist/react-router-7/components/UserButtonPresentation.js.map +1 -0
  22. package/dist/react-router-7/config.d.ts +113 -0
  23. package/dist/react-router-7/config.d.ts.map +1 -0
  24. package/dist/react-router-7/config.js +88 -0
  25. package/dist/react-router-7/config.js.map +1 -0
  26. package/dist/react-router-7/cookies.d.ts +41 -0
  27. package/dist/react-router-7/cookies.d.ts.map +1 -0
  28. package/dist/react-router-7/cookies.js +188 -0
  29. package/dist/react-router-7/cookies.js.map +1 -0
  30. package/dist/react-router-7/index.d.ts +10 -0
  31. package/dist/react-router-7/index.d.ts.map +1 -0
  32. package/dist/react-router-7/index.js +12 -0
  33. package/dist/react-router-7/index.js.map +1 -0
  34. package/dist/react-router-7/routeHandler.d.ts +54 -0
  35. package/dist/react-router-7/routeHandler.d.ts.map +1 -0
  36. package/dist/react-router-7/routeHandler.js +397 -0
  37. package/dist/react-router-7/routeHandler.js.map +1 -0
  38. package/dist/react-router-7/useUser.d.ts +38 -0
  39. package/dist/react-router-7/useUser.d.ts.map +1 -0
  40. package/dist/react-router-7/useUser.js +92 -0
  41. package/dist/react-router-7/useUser.js.map +1 -0
  42. package/dist/reactjs/core/GlobalAuthManager.d.ts +6 -4
  43. package/dist/reactjs/core/GlobalAuthManager.d.ts.map +1 -1
  44. package/dist/reactjs/core/GlobalAuthManager.js +17 -6
  45. package/dist/reactjs/core/GlobalAuthManager.js.map +1 -1
  46. package/dist/reactjs/hooks/useUser.js.map +1 -1
  47. package/dist/server/config.d.ts +1 -0
  48. package/dist/server/config.d.ts.map +1 -1
  49. package/dist/server/config.js.map +1 -1
  50. package/dist/server/session.d.ts.map +1 -1
  51. package/dist/server/session.js +1 -0
  52. package/dist/server/session.js.map +1 -1
  53. package/dist/services/AuthenticationService.d.ts.map +1 -1
  54. package/dist/services/AuthenticationService.js +0 -5
  55. package/dist/services/AuthenticationService.js.map +1 -1
  56. package/dist/shared/hooks/useCivicAuthConfig.d.ts +1 -1
  57. package/dist/shared/hooks/useCivicAuthConfig.d.ts.map +1 -1
  58. package/dist/shared/lib/cookieConfig.d.ts +46 -0
  59. package/dist/shared/lib/cookieConfig.d.ts.map +1 -0
  60. package/dist/shared/lib/cookieConfig.js +99 -0
  61. package/dist/shared/lib/cookieConfig.js.map +1 -0
  62. package/dist/shared/lib/util.d.ts +5 -0
  63. package/dist/shared/lib/util.d.ts.map +1 -1
  64. package/dist/shared/lib/util.js +65 -3
  65. package/dist/shared/lib/util.js.map +1 -1
  66. package/dist/shared/version.d.ts +1 -1
  67. package/dist/shared/version.js +1 -1
  68. package/dist/shared/version.js.map +1 -1
  69. package/dist/types.d.ts +1 -1
  70. package/dist/types.d.ts.map +1 -1
  71. package/dist/types.js.map +1 -1
  72. package/dist/vanillajs/auth/CivicAuth.d.ts +1 -1
  73. package/dist/vanillajs/auth/CivicAuth.d.ts.map +1 -1
  74. package/dist/vanillajs/auth/CivicAuth.js +33 -12
  75. package/dist/vanillajs/auth/CivicAuth.js.map +1 -1
  76. package/dist/vanillajs/auth/SessionManager.d.ts +9 -1
  77. package/dist/vanillajs/auth/SessionManager.d.ts.map +1 -1
  78. package/dist/vanillajs/auth/SessionManager.js +103 -47
  79. package/dist/vanillajs/auth/SessionManager.js.map +1 -1
  80. package/dist/vanillajs/auth/config/ConfigProcessor.d.ts.map +1 -1
  81. package/dist/vanillajs/auth/config/ConfigProcessor.js +16 -2
  82. package/dist/vanillajs/auth/config/ConfigProcessor.js.map +1 -1
  83. package/dist/vanillajs/auth/handlers/MessageHandler.d.ts.map +1 -1
  84. package/dist/vanillajs/auth/handlers/MessageHandler.js +3 -0
  85. package/dist/vanillajs/auth/handlers/MessageHandler.js.map +1 -1
  86. package/dist/vanillajs/iframe/IframeManager.d.ts.map +1 -1
  87. package/dist/vanillajs/iframe/IframeManager.js +13 -0
  88. package/dist/vanillajs/iframe/IframeManager.js.map +1 -1
  89. package/package.json +12 -3
@@ -5,6 +5,9 @@ import { retrieveTokens, clearTokens, getBackendEndpoints, } from "../../shared/
5
5
  import { getUser } from "../../shared/lib/session.js";
6
6
  import { GenericUserSession } from "../../shared/lib/UserSession.js";
7
7
  import { AUTOREFRESH_TIMEOUT_NAME, REFRESH_IN_PROGRESS, } from "../../constants.js";
8
+ import { verify } from "@civic/auth-verify";
9
+ import { OAuthTokenTypes } from "../../shared/lib/types.js";
10
+ import { addSlashIfNeeded } from "../../lib/oauth.js";
8
11
  export class SessionManager {
9
12
  storage;
10
13
  events;
@@ -49,7 +52,18 @@ export class SessionManager {
49
52
  const tokens = await retrieveTokens(this.storage);
50
53
  const user = await this.getCurrentUser();
51
54
  // If we have tokens and user (normal flow), create full session
52
- if (tokens?.id_token && user) {
55
+ if ((tokens?.id_token || tokens?.access_token) && user) {
56
+ // Validate all available tokens
57
+ const validationResults = await this.validateTokens({
58
+ id_token: tokens.id_token,
59
+ access_token: tokens.access_token,
60
+ });
61
+ // Check if any tokens failed validation
62
+ if (!validationResults.idTokenValid ||
63
+ !validationResults.accessTokenValid) {
64
+ this.logger.warn("Token validation failed in getCurrentSession, returning null");
65
+ return null;
66
+ }
53
67
  return {
54
68
  user,
55
69
  accessToken: tokens.access_token,
@@ -99,30 +113,36 @@ export class SessionManager {
99
113
  this.logger.debug("No tokens accessible, checking backend session...", await this.checkBackendSession());
100
114
  return await this.checkBackendSession();
101
115
  }
102
- // If we have an ID token, validate it
116
+ // If we have tokens, validate them
103
117
  if (hasIdToken) {
104
- const isTokenValid = await this.validateToken(tokens.id_token);
105
- if (isTokenValid) {
106
- this.logger.debug("ID token is valid, user is authenticated");
118
+ const validationResults = await this.validateTokens({
119
+ id_token: tokens.id_token,
120
+ access_token: tokens.access_token,
121
+ });
122
+ // Both tokens must be valid (if they exist) for authentication to be considered valid
123
+ const allTokensValid = validationResults.idTokenValid && validationResults.accessTokenValid;
124
+ if (allTokensValid) {
125
+ this.logger.debug("All available tokens are valid, user is authenticated");
107
126
  return true;
108
127
  }
109
- // Token is invalid/expired - attempt refresh if refresh token exists
128
+ // Some tokens are invalid/expired - attempt refresh if refresh token exists
110
129
  if (hasRefreshToken && this.tokenRefresher) {
111
- this.logger.info("ID token expired/invalid, attempting refresh with refresh token");
130
+ this.logger.info("Some tokens expired/invalid, attempting refresh with refresh token");
112
131
  try {
113
132
  // Attempt token refresh
114
133
  await this.tokenRefresher.refreshTokens();
115
134
  this.logger.info("Token refresh successful during authentication check");
116
135
  // Check if we now have valid tokens after refresh
117
136
  const refreshedTokens = await retrieveTokens(this.storage);
118
- const hasRefreshedIdToken = refreshedTokens?.id_token &&
119
- refreshedTokens.id_token.trim() !== "";
120
- if (hasRefreshedIdToken) {
121
- const isRefreshedTokenValid = await this.validateToken(refreshedTokens.id_token);
122
- if (isRefreshedTokenValid) {
123
- this.logger.info("Successfully restored session via refresh token");
124
- return true;
125
- }
137
+ const refreshedValidationResults = await this.validateTokens({
138
+ id_token: refreshedTokens?.id_token,
139
+ access_token: refreshedTokens?.access_token,
140
+ });
141
+ const allRefreshedTokensValid = refreshedValidationResults.idTokenValid &&
142
+ refreshedValidationResults.accessTokenValid;
143
+ if (allRefreshedTokensValid) {
144
+ this.logger.info("Successfully restored session via refresh token");
145
+ return true;
126
146
  }
127
147
  }
128
148
  catch (error) {
@@ -132,7 +152,7 @@ export class SessionManager {
132
152
  }
133
153
  }
134
154
  else {
135
- this.logger.warn("ID token invalid and no refresh token available, clearing tokens");
155
+ this.logger.warn("Some tokens invalid and no refresh token available, clearing tokens");
136
156
  await clearTokens(this.storage);
137
157
  }
138
158
  }
@@ -145,13 +165,15 @@ export class SessionManager {
145
165
  this.logger.info("Token refresh successful during session restore");
146
166
  // Check if we now have valid tokens after refresh
147
167
  const refreshedTokens = await retrieveTokens(this.storage);
148
- const hasRefreshedIdToken = refreshedTokens?.id_token && refreshedTokens.id_token.trim() !== "";
149
- if (hasRefreshedIdToken) {
150
- const isRefreshedTokenValid = await this.validateToken(refreshedTokens.id_token);
151
- if (isRefreshedTokenValid) {
152
- this.logger.info("Successfully restored session from refresh token only");
153
- return true;
154
- }
168
+ const refreshedValidationResults = await this.validateTokens({
169
+ id_token: refreshedTokens?.id_token,
170
+ access_token: refreshedTokens?.access_token,
171
+ });
172
+ const allRefreshedTokensValid = refreshedValidationResults.idTokenValid &&
173
+ refreshedValidationResults.accessTokenValid;
174
+ if (allRefreshedTokensValid) {
175
+ this.logger.info("Successfully restored session from refresh token only");
176
+ return true;
155
177
  }
156
178
  }
157
179
  catch (error) {
@@ -168,38 +190,72 @@ export class SessionManager {
168
190
  }
169
191
  }
170
192
  /**
171
- * Validate if a token is still valid (not expired)
193
+ * Validate if a token is cryptographically valid and not expired
194
+ * Uses proper JWT verification with signature validation using JWKS
172
195
  * @param token JWT token to validate
196
+ * @param tokenType Type of token (for logging and cleanup purposes)
173
197
  * @returns true if token is valid, false if expired or invalid
174
198
  */
175
- async validateToken(token) {
199
+ async validateToken(token, tokenType = "id_token") {
176
200
  try {
177
- // Validate JWT format (should have 3 parts separated by dots)
178
- const parts = token.split(".");
179
- if (parts.length !== 3 || !parts[1]) {
180
- this.logger.warn("Invalid JWT format");
181
- return false;
201
+ // For backend flows with httpOnly cookies, we can't validate tokens client-side
202
+ // since they're not accessible to JavaScript
203
+ if (this.isBrowserCookieStorage()) {
204
+ this.logger.debug("Backend flow: skipping client-side token validation");
205
+ return true; // Backend will validate tokens server-side
182
206
  }
183
- // Decode the JWT to check expiration
184
- const decoded = JSON.parse(atob(parts[1]));
185
- const now = Math.floor(Date.now() / 1000);
186
- // Check if token is expired (with 30 second buffer for clock skew)
187
- if (decoded.exp && decoded.exp <= now + 30) {
188
- this.logger.debug("Token is expired", {
189
- exp: decoded.exp,
190
- now: now,
191
- expired: decoded.exp <= now,
192
- });
193
- return false;
207
+ // Configure verification options based on token type
208
+ const verifyOptions = {
209
+ issuer: addSlashIfNeeded(this.config.oauthServerBaseUrl ?? "https://auth.civic.com/oauth/"),
210
+ };
211
+ // Set audience based on token type
212
+ if (tokenType === "id_token" && this.config.clientId) {
213
+ // ID tokens should have the client ID as audience for proper OIDC compliance
214
+ verifyOptions.aud = this.config.clientId;
215
+ }
216
+ else if (tokenType === "access_token") {
217
+ // Access tokens have "civic" as audience based on auth server configuration
218
+ verifyOptions.aud = "civic";
219
+ verifyOptions.clientId = this.config.clientId;
194
220
  }
195
- this.logger.debug("Token is valid");
221
+ this.logger.debug(`Verifying ${tokenType} with options:`, verifyOptions);
222
+ // Use the @civic/auth-verify package for proper JWT verification
223
+ await verify(token, verifyOptions);
224
+ this.logger.debug(`${tokenType} cryptographically verified and valid`);
196
225
  return true;
197
226
  }
198
227
  catch (error) {
199
- this.logger.warn("Token validation failed", error);
228
+ this.logger.warn(`${tokenType} validation failed`, error);
229
+ // Clear the specific token that failed validation
230
+ if (tokenType === "id_token") {
231
+ this.storage.delete(OAuthTokenTypes.ID_TOKEN);
232
+ }
233
+ else if (tokenType === "access_token") {
234
+ this.storage.delete(OAuthTokenTypes.ACCESS_TOKEN);
235
+ }
200
236
  return false;
201
237
  }
202
238
  }
239
+ /**
240
+ * Validate both ID token and access token if they exist
241
+ * @param tokens Token object containing id_token and access_token
242
+ * @returns Object indicating which tokens are valid
243
+ */
244
+ async validateTokens(tokens) {
245
+ const results = {
246
+ idTokenValid: true, // Default to true if token doesn't exist
247
+ accessTokenValid: true, // Default to true if token doesn't exist
248
+ };
249
+ // Validate ID token if it exists
250
+ if (tokens.id_token && tokens.id_token.trim() !== "") {
251
+ results.idTokenValid = await this.validateToken(tokens.id_token, "id_token");
252
+ }
253
+ // Validate access token if it exists
254
+ if (tokens.access_token && tokens.access_token.trim() !== "") {
255
+ results.accessTokenValid = await this.validateToken(tokens.access_token, "access_token");
256
+ }
257
+ return results;
258
+ }
203
259
  /**
204
260
  * Check if we're using BrowserCookieStorage
205
261
  */
@@ -343,10 +399,10 @@ export class SessionManager {
343
399
  // Clear all token-related keys except LOGOUT_STATE
344
400
  // These are the OAuth token types from the constants
345
401
  const keysToDelete = [
346
- "id_token",
347
- "access_token",
348
- "refresh_token",
349
- "oidc_session_expires_at",
402
+ OAuthTokenTypes.ID_TOKEN,
403
+ OAuthTokenTypes.ACCESS_TOKEN,
404
+ OAuthTokenTypes.REFRESH_TOKEN,
405
+ OAuthTokenTypes.OIDC_SESSION_EXPIRES_AT,
350
406
  REFRESH_IN_PROGRESS,
351
407
  AUTOREFRESH_TIMEOUT_NAME,
352
408
  // Note: NOT clearing LOGOUT_STATE here - it's needed for cleanup after redirect
@@ -1 +1 @@
1
- {"version":3,"file":"SessionManager.js","sourceRoot":"","sources":["../../../src/vanillajs/auth/SessionManager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AuthStorage } from \"../../types.js\";\nimport type { AuthenticationEvents } from \"./AuthenticationEvents.js\";\nimport type { User, Session } from \"../types/index.js\"; // Assuming Session might be used internally\nimport { AuthEvent } from \"../types/index.js\";\nimport { createLogger } from \"../utils/logger.js\";\nimport { TokenRefresher } from \"./TokenRefresher.js\";\nimport type { AuthConfig } from \"../../server/config.js\";\nimport {\n retrieveTokens,\n clearTokens,\n getBackendEndpoints,\n} from \"../../shared/lib/util.js\";\nimport { getUser } from \"../../shared/lib/session.js\";\nimport { GenericUserSession } from \"../../shared/lib/UserSession.js\";\nimport {\n AUTOREFRESH_TIMEOUT_NAME,\n REFRESH_IN_PROGRESS,\n} from \"../../constants.js\";\n\nexport class SessionManager {\n private storage: AuthStorage;\n private events: AuthenticationEvents;\n private tokenRefresher?: TokenRefresher;\n private logger = createLogger(\"session\");\n private config: AuthConfig;\n\n // Simple cache for backend session check to avoid excessive API calls\n private backendSessionCache: {\n result: boolean;\n timestamp: number;\n } | null = null;\n private readonly CACHE_DURATION = 30 * 1000; // 30 seconds\n\n constructor(\n storageAdapter: AuthStorage,\n events: AuthenticationEvents,\n config: AuthConfig,\n ) {\n this.storage = storageAdapter;\n this.events = events;\n this.config = config;\n this.logger.info(\n \"SessionManager initialized with shared lib token storage\",\n );\n }\n\n /**\n * Initialize the session manager with auth configuration to enable token refresh\n */\n async initializeWithAuthConfig(authConfig: AuthConfig): Promise<void> {\n try {\n // Initialize token refresher\n this.tokenRefresher = new TokenRefresher(\n this.storage,\n this.events,\n authConfig,\n );\n await this.tokenRefresher.initialize(authConfig);\n\n // Check current authentication state (this now includes token validation and refresh attempts)\n const isAuthenticated = await this.isAuthenticated();\n\n // Set authentication state on the token refresher\n // This will enable auto-refresh if the user is authenticated (including after successful refresh)\n this.tokenRefresher.setAuthenticationState(isAuthenticated);\n\n this.logger.info(\n \"SessionManager initialized with token refresh capability\",\n { isAuthenticated },\n );\n } catch (error) {\n this.logger.error(\n \"Failed to initialize SessionManager with auth config:\",\n error,\n );\n this.events.emit(AuthEvent.TOKEN_REFRESH_ERROR, error);\n }\n }\n\n /**\n * Build current session from shared lib storage or backend API\n * For backend flows with httpOnly cookies, creates limited session when tokens aren't accessible\n */\n async getCurrentSession(): Promise<Session | null> {\n try {\n const tokens = await retrieveTokens(this.storage);\n const user = await this.getCurrentUser();\n\n // If we have tokens and user (normal flow), create full session\n if (tokens?.id_token && user) {\n return {\n user,\n accessToken: tokens.access_token,\n idToken: tokens.id_token,\n refreshToken: tokens.refresh_token ?? undefined,\n expiresAt: tokens.oidc_session_expires_at ?? undefined,\n };\n }\n\n // If we have user but no accessible tokens (backend flow with httpOnly cookies),\n // create a limited session with just user info\n if (user && this.isBrowserCookieStorage()) {\n this.logger.debug(\n \"Creating limited session for backend flow (tokens in httpOnly cookies)\",\n );\n return {\n user,\n accessToken: undefined, // Not accessible in httpOnly cookies\n idToken: undefined, // Not accessible in httpOnly cookies\n refreshToken: undefined, // Not accessible in httpOnly cookies\n expiresAt: undefined, // Not accessible in httpOnly cookies\n };\n }\n\n this.logger.debug(\"No session available - no tokens or user found\");\n return null;\n } catch (error) {\n this.logger.error(\"Failed to load session:\", error);\n return null;\n }\n }\n\n /**\n * Check if user is authenticated using shared lib utilities\n * For backend flows with httpOnly cookies, falls back to API check\n *\n * This method now validates tokens on load and attempts refresh if:\n * - Tokens exist but are expired/invalid\n * - A refresh token is available\n */\n async isAuthenticated(): Promise<boolean> {\n try {\n // First, try the standard token-based check\n const tokens = await retrieveTokens(this.storage);\n\n // Normalize empty strings to null for consistent checking\n const hasIdToken = tokens?.id_token && tokens.id_token.trim() !== \"\";\n const hasRefreshToken =\n tokens?.refresh_token && tokens.refresh_token.trim() !== \"\";\n\n // If no tokens found and we're using BrowserCookieStorage,\n // try checking backend session (for httpOnly cookies)\n if (!hasIdToken && this.isBrowserCookieStorage()) {\n this.logger.debug(\n \"No tokens accessible, checking backend session...\",\n await this.checkBackendSession(),\n );\n return await this.checkBackendSession();\n }\n\n // If we have an ID token, validate it\n if (hasIdToken) {\n const isTokenValid = await this.validateToken(tokens.id_token!);\n\n if (isTokenValid) {\n this.logger.debug(\"ID token is valid, user is authenticated\");\n return true;\n }\n\n // Token is invalid/expired - attempt refresh if refresh token exists\n if (hasRefreshToken && this.tokenRefresher) {\n this.logger.info(\n \"ID token expired/invalid, attempting refresh with refresh token\",\n );\n\n try {\n // Attempt token refresh\n await this.tokenRefresher.refreshTokens();\n this.logger.info(\n \"Token refresh successful during authentication check\",\n );\n\n // Check if we now have valid tokens after refresh\n const refreshedTokens = await retrieveTokens(this.storage);\n const hasRefreshedIdToken =\n refreshedTokens?.id_token &&\n refreshedTokens.id_token.trim() !== \"\";\n if (hasRefreshedIdToken) {\n const isRefreshedTokenValid = await this.validateToken(\n refreshedTokens.id_token!,\n );\n if (isRefreshedTokenValid) {\n this.logger.info(\n \"Successfully restored session via refresh token\",\n );\n return true;\n }\n }\n } catch (error) {\n this.logger.warn(\n \"Token refresh failed during authentication check:\",\n error,\n );\n // Clear invalid tokens and refresh token since refresh failed\n await clearTokens(this.storage);\n }\n } else {\n this.logger.warn(\n \"ID token invalid and no refresh token available, clearing tokens\",\n );\n await clearTokens(this.storage);\n }\n } else if (hasRefreshToken && this.tokenRefresher) {\n // No ID token but we have a refresh token - attempt to restore session\n this.logger.info(\n \"No ID token found but refresh token exists, attempting session restore\",\n );\n\n try {\n // Attempt token refresh\n await this.tokenRefresher.refreshTokens();\n this.logger.info(\"Token refresh successful during session restore\");\n\n // Check if we now have valid tokens after refresh\n const refreshedTokens = await retrieveTokens(this.storage);\n const hasRefreshedIdToken =\n refreshedTokens?.id_token && refreshedTokens.id_token.trim() !== \"\";\n if (hasRefreshedIdToken) {\n const isRefreshedTokenValid = await this.validateToken(\n refreshedTokens.id_token!,\n );\n if (isRefreshedTokenValid) {\n this.logger.info(\n \"Successfully restored session from refresh token only\",\n );\n return true;\n }\n }\n } catch (error) {\n this.logger.warn(\"Session restore from refresh token failed:\", error);\n // Clear invalid refresh token since refresh failed\n await clearTokens(this.storage);\n }\n }\n\n return false;\n } catch (error) {\n this.logger.error(\"Error checking authentication:\", error);\n return false;\n }\n }\n\n /**\n * Validate if a token is still valid (not expired)\n * @param token JWT token to validate\n * @returns true if token is valid, false if expired or invalid\n */\n private async validateToken(token: string): Promise<boolean> {\n try {\n // Validate JWT format (should have 3 parts separated by dots)\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[1]) {\n this.logger.warn(\"Invalid JWT format\");\n return false;\n }\n\n // Decode the JWT to check expiration\n const decoded = JSON.parse(atob(parts[1]));\n const now = Math.floor(Date.now() / 1000);\n\n // Check if token is expired (with 30 second buffer for clock skew)\n if (decoded.exp && decoded.exp <= now + 30) {\n this.logger.debug(\"Token is expired\", {\n exp: decoded.exp,\n now: now,\n expired: decoded.exp <= now,\n });\n return false;\n }\n\n this.logger.debug(\"Token is valid\");\n return true;\n } catch (error) {\n this.logger.warn(\"Token validation failed\", error);\n return false;\n }\n }\n\n /**\n * Check if we're using BrowserCookieStorage\n */\n private isBrowserCookieStorage(): boolean {\n return this.storage.constructor.name === \"BrowserCookieStorage\";\n }\n\n /**\n * Simple backend session check via API call (with caching)\n */\n private async checkBackendSession(): Promise<boolean> {\n try {\n // Check if we have a valid cached result\n if (this.backendSessionCache) {\n const age = Date.now() - this.backendSessionCache.timestamp;\n if (age < this.CACHE_DURATION) {\n this.logger.debug(\n `Using cached backend session result: ${this.backendSessionCache.result}`,\n );\n return this.backendSessionCache.result;\n }\n }\n\n if (!this.config.loginUrl) {\n return false;\n }\n\n const backendUrl = new URL(this.config.loginUrl).origin;\n const endpoints = getBackendEndpoints(this.config.backendEndpoints);\n const response = await fetch(`${backendUrl}${endpoints.user}`, {\n method: \"GET\",\n credentials: \"include\", // Send httpOnly cookies\n });\n\n const result = response.ok;\n\n // Cache the result\n this.backendSessionCache = {\n result,\n timestamp: Date.now(),\n };\n\n this.logger.debug(\n `Backend session check: ${result} (cached for ${this.CACHE_DURATION / 1000}s)`,\n );\n return result;\n } catch (error) {\n this.logger.debug(\"Backend session check failed:\", error);\n\n // Cache negative result for shorter time\n this.backendSessionCache = {\n result: false,\n timestamp: Date.now(),\n };\n\n return false;\n }\n }\n\n /**\n * Get current user from shared lib storage or backend API\n * For backend flows with httpOnly cookies, falls back to API check\n */\n async getCurrentUser(): Promise<User | null> {\n try {\n // First, try to get user from accessible tokens\n const user = await getUser(this.storage);\n if (user) {\n this.logger.debug(\"Found user from accessible tokens\");\n return user;\n }\n\n // If no user found and we're using BrowserCookieStorage,\n // try getting user from backend API (for httpOnly cookies)\n if (this.isBrowserCookieStorage()) {\n this.logger.debug(\"No user from tokens, trying backend API...\");\n return await this.getUserFromBackendApi();\n }\n\n this.logger.debug(\"No user found and not using browser cookie storage\");\n return null;\n } catch (error) {\n this.logger.error(\"Failed to get user from shared storage:\", error);\n return null;\n }\n }\n\n /**\n * Get user information from backend API (with caching)\n */\n private async getUserFromBackendApi(): Promise<User | null> {\n try {\n if (!this.config.loginUrl) {\n this.logger.debug(\"No backend URL available for user fetch\");\n return null;\n }\n\n this.logger.debug(\"Fetching user from backend API...\");\n\n const backendUrl = new URL(this.config.loginUrl).origin;\n const endpoints = getBackendEndpoints(this.config.backendEndpoints);\n const response = await fetch(`${backendUrl}${endpoints.user}`, {\n method: \"GET\",\n credentials: \"include\", // Send httpOnly cookies\n headers: { \"Content-Type\": \"application/json\" },\n });\n\n if (response.ok) {\n const data = await response.json();\n const user = data.user;\n this.logger.debug(\"Successfully fetched user from backend API\");\n return user;\n } else {\n this.logger.debug(`Backend user fetch failed: ${response.status}`);\n return null;\n }\n } catch (error) {\n this.logger.debug(\"Backend user fetch failed:\", error);\n return null;\n }\n }\n\n /**\n * Clear all authentication data using shared lib utilities\n * @param preserveLogoutState - If true, preserves logout state for cleanup after redirect\n */\n async clearSession(preserveLogoutState: boolean = false): Promise<void> {\n try {\n if (preserveLogoutState) {\n // During logout, we need to preserve logout state for cleanup after redirect\n await this.clearTokensExceptLogoutState();\n } else {\n // Normal session clearing - clear everything\n await clearTokens(this.storage);\n }\n\n // Clear user session using shared utilities\n const userSession = new GenericUserSession(this.storage);\n await userSession.clear();\n\n // Clear backend session cache\n this.backendSessionCache = null;\n\n // Stop token refresher when session is cleared\n this.tokenRefresher?.setAuthenticationState(false);\n\n this.events.emit(AuthEvent.USER_SESSION_CHANGED, null);\n this.logger.info(\"Session cleared using shared lib utilities\");\n } catch (error) {\n this.logger.error(\"Failed to clear session:\", error);\n throw error;\n }\n }\n\n /**\n * Clear tokens from storage except logout state\n * This is needed during logout to preserve the logout state for cleanup after redirect\n */\n private async clearTokensExceptLogoutState(): Promise<void> {\n // Clear all token-related keys except LOGOUT_STATE\n // These are the OAuth token types from the constants\n const keysToDelete = [\n \"id_token\",\n \"access_token\",\n \"refresh_token\",\n \"oidc_session_expires_at\",\n REFRESH_IN_PROGRESS,\n AUTOREFRESH_TIMEOUT_NAME,\n // Note: NOT clearing LOGOUT_STATE here - it's needed for cleanup after redirect\n ];\n\n const clearPromises = keysToDelete.map(async (key) => {\n await this.storage.delete(key);\n });\n\n await Promise.all(clearPromises);\n }\n\n /**\n * Manually trigger token refresh\n */\n async refreshTokens(): Promise<void> {\n if (!this.tokenRefresher) {\n throw new Error(\n \"Token refresher not initialized. Call initializeWithAuthConfig first.\",\n );\n }\n\n return this.tokenRefresher.refreshTokens();\n }\n\n /**\n * Get token refresher state for debugging\n */\n getTokenRefresherState(): {\n isInitialized: boolean;\n isAuthenticated: boolean;\n isAutoRefreshActive: boolean;\n } | null {\n return this.tokenRefresher?.getState() || null;\n }\n\n /**\n * Clean up resources when session manager is destroyed\n */\n async destroy(): Promise<void> {\n await this.tokenRefresher?.destroy();\n this.tokenRefresher = undefined;\n this.logger.info(\"SessionManager destroyed\");\n }\n}\n"]}
1
+ {"version":3,"file":"SessionManager.js","sourceRoot":"","sources":["../../../src/vanillajs/auth/SessionManager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AuthStorage } from \"../../types.js\";\nimport type { AuthenticationEvents } from \"./AuthenticationEvents.js\";\nimport type { User, Session } from \"../types/index.js\"; // Assuming Session might be used internally\nimport { AuthEvent } from \"../types/index.js\";\nimport { createLogger } from \"../utils/logger.js\";\nimport { TokenRefresher } from \"./TokenRefresher.js\";\nimport type { AuthConfig } from \"../../server/config.js\";\nimport {\n retrieveTokens,\n clearTokens,\n getBackendEndpoints,\n} from \"../../shared/lib/util.js\";\nimport { getUser } from \"../../shared/lib/session.js\";\nimport { GenericUserSession } from \"../../shared/lib/UserSession.js\";\nimport {\n AUTOREFRESH_TIMEOUT_NAME,\n REFRESH_IN_PROGRESS,\n} from \"../../constants.js\";\nimport { verify, type VerifyOptions } from \"@civic/auth-verify\";\nimport { OAuthTokenTypes } from \"@/shared/lib/types.js\";\nimport { addSlashIfNeeded } from \"@/lib/oauth.js\";\n\nexport class SessionManager {\n private storage: AuthStorage;\n private events: AuthenticationEvents;\n private tokenRefresher?: TokenRefresher;\n private logger = createLogger(\"session\");\n private config: AuthConfig;\n\n // Simple cache for backend session check to avoid excessive API calls\n private backendSessionCache: {\n result: boolean;\n timestamp: number;\n } | null = null;\n private readonly CACHE_DURATION = 30 * 1000; // 30 seconds\n\n constructor(\n storageAdapter: AuthStorage,\n events: AuthenticationEvents,\n config: AuthConfig,\n ) {\n this.storage = storageAdapter;\n this.events = events;\n this.config = config;\n this.logger.info(\n \"SessionManager initialized with shared lib token storage\",\n );\n }\n\n /**\n * Initialize the session manager with auth configuration to enable token refresh\n */\n async initializeWithAuthConfig(authConfig: AuthConfig): Promise<void> {\n try {\n // Initialize token refresher\n this.tokenRefresher = new TokenRefresher(\n this.storage,\n this.events,\n authConfig,\n );\n await this.tokenRefresher.initialize(authConfig);\n\n // Check current authentication state (this now includes token validation and refresh attempts)\n const isAuthenticated = await this.isAuthenticated();\n\n // Set authentication state on the token refresher\n // This will enable auto-refresh if the user is authenticated (including after successful refresh)\n this.tokenRefresher.setAuthenticationState(isAuthenticated);\n\n this.logger.info(\n \"SessionManager initialized with token refresh capability\",\n { isAuthenticated },\n );\n } catch (error) {\n this.logger.error(\n \"Failed to initialize SessionManager with auth config:\",\n error,\n );\n this.events.emit(AuthEvent.TOKEN_REFRESH_ERROR, error);\n }\n }\n\n /**\n * Build current session from shared lib storage or backend API\n * For backend flows with httpOnly cookies, creates limited session when tokens aren't accessible\n */\n async getCurrentSession(): Promise<Session | null> {\n try {\n const tokens = await retrieveTokens(this.storage);\n\n const user = await this.getCurrentUser();\n\n // If we have tokens and user (normal flow), create full session\n if ((tokens?.id_token || tokens?.access_token) && user) {\n // Validate all available tokens\n const validationResults = await this.validateTokens({\n id_token: tokens.id_token,\n access_token: tokens.access_token,\n });\n\n // Check if any tokens failed validation\n if (\n !validationResults.idTokenValid ||\n !validationResults.accessTokenValid\n ) {\n this.logger.warn(\n \"Token validation failed in getCurrentSession, returning null\",\n );\n return null;\n }\n\n return {\n user,\n accessToken: tokens.access_token,\n idToken: tokens.id_token,\n refreshToken: tokens.refresh_token ?? undefined,\n expiresAt: tokens.oidc_session_expires_at ?? undefined,\n };\n }\n\n // If we have user but no accessible tokens (backend flow with httpOnly cookies),\n // create a limited session with just user info\n if (user && this.isBrowserCookieStorage()) {\n this.logger.debug(\n \"Creating limited session for backend flow (tokens in httpOnly cookies)\",\n );\n return {\n user,\n accessToken: undefined, // Not accessible in httpOnly cookies\n idToken: undefined, // Not accessible in httpOnly cookies\n refreshToken: undefined, // Not accessible in httpOnly cookies\n expiresAt: undefined, // Not accessible in httpOnly cookies\n };\n }\n\n this.logger.debug(\"No session available - no tokens or user found\");\n return null;\n } catch (error) {\n this.logger.error(\"Failed to load session:\", error);\n return null;\n }\n }\n\n /**\n * Check if user is authenticated using shared lib utilities\n * For backend flows with httpOnly cookies, falls back to API check\n *\n * This method now validates tokens on load and attempts refresh if:\n * - Tokens exist but are expired/invalid\n * - A refresh token is available\n */\n async isAuthenticated(): Promise<boolean> {\n try {\n // First, try the standard token-based check\n const tokens = await retrieveTokens(this.storage);\n\n // Normalize empty strings to null for consistent checking\n const hasIdToken = tokens?.id_token && tokens.id_token.trim() !== \"\";\n const hasRefreshToken =\n tokens?.refresh_token && tokens.refresh_token.trim() !== \"\";\n\n // If no tokens found and we're using BrowserCookieStorage,\n // try checking backend session (for httpOnly cookies)\n if (!hasIdToken && this.isBrowserCookieStorage()) {\n this.logger.debug(\n \"No tokens accessible, checking backend session...\",\n await this.checkBackendSession(),\n );\n return await this.checkBackendSession();\n }\n\n // If we have tokens, validate them\n if (hasIdToken) {\n const validationResults = await this.validateTokens({\n id_token: tokens.id_token,\n access_token: tokens.access_token,\n });\n\n // Both tokens must be valid (if they exist) for authentication to be considered valid\n const allTokensValid =\n validationResults.idTokenValid && validationResults.accessTokenValid;\n\n if (allTokensValid) {\n this.logger.debug(\n \"All available tokens are valid, user is authenticated\",\n );\n return true;\n }\n\n // Some tokens are invalid/expired - attempt refresh if refresh token exists\n if (hasRefreshToken && this.tokenRefresher) {\n this.logger.info(\n \"Some tokens expired/invalid, attempting refresh with refresh token\",\n );\n\n try {\n // Attempt token refresh\n await this.tokenRefresher.refreshTokens();\n this.logger.info(\n \"Token refresh successful during authentication check\",\n );\n\n // Check if we now have valid tokens after refresh\n const refreshedTokens = await retrieveTokens(this.storage);\n const refreshedValidationResults = await this.validateTokens({\n id_token: refreshedTokens?.id_token,\n access_token: refreshedTokens?.access_token,\n });\n\n const allRefreshedTokensValid =\n refreshedValidationResults.idTokenValid &&\n refreshedValidationResults.accessTokenValid;\n\n if (allRefreshedTokensValid) {\n this.logger.info(\n \"Successfully restored session via refresh token\",\n );\n return true;\n }\n } catch (error) {\n this.logger.warn(\n \"Token refresh failed during authentication check:\",\n error,\n );\n // Clear invalid tokens and refresh token since refresh failed\n await clearTokens(this.storage);\n }\n } else {\n this.logger.warn(\n \"Some tokens invalid and no refresh token available, clearing tokens\",\n );\n await clearTokens(this.storage);\n }\n } else if (hasRefreshToken && this.tokenRefresher) {\n // No ID token but we have a refresh token - attempt to restore session\n this.logger.info(\n \"No ID token found but refresh token exists, attempting session restore\",\n );\n\n try {\n // Attempt token refresh\n await this.tokenRefresher.refreshTokens();\n this.logger.info(\"Token refresh successful during session restore\");\n\n // Check if we now have valid tokens after refresh\n const refreshedTokens = await retrieveTokens(this.storage);\n const refreshedValidationResults = await this.validateTokens({\n id_token: refreshedTokens?.id_token,\n access_token: refreshedTokens?.access_token,\n });\n\n const allRefreshedTokensValid =\n refreshedValidationResults.idTokenValid &&\n refreshedValidationResults.accessTokenValid;\n\n if (allRefreshedTokensValid) {\n this.logger.info(\n \"Successfully restored session from refresh token only\",\n );\n return true;\n }\n } catch (error) {\n this.logger.warn(\"Session restore from refresh token failed:\", error);\n // Clear invalid refresh token since refresh failed\n await clearTokens(this.storage);\n }\n }\n\n return false;\n } catch (error) {\n this.logger.error(\"Error checking authentication:\", error);\n return false;\n }\n }\n\n /**\n * Validate if a token is cryptographically valid and not expired\n * Uses proper JWT verification with signature validation using JWKS\n * @param token JWT token to validate\n * @param tokenType Type of token (for logging and cleanup purposes)\n * @returns true if token is valid, false if expired or invalid\n */\n private async validateToken(\n token: string,\n tokenType: \"id_token\" | \"access_token\" = \"id_token\",\n ): Promise<boolean> {\n try {\n // For backend flows with httpOnly cookies, we can't validate tokens client-side\n // since they're not accessible to JavaScript\n if (this.isBrowserCookieStorage()) {\n this.logger.debug(\n \"Backend flow: skipping client-side token validation\",\n );\n return true; // Backend will validate tokens server-side\n }\n\n // Configure verification options based on token type\n const verifyOptions: VerifyOptions = {\n issuer: addSlashIfNeeded(\n this.config.oauthServerBaseUrl ?? \"https://auth.civic.com/oauth/\",\n ),\n };\n\n // Set audience based on token type\n if (tokenType === \"id_token\" && this.config.clientId) {\n // ID tokens should have the client ID as audience for proper OIDC compliance\n verifyOptions.aud = this.config.clientId;\n } else if (tokenType === \"access_token\") {\n // Access tokens have \"civic\" as audience based on auth server configuration\n verifyOptions.aud = \"civic\";\n verifyOptions.clientId = this.config.clientId;\n }\n\n this.logger.debug(`Verifying ${tokenType} with options:`, verifyOptions);\n\n // Use the @civic/auth-verify package for proper JWT verification\n await verify(token, verifyOptions);\n\n this.logger.debug(`${tokenType} cryptographically verified and valid`);\n return true;\n } catch (error) {\n this.logger.warn(`${tokenType} validation failed`, error);\n // Clear the specific token that failed validation\n if (tokenType === \"id_token\") {\n this.storage.delete(OAuthTokenTypes.ID_TOKEN);\n } else if (tokenType === \"access_token\") {\n this.storage.delete(OAuthTokenTypes.ACCESS_TOKEN);\n }\n return false;\n }\n }\n\n /**\n * Validate both ID token and access token if they exist\n * @param tokens Token object containing id_token and access_token\n * @returns Object indicating which tokens are valid\n */\n private async validateTokens(tokens: {\n id_token?: string;\n access_token?: string;\n }): Promise<{\n idTokenValid: boolean;\n accessTokenValid: boolean;\n }> {\n const results = {\n idTokenValid: true, // Default to true if token doesn't exist\n accessTokenValid: true, // Default to true if token doesn't exist\n };\n\n // Validate ID token if it exists\n if (tokens.id_token && tokens.id_token.trim() !== \"\") {\n results.idTokenValid = await this.validateToken(\n tokens.id_token,\n \"id_token\",\n );\n }\n\n // Validate access token if it exists\n if (tokens.access_token && tokens.access_token.trim() !== \"\") {\n results.accessTokenValid = await this.validateToken(\n tokens.access_token,\n \"access_token\",\n );\n }\n\n return results;\n }\n\n /**\n * Check if we're using BrowserCookieStorage\n */\n private isBrowserCookieStorage(): boolean {\n return this.storage.constructor.name === \"BrowserCookieStorage\";\n }\n\n /**\n * Simple backend session check via API call (with caching)\n */\n private async checkBackendSession(): Promise<boolean> {\n try {\n // Check if we have a valid cached result\n if (this.backendSessionCache) {\n const age = Date.now() - this.backendSessionCache.timestamp;\n if (age < this.CACHE_DURATION) {\n this.logger.debug(\n `Using cached backend session result: ${this.backendSessionCache.result}`,\n );\n return this.backendSessionCache.result;\n }\n }\n\n if (!this.config.loginUrl) {\n return false;\n }\n\n const backendUrl = new URL(this.config.loginUrl).origin;\n const endpoints = getBackendEndpoints(this.config.backendEndpoints);\n const response = await fetch(`${backendUrl}${endpoints.user}`, {\n method: \"GET\",\n credentials: \"include\", // Send httpOnly cookies\n });\n\n const result = response.ok;\n\n // Cache the result\n this.backendSessionCache = {\n result,\n timestamp: Date.now(),\n };\n\n this.logger.debug(\n `Backend session check: ${result} (cached for ${this.CACHE_DURATION / 1000}s)`,\n );\n return result;\n } catch (error) {\n this.logger.debug(\"Backend session check failed:\", error);\n\n // Cache negative result for shorter time\n this.backendSessionCache = {\n result: false,\n timestamp: Date.now(),\n };\n\n return false;\n }\n }\n\n /**\n * Get current user from shared lib storage or backend API\n * For backend flows with httpOnly cookies, falls back to API check\n */\n async getCurrentUser(): Promise<User | null> {\n try {\n // First, try to get user from accessible tokens\n const user = await getUser(this.storage);\n if (user) {\n this.logger.debug(\"Found user from accessible tokens\");\n return user;\n }\n\n // If no user found and we're using BrowserCookieStorage,\n // try getting user from backend API (for httpOnly cookies)\n if (this.isBrowserCookieStorage()) {\n this.logger.debug(\"No user from tokens, trying backend API...\");\n return await this.getUserFromBackendApi();\n }\n\n this.logger.debug(\"No user found and not using browser cookie storage\");\n return null;\n } catch (error) {\n this.logger.error(\"Failed to get user from shared storage:\", error);\n return null;\n }\n }\n\n /**\n * Get user information from backend API (with caching)\n */\n private async getUserFromBackendApi(): Promise<User | null> {\n try {\n if (!this.config.loginUrl) {\n this.logger.debug(\"No backend URL available for user fetch\");\n return null;\n }\n\n this.logger.debug(\"Fetching user from backend API...\");\n\n const backendUrl = new URL(this.config.loginUrl).origin;\n const endpoints = getBackendEndpoints(this.config.backendEndpoints);\n const response = await fetch(`${backendUrl}${endpoints.user}`, {\n method: \"GET\",\n credentials: \"include\", // Send httpOnly cookies\n headers: { \"Content-Type\": \"application/json\" },\n });\n\n if (response.ok) {\n const data = await response.json();\n const user = data.user;\n this.logger.debug(\"Successfully fetched user from backend API\");\n return user;\n } else {\n this.logger.debug(`Backend user fetch failed: ${response.status}`);\n return null;\n }\n } catch (error) {\n this.logger.debug(\"Backend user fetch failed:\", error);\n return null;\n }\n }\n\n /**\n * Clear all authentication data using shared lib utilities\n * @param preserveLogoutState - If true, preserves logout state for cleanup after redirect\n */\n async clearSession(preserveLogoutState: boolean = false): Promise<void> {\n try {\n if (preserveLogoutState) {\n // During logout, we need to preserve logout state for cleanup after redirect\n await this.clearTokensExceptLogoutState();\n } else {\n // Normal session clearing - clear everything\n await clearTokens(this.storage);\n }\n\n // Clear user session using shared utilities\n const userSession = new GenericUserSession(this.storage);\n await userSession.clear();\n\n // Clear backend session cache\n this.backendSessionCache = null;\n\n // Stop token refresher when session is cleared\n this.tokenRefresher?.setAuthenticationState(false);\n\n this.events.emit(AuthEvent.USER_SESSION_CHANGED, null);\n this.logger.info(\"Session cleared using shared lib utilities\");\n } catch (error) {\n this.logger.error(\"Failed to clear session:\", error);\n throw error;\n }\n }\n\n /**\n * Clear tokens from storage except logout state\n * This is needed during logout to preserve the logout state for cleanup after redirect\n */\n private async clearTokensExceptLogoutState(): Promise<void> {\n // Clear all token-related keys except LOGOUT_STATE\n // These are the OAuth token types from the constants\n const keysToDelete = [\n OAuthTokenTypes.ID_TOKEN,\n OAuthTokenTypes.ACCESS_TOKEN,\n OAuthTokenTypes.REFRESH_TOKEN,\n OAuthTokenTypes.OIDC_SESSION_EXPIRES_AT,\n REFRESH_IN_PROGRESS,\n AUTOREFRESH_TIMEOUT_NAME,\n // Note: NOT clearing LOGOUT_STATE here - it's needed for cleanup after redirect\n ];\n\n const clearPromises = keysToDelete.map(async (key) => {\n await this.storage.delete(key);\n });\n\n await Promise.all(clearPromises);\n }\n\n /**\n * Manually trigger token refresh\n */\n async refreshTokens(): Promise<void> {\n if (!this.tokenRefresher) {\n throw new Error(\n \"Token refresher not initialized. Call initializeWithAuthConfig first.\",\n );\n }\n\n return this.tokenRefresher.refreshTokens();\n }\n\n /**\n * Get token refresher state for debugging\n */\n getTokenRefresherState(): {\n isInitialized: boolean;\n isAuthenticated: boolean;\n isAutoRefreshActive: boolean;\n } | null {\n return this.tokenRefresher?.getState() || null;\n }\n\n /**\n * Clean up resources when session manager is destroyed\n */\n async destroy(): Promise<void> {\n await this.tokenRefresher?.destroy();\n this.tokenRefresher = undefined;\n this.logger.info(\"SessionManager destroyed\");\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigProcessor.d.ts","sourceRoot":"","sources":["../../../../src/vanillajs/auth/config/ConfigProcessor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EAEzB,MAAM,uBAAuB,CAAC;AAO/B;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,qBAAqB,GAC5B,wBAAwB,CA2D1B"}
1
+ {"version":3,"file":"ConfigProcessor.d.ts","sourceRoot":"","sources":["../../../../src/vanillajs/auth/config/ConfigProcessor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EAEzB,MAAM,uBAAuB,CAAC;AAO/B;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,qBAAqB,GAC5B,wBAAwB,CA2E1B"}
@@ -18,7 +18,21 @@ export function processConfigWithDefaults(config) {
18
18
  // the original displaymode doesn't suppors embedded, so we need to proxy it to iframe + iframeDisplayMode: "embedded"
19
19
  const originalDisplayMode = config.displayMode || "iframe";
20
20
  const processedDisplayMode = originalDisplayMode === "embedded" ? "iframe" : originalDisplayMode;
21
- const processedIframeDisplayMode = originalDisplayMode === "embedded" ? "embedded" : config.iframeDisplayMode;
21
+ // Determine iframeDisplayMode with proper precedence:
22
+ // 1. User explicitly provided iframeDisplayMode (highest priority)
23
+ // 2. If displayMode is "embedded", set to "embedded"
24
+ // 3. Default to "modal" for iframe mode
25
+ const processedIframeDisplayMode = config.iframeDisplayMode ??
26
+ (originalDisplayMode === "embedded"
27
+ ? "embedded"
28
+ : processedDisplayMode === "iframe"
29
+ ? "modal"
30
+ : undefined);
31
+ // Determine preloadIframe with proper precedence:
32
+ // 1. User explicitly provided preloadIframe (highest priority)
33
+ // 2. If displayMode is "embedded", set to false (better for embedded visibility)
34
+ // 3. Default to true
35
+ const processedPreloadIframe = config.preloadIframe ?? originalDisplayMode !== "embedded";
22
36
  // Process redirectUrl - default to current page without query parameters
23
37
  const redirectUrl = config.redirectUrl ||
24
38
  `${window.location.origin}${window.location.pathname}`;
@@ -50,7 +64,7 @@ export function processConfigWithDefaults(config) {
50
64
  storageAdapter,
51
65
  loginUrl: config.loginUrl, // Include loginUrl in processed config
52
66
  backendEndpoints: config.backendEndpoints, // Include backend endpoints in processed config
53
- preloadIframe: config.preloadIframe !== false, // Default to true unless explicitly set to false
67
+ preloadIframe: processedPreloadIframe, // Use the processed preloadIframe value
54
68
  autoRedirect: config.autoRedirect !== false, // Default to true unless explicitly set to false
55
69
  }; // Type assertion: we've validated the config and ensured all required properties are present
56
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigProcessor.js","sourceRoot":"","sources":["../../../../src/vanillajs/auth/config/ConfigProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { LocalStorageAdapter } from \"../../../browser/storage.js\";\nimport { BrowserCookieStorage } from \"../../../shared/lib/BrowserCookieStorage.js\";\nimport { DEFAULT_SCOPES, DEFAULT_AUTH_SERVER } from \"../../../constants.js\";\nimport type { DisplayMode } from \"../../../types.js\";\nimport type {\n CivicAuthClientConfig,\n ProcessedCivicAuthConfig,\n LoggingConfig,\n} from \"../types/AuthTypes.js\";\nimport {\n CivicAuthError,\n CivicAuthErrorCode,\n CIVIC_AUTH_CONSTANTS,\n} from \"../types/AuthTypes.js\";\n\n/**\n * Process the configuration with defaults and validation\n */\nexport function processConfigWithDefaults(\n config: CivicAuthClientConfig,\n): ProcessedCivicAuthConfig {\n // Validate required configuration\n validateRequiredConfig(config);\n\n const loggingConfig: LoggingConfig = {\n enabled: false,\n namespace: \"*\",\n level: \"debug\" as const,\n ...config.logging,\n };\n\n // Handle displayMode proxy: map \"embedded\" to \"iframe\" + iframeDisplayMode: \"embedded\"\n // the original displaymode doesn't suppors embedded, so we need to proxy it to iframe + iframeDisplayMode: \"embedded\"\n const originalDisplayMode = config.displayMode || \"iframe\";\n const processedDisplayMode: DisplayMode =\n originalDisplayMode === \"embedded\" ? \"iframe\" : originalDisplayMode;\n\n const processedIframeDisplayMode =\n originalDisplayMode === \"embedded\" ? \"embedded\" : config.iframeDisplayMode;\n\n // Process redirectUrl - default to current page without query parameters\n const redirectUrl =\n config.redirectUrl ||\n `${window.location.origin}${window.location.pathname}`;\n\n // Process logoutRedirectUrl - default to redirectUrl if not provided (same behavior as reactjs implementation)\n const logoutRedirectUrl = config.logoutRedirectUrl || redirectUrl;\n\n // Auto-select storage adapter based on loginUrl\n // If loginUrl is provided (backend integration), automatically use BrowserCookieStorage\n // Otherwise, use provided storageAdapter or default to LocalStorageAdapter\n const storageAdapter = config.loginUrl\n ? new BrowserCookieStorage()\n : config.storageAdapter || new LocalStorageAdapter();\n\n return {\n ...config,\n redirectUrl,\n logoutRedirectUrl,\n oauthServerBaseUrl: config.oauthServerBaseUrl || DEFAULT_AUTH_SERVER,\n scopes: config.scopes || DEFAULT_SCOPES,\n textSignals: config.textSignals || {\n success: \"Authentication successful!\",\n error: \"Authentication failed. Please try again.\",\n },\n displayMode: processedDisplayMode,\n iframeDisplayMode: processedIframeDisplayMode,\n authProcessTimeout:\n config.authProcessTimeout ||\n CIVIC_AUTH_CONSTANTS.DEFAULT_AUTH_PROCESS_TIMEOUT,\n iframeId: config.iframeId || CIVIC_AUTH_CONSTANTS.DEFAULT_IFRAME_ID,\n prompt: \"consent\",\n logging: loggingConfig,\n storageAdapter,\n loginUrl: config.loginUrl, // Include loginUrl in processed config\n backendEndpoints: config.backendEndpoints, // Include backend endpoints in processed config\n preloadIframe: config.preloadIframe !== false, // Default to true unless explicitly set to false\n autoRedirect: config.autoRedirect !== false, // Default to true unless explicitly set to false\n } as ProcessedCivicAuthConfig; // Type assertion: we've validated the config and ensured all required properties are present\n}\n\n/**\n * Validates required configuration properties\n */\nfunction validateRequiredConfig(config: CivicAuthClientConfig): void {\n // Dynamic validation: clientId is only required if loginUrl is not provided\n if (!config.loginUrl && !config.clientId) {\n throw new CivicAuthError(\n \"CivicAuth: clientId is required when loginUrl is not provided.\",\n CivicAuthErrorCode.CONFIG_REQUIRED,\n );\n }\n\n // Conditional validation for targetContainerElement\n // Handle both the new \"embedded\" displayMode and the legacy iframe + iframeDisplayMode approach\n const displayMode = config.displayMode || \"iframe\";\n const iframeDisplayMode = config.iframeDisplayMode;\n\n // Check if we need a container element\n const needsContainer =\n displayMode === \"embedded\" || // New simplified API\n (displayMode === \"iframe\" && iframeDisplayMode === \"embedded\"); // Legacy API\n\n if (needsContainer && !config.targetContainerElement) {\n throw new CivicAuthError(\n \"CivicAuth: targetContainerElement is required for embedded iframe mode. \" +\n \"You can use displayMode: 'embedded' for a simplified API, \" +\n \"or use displayMode: 'iframe' with iframeDisplayMode: 'embedded'. \" +\n \"For modal iframe mode, use displayMode: 'iframe' (default). \" +\n \"For non-iframe modes, use displayMode 'redirect' or 'new_tab'.\",\n CivicAuthErrorCode.CONFIG_REQUIRED,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"ConfigProcessor.js","sourceRoot":"","sources":["../../../../src/vanillajs/auth/config/ConfigProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { LocalStorageAdapter } from \"../../../browser/storage.js\";\nimport { BrowserCookieStorage } from \"../../../shared/lib/BrowserCookieStorage.js\";\nimport { DEFAULT_SCOPES, DEFAULT_AUTH_SERVER } from \"../../../constants.js\";\nimport type { DisplayMode } from \"../../../types.js\";\nimport type {\n CivicAuthClientConfig,\n ProcessedCivicAuthConfig,\n LoggingConfig,\n} from \"../types/AuthTypes.js\";\nimport {\n CivicAuthError,\n CivicAuthErrorCode,\n CIVIC_AUTH_CONSTANTS,\n} from \"../types/AuthTypes.js\";\n\n/**\n * Process the configuration with defaults and validation\n */\nexport function processConfigWithDefaults(\n config: CivicAuthClientConfig,\n): ProcessedCivicAuthConfig {\n // Validate required configuration\n validateRequiredConfig(config);\n\n const loggingConfig: LoggingConfig = {\n enabled: false,\n namespace: \"*\",\n level: \"debug\" as const,\n ...config.logging,\n };\n\n // Handle displayMode proxy: map \"embedded\" to \"iframe\" + iframeDisplayMode: \"embedded\"\n // the original displaymode doesn't suppors embedded, so we need to proxy it to iframe + iframeDisplayMode: \"embedded\"\n const originalDisplayMode = config.displayMode || \"iframe\";\n const processedDisplayMode: DisplayMode =\n originalDisplayMode === \"embedded\" ? \"iframe\" : originalDisplayMode;\n\n // Determine iframeDisplayMode with proper precedence:\n // 1. User explicitly provided iframeDisplayMode (highest priority)\n // 2. If displayMode is \"embedded\", set to \"embedded\"\n // 3. Default to \"modal\" for iframe mode\n const processedIframeDisplayMode =\n config.iframeDisplayMode ??\n (originalDisplayMode === \"embedded\"\n ? \"embedded\"\n : processedDisplayMode === \"iframe\"\n ? \"modal\"\n : undefined);\n\n // Determine preloadIframe with proper precedence:\n // 1. User explicitly provided preloadIframe (highest priority)\n // 2. If displayMode is \"embedded\", set to false (better for embedded visibility)\n // 3. Default to true\n const processedPreloadIframe =\n config.preloadIframe ?? originalDisplayMode !== \"embedded\";\n\n // Process redirectUrl - default to current page without query parameters\n const redirectUrl =\n config.redirectUrl ||\n `${window.location.origin}${window.location.pathname}`;\n\n // Process logoutRedirectUrl - default to redirectUrl if not provided (same behavior as reactjs implementation)\n const logoutRedirectUrl = config.logoutRedirectUrl || redirectUrl;\n\n // Auto-select storage adapter based on loginUrl\n // If loginUrl is provided (backend integration), automatically use BrowserCookieStorage\n // Otherwise, use provided storageAdapter or default to LocalStorageAdapter\n const storageAdapter = config.loginUrl\n ? new BrowserCookieStorage()\n : config.storageAdapter || new LocalStorageAdapter();\n\n return {\n ...config,\n redirectUrl,\n logoutRedirectUrl,\n oauthServerBaseUrl: config.oauthServerBaseUrl || DEFAULT_AUTH_SERVER,\n scopes: config.scopes || DEFAULT_SCOPES,\n textSignals: config.textSignals || {\n success: \"Authentication successful!\",\n error: \"Authentication failed. Please try again.\",\n },\n displayMode: processedDisplayMode,\n iframeDisplayMode: processedIframeDisplayMode,\n authProcessTimeout:\n config.authProcessTimeout ||\n CIVIC_AUTH_CONSTANTS.DEFAULT_AUTH_PROCESS_TIMEOUT,\n iframeId: config.iframeId || CIVIC_AUTH_CONSTANTS.DEFAULT_IFRAME_ID,\n prompt: \"consent\",\n logging: loggingConfig,\n storageAdapter,\n loginUrl: config.loginUrl, // Include loginUrl in processed config\n backendEndpoints: config.backendEndpoints, // Include backend endpoints in processed config\n preloadIframe: processedPreloadIframe, // Use the processed preloadIframe value\n autoRedirect: config.autoRedirect !== false, // Default to true unless explicitly set to false\n } as ProcessedCivicAuthConfig; // Type assertion: we've validated the config and ensured all required properties are present\n}\n\n/**\n * Validates required configuration properties\n */\nfunction validateRequiredConfig(config: CivicAuthClientConfig): void {\n // Dynamic validation: clientId is only required if loginUrl is not provided\n if (!config.loginUrl && !config.clientId) {\n throw new CivicAuthError(\n \"CivicAuth: clientId is required when loginUrl is not provided.\",\n CivicAuthErrorCode.CONFIG_REQUIRED,\n );\n }\n\n // Conditional validation for targetContainerElement\n // Handle both the new \"embedded\" displayMode and the legacy iframe + iframeDisplayMode approach\n const displayMode = config.displayMode || \"iframe\";\n const iframeDisplayMode = config.iframeDisplayMode;\n\n // Check if we need a container element\n const needsContainer =\n displayMode === \"embedded\" || // New simplified API\n (displayMode === \"iframe\" && iframeDisplayMode === \"embedded\"); // Legacy API\n\n if (needsContainer && !config.targetContainerElement) {\n throw new CivicAuthError(\n \"CivicAuth: targetContainerElement is required for embedded iframe mode. \" +\n \"You can use displayMode: 'embedded' for a simplified API, \" +\n \"or use displayMode: 'iframe' with iframeDisplayMode: 'embedded'. \" +\n \"For modal iframe mode, use displayMode: 'iframe' (default). \" +\n \"For non-iframe modes, use displayMode 'redirect' or 'new_tab'.\",\n CivicAuthErrorCode.CONFIG_REQUIRED,\n );\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../../../src/vanillajs/auth/handlers/MessageHandler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAIV,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAI1D,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAC5C,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACpC,cAAc,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,0BAA0B,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,aAAa,CAAC,CAAoB;IAC1C,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,0BAA0B,CAG9B;IACJ,OAAO,CAAC,OAAO,CAAa;IAG5B,OAAO,CAAC,oBAAoB,CAAC,CAAS;gBAE1B,aAAa,EAAE,oBAAoB;IAU/C;;;;;;;;OAQG;IACI,mBAAmB,CAAC,aAAa,EAAE,iBAAiB,GAAG,IAAI;IAIlE;;;;;;;OAOG;IACI,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAQ5D;;OAEG;IACI,yBAAyB,IAAI,IAAI;IAOxC;;;;;;;OAOG;IACI,aAAa,UAAW,YAAY,KAAG,IAAI,CAYhD;IAEF;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IAyD5B;;;;;;;OAOG;IACH,OAAO,CAAC,kBAAkB;IAoC1B;;;;;;;;OAQG;IACH,OAAO,CAAC,sBAAsB;IAS9B;;;;;;;;OAQG;IACH,OAAO,CAAC,0BAA0B;IA6DlC;;;;;;;OAOG;IAEH,OAAO,CAAC,sBAAsB;IAc9B;;;;;;;OAOG;IACH,OAAO,CAAC,wBAAwB;IAoBhC;;;;;OAKG;IACH,OAAO,CAAC,8BAA8B;IAkBtC;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;;;;;;OAOG;IACH,OAAO,CAAC,iCAAiC;IAqBzC;;;;;;;;OAQG;IACH,OAAO,CAAC,sBAAsB;IAY9B;;;;;;;OAOG;IACH,OAAO,CAAC,0BAA0B;IAYlC;;;;;;;OAOG;IACH,OAAO,CAAC,yBAAyB;IAmCjC;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB;IASzB;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;CAcxB"}
1
+ {"version":3,"file":"MessageHandler.d.ts","sourceRoot":"","sources":["../../../../src/vanillajs/auth/handlers/MessageHandler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAIV,wBAAwB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAI1D,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAC5C,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACpC,cAAc,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,0BAA0B,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,aAAa,CAAC,CAAoB;IAC1C,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,0BAA0B,CAG9B;IACJ,OAAO,CAAC,OAAO,CAAa;IAG5B,OAAO,CAAC,oBAAoB,CAAC,CAAS;gBAE1B,aAAa,EAAE,oBAAoB;IAU/C;;;;;;;;OAQG;IACI,mBAAmB,CAAC,aAAa,EAAE,iBAAiB,GAAG,IAAI;IAIlE;;;;;;;OAOG;IACI,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAQ5D;;OAEG;IACI,yBAAyB,IAAI,IAAI;IAOxC;;;;;;;OAOG;IACI,aAAa,UAAW,YAAY,KAAG,IAAI,CAYhD;IAEF;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB;IA6D5B;;;;;;;OAOG;IACH,OAAO,CAAC,kBAAkB;IAoC1B;;;;;;;;OAQG;IACH,OAAO,CAAC,sBAAsB;IAS9B;;;;;;;;OAQG;IACH,OAAO,CAAC,0BAA0B;IA6DlC;;;;;;;OAOG;IAEH,OAAO,CAAC,sBAAsB;IAc9B;;;;;;;OAOG;IACH,OAAO,CAAC,wBAAwB;IAoBhC;;;;;OAKG;IACH,OAAO,CAAC,8BAA8B;IAkBtC;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;;;;;;OAOG;IACH,OAAO,CAAC,iCAAiC;IAqBzC;;;;;;;;OAQG;IACH,OAAO,CAAC,sBAAsB;IAY9B;;;;;;;OAOG;IACH,OAAO,CAAC,0BAA0B;IAYlC;;;;;;;OAOG;IACH,OAAO,CAAC,yBAAyB;IAmCjC;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB;IASzB;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;CAcxB"}
@@ -123,6 +123,9 @@ export class MessageHandler {
123
123
  // ignore URL parsing errors
124
124
  }
125
125
  }
126
+ if (this.config.oauthServerBaseUrl) {
127
+ allowedOrigins.push(new URL(this.config.oauthServerBaseUrl).origin);
128
+ }
126
129
  // Add the client app's own origin (same-origin) for messages sent after token exchange
127
130
  // This is needed when the iframe navigates to the client app's callback URL
128
131
  const clientOrigin = window.location.origin;