@civic/auth 0.8.2 → 0.9.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +9 -3
  3. package/dist/constants.d.ts +2 -1
  4. package/dist/constants.d.ts.map +1 -1
  5. package/dist/constants.js +3 -1
  6. package/dist/constants.js.map +1 -1
  7. package/dist/lib/oauth.d.ts +4 -2
  8. package/dist/lib/oauth.d.ts.map +1 -1
  9. package/dist/lib/oauth.js +4 -2
  10. package/dist/lib/oauth.js.map +1 -1
  11. package/dist/nextjs/NextClientAuthenticationRefresher.d.ts +1 -1
  12. package/dist/nextjs/NextClientAuthenticationRefresher.d.ts.map +1 -1
  13. package/dist/nextjs/NextClientAuthenticationRefresher.js.map +1 -1
  14. package/dist/nextjs/NextServerAuthenticationRefresherImpl.d.ts +1 -1
  15. package/dist/nextjs/NextServerAuthenticationRefresherImpl.d.ts.map +1 -1
  16. package/dist/nextjs/NextServerAuthenticationRefresherImpl.js +3 -0
  17. package/dist/nextjs/NextServerAuthenticationRefresherImpl.js.map +1 -1
  18. package/dist/nextjs/config.d.ts +3 -0
  19. package/dist/nextjs/config.d.ts.map +1 -1
  20. package/dist/nextjs/config.js +3 -0
  21. package/dist/nextjs/config.js.map +1 -1
  22. package/dist/nextjs/providers/NextAuthProvider.d.ts.map +1 -1
  23. package/dist/nextjs/providers/NextAuthProvider.js +1 -1
  24. package/dist/nextjs/providers/NextAuthProvider.js.map +1 -1
  25. package/dist/nextjs/routeHandler.d.ts.map +1 -1
  26. package/dist/nextjs/routeHandler.js +2 -1
  27. package/dist/nextjs/routeHandler.js.map +1 -1
  28. package/dist/reactjs/core/GlobalAuthManager.d.ts +16 -0
  29. package/dist/reactjs/core/GlobalAuthManager.d.ts.map +1 -1
  30. package/dist/reactjs/core/GlobalAuthManager.js +28 -1
  31. package/dist/reactjs/core/GlobalAuthManager.js.map +1 -1
  32. package/dist/reactjs/hooks/useUser.d.ts +3 -0
  33. package/dist/reactjs/hooks/useUser.d.ts.map +1 -1
  34. package/dist/reactjs/hooks/useUser.js +32 -0
  35. package/dist/reactjs/hooks/useUser.js.map +1 -1
  36. package/dist/reactjs/providers/CivicAuthContext.d.ts +4 -0
  37. package/dist/reactjs/providers/CivicAuthContext.d.ts.map +1 -1
  38. package/dist/reactjs/providers/CivicAuthContext.js +22 -13
  39. package/dist/reactjs/providers/CivicAuthContext.js.map +1 -1
  40. package/dist/reactjs/providers/CivicAuthProvider.d.ts +2 -0
  41. package/dist/reactjs/providers/CivicAuthProvider.d.ts.map +1 -1
  42. package/dist/reactjs/providers/CivicAuthProvider.js +5 -1
  43. package/dist/reactjs/providers/CivicAuthProvider.js.map +1 -1
  44. package/dist/server/config.d.ts +47 -0
  45. package/dist/server/config.d.ts.map +1 -1
  46. package/dist/server/config.js.map +1 -1
  47. package/dist/server/index.d.ts +8 -2
  48. package/dist/server/index.d.ts.map +1 -1
  49. package/dist/server/index.js +5 -1
  50. package/dist/server/index.js.map +1 -1
  51. package/dist/server/login.d.ts +9 -0
  52. package/dist/server/login.d.ts.map +1 -1
  53. package/dist/server/login.js +4 -2
  54. package/dist/server/login.js.map +1 -1
  55. package/dist/server/refresh.d.ts +1 -1
  56. package/dist/server/refresh.d.ts.map +1 -1
  57. package/dist/server/refresh.js.map +1 -1
  58. package/dist/server/session.d.ts +60 -2
  59. package/dist/server/session.d.ts.map +1 -1
  60. package/dist/server/session.js +216 -5
  61. package/dist/server/session.js.map +1 -1
  62. package/dist/server/types/express.d.ts +97 -0
  63. package/dist/server/types/express.d.ts.map +1 -0
  64. package/dist/server/types/express.js +2 -0
  65. package/dist/server/types/express.js.map +1 -0
  66. package/dist/services/AuthenticationService.d.ts +12 -0
  67. package/dist/services/AuthenticationService.d.ts.map +1 -1
  68. package/dist/services/AuthenticationService.js +62 -6
  69. package/dist/services/AuthenticationService.js.map +1 -1
  70. package/dist/services/types.d.ts +1 -1
  71. package/dist/services/types.d.ts.map +1 -1
  72. package/dist/services/types.js.map +1 -1
  73. package/dist/shared/components/CivicAuthIframe.d.ts +1 -0
  74. package/dist/shared/components/CivicAuthIframe.d.ts.map +1 -1
  75. package/dist/shared/components/CivicAuthIframe.js +4 -4
  76. package/dist/shared/components/CivicAuthIframe.js.map +1 -1
  77. package/dist/shared/components/CivicAuthIframeContainer.d.ts +2 -1
  78. package/dist/shared/components/CivicAuthIframeContainer.d.ts.map +1 -1
  79. package/dist/shared/components/CivicAuthIframeContainer.js +10 -3
  80. package/dist/shared/components/CivicAuthIframeContainer.js.map +1 -1
  81. package/dist/shared/components/IFrameAndLoading.d.ts.map +1 -1
  82. package/dist/shared/components/IFrameAndLoading.js +1 -1
  83. package/dist/shared/components/IFrameAndLoading.js.map +1 -1
  84. package/dist/shared/hooks/useSignIn.d.ts.map +1 -1
  85. package/dist/shared/hooks/useSignIn.js +5 -3
  86. package/dist/shared/hooks/useSignIn.js.map +1 -1
  87. package/dist/shared/lib/AuthenticationRefresherImpl.d.ts +2 -2
  88. package/dist/shared/lib/AuthenticationRefresherImpl.d.ts.map +1 -1
  89. package/dist/shared/lib/AuthenticationRefresherImpl.js +3 -0
  90. package/dist/shared/lib/AuthenticationRefresherImpl.js.map +1 -1
  91. package/dist/shared/lib/GenericAuthenticationRefresher.d.ts +2 -2
  92. package/dist/shared/lib/GenericAuthenticationRefresher.d.ts.map +1 -1
  93. package/dist/shared/lib/GenericAuthenticationRefresher.js.map +1 -1
  94. package/dist/shared/lib/iframeUtils.d.ts +2 -0
  95. package/dist/shared/lib/iframeUtils.d.ts.map +1 -1
  96. package/dist/shared/lib/iframeUtils.js +12 -0
  97. package/dist/shared/lib/iframeUtils.js.map +1 -1
  98. package/dist/shared/lib/types.d.ts +1 -0
  99. package/dist/shared/lib/types.d.ts.map +1 -1
  100. package/dist/shared/lib/types.js.map +1 -1
  101. package/dist/shared/lib/util.d.ts +7 -0
  102. package/dist/shared/lib/util.d.ts.map +1 -1
  103. package/dist/shared/lib/util.js +12 -0
  104. package/dist/shared/lib/util.js.map +1 -1
  105. package/dist/shared/providers/CivicAuthConfigContext.d.ts +2 -1
  106. package/dist/shared/providers/CivicAuthConfigContext.d.ts.map +1 -1
  107. package/dist/shared/providers/CivicAuthConfigContext.js +3 -1
  108. package/dist/shared/providers/CivicAuthConfigContext.js.map +1 -1
  109. package/dist/shared/version.d.ts +1 -1
  110. package/dist/shared/version.d.ts.map +1 -1
  111. package/dist/shared/version.js +1 -1
  112. package/dist/shared/version.js.map +1 -1
  113. package/dist/vanillajs/auth/BackendAuthenticationRefresher.d.ts +41 -0
  114. package/dist/vanillajs/auth/BackendAuthenticationRefresher.d.ts.map +1 -0
  115. package/dist/vanillajs/auth/BackendAuthenticationRefresher.js +125 -0
  116. package/dist/vanillajs/auth/BackendAuthenticationRefresher.js.map +1 -0
  117. package/dist/vanillajs/auth/CivicAuth.d.ts +67 -0
  118. package/dist/vanillajs/auth/CivicAuth.d.ts.map +1 -1
  119. package/dist/vanillajs/auth/CivicAuth.js +310 -10
  120. package/dist/vanillajs/auth/CivicAuth.js.map +1 -1
  121. package/dist/vanillajs/auth/SessionManager.d.ts +31 -3
  122. package/dist/vanillajs/auth/SessionManager.d.ts.map +1 -1
  123. package/dist/vanillajs/auth/SessionManager.js +253 -22
  124. package/dist/vanillajs/auth/SessionManager.js.map +1 -1
  125. package/dist/vanillajs/auth/TokenRefresher.d.ts.map +1 -1
  126. package/dist/vanillajs/auth/TokenRefresher.js +31 -18
  127. package/dist/vanillajs/auth/TokenRefresher.js.map +1 -1
  128. package/dist/vanillajs/auth/config/ConfigProcessor.d.ts.map +1 -1
  129. package/dist/vanillajs/auth/config/ConfigProcessor.js +15 -8
  130. package/dist/vanillajs/auth/config/ConfigProcessor.js.map +1 -1
  131. package/dist/vanillajs/auth/handlers/IframeAuthHandler.d.ts +44 -0
  132. package/dist/vanillajs/auth/handlers/IframeAuthHandler.d.ts.map +1 -1
  133. package/dist/vanillajs/auth/handlers/IframeAuthHandler.js +163 -1
  134. package/dist/vanillajs/auth/handlers/IframeAuthHandler.js.map +1 -1
  135. package/dist/vanillajs/auth/handlers/MessageHandler.d.ts +23 -0
  136. package/dist/vanillajs/auth/handlers/MessageHandler.d.ts.map +1 -1
  137. package/dist/vanillajs/auth/handlers/MessageHandler.js +59 -2
  138. package/dist/vanillajs/auth/handlers/MessageHandler.js.map +1 -1
  139. package/dist/vanillajs/auth/types/AuthTypes.d.ts +20 -0
  140. package/dist/vanillajs/auth/types/AuthTypes.d.ts.map +1 -1
  141. package/dist/vanillajs/auth/types/AuthTypes.js +1 -0
  142. package/dist/vanillajs/auth/types/AuthTypes.js.map +1 -1
  143. package/dist/vanillajs/iframe/IframeManager.d.ts +36 -0
  144. package/dist/vanillajs/iframe/IframeManager.d.ts.map +1 -1
  145. package/dist/vanillajs/iframe/IframeManager.js +216 -24
  146. package/dist/vanillajs/iframe/IframeManager.js.map +1 -1
  147. package/dist/vanillajs/index.d.ts +2 -0
  148. package/dist/vanillajs/index.d.ts.map +1 -1
  149. package/dist/vanillajs/index.js +4 -0
  150. package/dist/vanillajs/index.js.map +1 -1
  151. package/dist/vanillajs/ui/LoadingComponents.d.ts.map +1 -1
  152. package/dist/vanillajs/ui/LoadingComponents.js +1 -1
  153. package/dist/vanillajs/ui/LoadingComponents.js.map +1 -1
  154. package/package.json +7 -2
@@ -1,4 +1,4 @@
1
- import {} from "../types.js";
1
+ import { tokenKeys, } from "../types.js";
2
2
  import { getUser as getUserFromShared, getTokens as getTokensFromShared, } from "../shared/lib/session.js";
3
3
  import { clearTokens as clearTokensUtil } from "../shared/lib/util.js";
4
4
  import { resolveOAuthAccessCode } from "../server/login.js";
@@ -7,7 +7,37 @@ import { buildLogoutRedirectUrl } from "../server/logout.js";
7
7
  import { refreshTokens } from "../server/refresh.js";
8
8
  import { getVersion } from "../shared/index.js";
9
9
  import { ServerAuthenticationResolver } from "../server/ServerAuthenticationResolver.js";
10
- import { DEFAULT_AUTH_SERVER } from "../constants.js";
10
+ import { DEFAULT_AUTH_SERVER, JWT_PAYLOAD_KNOWN_CLAIM_KEYS, } from "../constants.js";
11
+ import { displayModeFromState } from "../lib/oauth.js";
12
+ import { decodeJwt } from "jose";
13
+ import { generateOauthLogoutUrl } from "../shared/lib/util.js";
14
+ // Function to omit keys from an object
15
+ const omitKeys = (keys, obj) => {
16
+ const result = { ...obj };
17
+ keys.forEach((key) => {
18
+ delete result[key];
19
+ });
20
+ return result;
21
+ };
22
+ /**
23
+ * Extract user information directly from OIDC tokens
24
+ * @param tokens The OIDC tokens response
25
+ * @returns The user object or null if no valid ID token
26
+ */
27
+ function getUserFromTokens(tokens) {
28
+ if (!tokens.id_token)
29
+ return null;
30
+ const parsedToken = decodeJwt(tokens.id_token);
31
+ if (!parsedToken.sub)
32
+ return null;
33
+ // set the user ID from the token sub
34
+ const userWithAdditionalTokenFields = {
35
+ ...parsedToken,
36
+ id: parsedToken.sub,
37
+ };
38
+ // Remove the token keys from the user object to stop it getting too large
39
+ return omitKeys([...JWT_PAYLOAD_KNOWN_CLAIM_KEYS, ...tokenKeys], userWithAdditionalTokenFields);
40
+ }
11
41
  /**
12
42
  * CivicAuth is the main entry point for server-side authentication operations.
13
43
  * It provides a unified interface to all the authentication functions.
@@ -66,10 +96,11 @@ export class CivicAuth {
66
96
  return null;
67
97
  }
68
98
  // If session is valid, use the shared implementation to get the tokens
69
- return getTokensFromShared(this.storage);
99
+ const tokens = await getTokensFromShared(this.storage);
100
+ return tokens;
70
101
  }
71
102
  catch (error) {
72
- console.error("Token validation failed during getTokens", error);
103
+ console.error("Token validation failed during getTokens", error);
73
104
  return null;
74
105
  }
75
106
  }
@@ -112,6 +143,30 @@ export class CivicAuth {
112
143
  * @returns The logout URL
113
144
  */
114
145
  async buildLogoutRedirectUrl(options) {
146
+ // For backend flows with HTTP-only cookies, try to get tokens directly
147
+ // For logout, we don't need valid/authenticated tokens - just the ID token to build logout URL
148
+ try {
149
+ // Use the shared getTokens function directly - this bypasses session validation
150
+ // since for logout we just need the raw ID token, not validated tokens
151
+ const tokens = await getTokensFromShared(this.storage);
152
+ if (tokens?.idToken) {
153
+ // We have access to the ID token from HTTP-only cookies
154
+ // Build the logout URL manually using the shared utility
155
+ const logoutUrl = await generateOauthLogoutUrl({
156
+ clientId: this.authConfig.clientId,
157
+ redirectUrl: this.authConfig.postLogoutRedirectUrl || "/",
158
+ idToken: tokens.idToken,
159
+ state: options?.state ?? Math.random().toString(36).substring(2),
160
+ oauthServer: this.oauthServer,
161
+ });
162
+ return logoutUrl;
163
+ }
164
+ }
165
+ catch (error) {
166
+ // If direct token access fails, fall back to the generic function
167
+ console.warn("❌ Could not get tokens directly from storage, falling back to generic logout method:", error);
168
+ }
169
+ // Fallback to the generic function for other storage types or when tokens aren't accessible
115
170
  return buildLogoutRedirectUrl({
116
171
  ...this.authConfig,
117
172
  scopes: options?.scopes,
@@ -120,7 +175,7 @@ export class CivicAuth {
120
175
  }
121
176
  /**
122
177
  * Refresh the current set of OIDC tokens
123
- * @returns The refreshed tokens
178
+ * @returns The refreshed tokens or null for backend flows where tokens are managed in HTTP-only cookies
124
179
  */
125
180
  async refreshTokens() {
126
181
  return refreshTokens(this.storage, this.authConfig);
@@ -131,5 +186,161 @@ export class CivicAuth {
131
186
  async clearTokens() {
132
187
  return clearTokensUtil(this.storage);
133
188
  }
189
+ /**
190
+ * Smart callback handler that automatically detects frontend vs backend requests
191
+ * and redirects appropriately. Use this instead of resolveOAuthAccessCode + manual redirect.
192
+ *
193
+ * @param params An object containing the authorization code, state, and the incoming request.
194
+ * @param params.code The authorization code from query parameters.
195
+ * @param params.state The OAuth state parameter.
196
+ * @param params.req The incoming request object (e.g., from Express).
197
+ * @param options Configuration options (frontendUrl override, apiResponse flag).
198
+ * @returns Object with redirect information or HTML content for iframe completion.
199
+ *
200
+ * @example
201
+ * ```javascript
202
+ * app.get('/auth/callback', async (req, res) => {
203
+ * const { code, state } = req.query;
204
+ * // The request object 'req' is passed directly
205
+ * const result = await req.civicAuth.handleCallback({ code, state, req });
206
+ *
207
+ * if (result.htmlContent) {
208
+ * res.setHeader('Content-Type', 'text/html');
209
+ * res.send(result.htmlContent);
210
+ * } else if (result.redirectTo) {
211
+ * res.redirect(result.redirectTo);
212
+ * } else {
213
+ * res.json({ success: true, user: result.user });
214
+ * }
215
+ * });
216
+ * ```
217
+ */
218
+ async handleCallback({ code, state, req }, options) {
219
+ // First, resolve the OAuth code and create session
220
+ const tokens = await this.resolveOAuthAccessCode(code, state);
221
+ // Extract user info directly from tokens
222
+ const user = getUserFromTokens(tokens);
223
+ const frontendUrl = options?.frontendUrl || this.authConfig.loginSuccessUrl;
224
+ // Priority 1: Check state for display mode configuration
225
+ const stateDisplayMode = displayModeFromState(state, undefined);
226
+ const isConfiguredForIframe = stateDisplayMode === "iframe";
227
+ // Determine if this should be treated as an iframe request
228
+ // Configuration (from state) takes precedence over auto-detection
229
+ const shouldTreatAsIframe = isConfiguredForIframe && !this.authConfig.disableIframeDetection;
230
+ const isTopLevelRedirect = req.headers["sec-fetch-dest"] === "document";
231
+ const isApiRequest = options?.apiResponse || req.headers.accept?.includes("application/json");
232
+ // Detect Safari or other browsers where iframe postMessage may fail due to cross-origin restrictions
233
+ const userAgent = req.headers["user-agent"] || "";
234
+ const isSafari = userAgent.includes("Safari") && !userAgent.includes("Chrome");
235
+ const isLikelyCrossOriginIframe = isSafari ||
236
+ (userAgent.includes("WebKit") && !userAgent.includes("Chrome"));
237
+ // Case 1: The request should be treated as iframe. Return HTML content.
238
+ // Unless iframe detection is disabled via configuration OR we detect cross-origin issues
239
+ if (shouldTreatAsIframe &&
240
+ user &&
241
+ frontendUrl &&
242
+ !isLikelyCrossOriginIframe) {
243
+ const completionHtml = this.generateIframeCompletionHtml(user);
244
+ return { content: completionHtml };
245
+ }
246
+ // Case 1b: Safari/cross-origin iframe case - redirect instead of HTML
247
+ if (shouldTreatAsIframe &&
248
+ user &&
249
+ frontendUrl &&
250
+ isLikelyCrossOriginIframe) {
251
+ return { redirectTo: frontendUrl };
252
+ }
253
+ // Case 2: The request is a top-level navigation. Return redirect URL.
254
+ if (isTopLevelRedirect && frontendUrl) {
255
+ return { redirectTo: frontendUrl };
256
+ }
257
+ // Case 3: The request is an API call. Return JSON content.
258
+ if (isApiRequest) {
259
+ return {
260
+ content: {
261
+ success: true,
262
+ user,
263
+ },
264
+ };
265
+ }
266
+ // Fallback for older browsers or other contexts: if a frontend URL is configured,
267
+ // assume a redirect to it.
268
+ if (frontendUrl) {
269
+ return { redirectTo: frontendUrl };
270
+ }
271
+ // Server-side fallback: if no frontend URL is configured but we have a postLogoutRedirectUrl,
272
+ // redirect there instead of returning JSON content
273
+ if (this.authConfig.postLogoutRedirectUrl) {
274
+ return { redirectTo: this.authConfig.postLogoutRedirectUrl };
275
+ }
276
+ // Absolute fallback: return success as JSON content if no other conditions are met.
277
+ // This could happen if no loginSuccessUrl or postLogoutRedirectUrl is configured.
278
+ return {
279
+ content: {
280
+ success: true,
281
+ user,
282
+ },
283
+ };
284
+ }
285
+ /**
286
+ * Generate HTML content for iframe completion that sends postMessage to parent
287
+ */
288
+ generateIframeCompletionHtml(user) {
289
+ const escapedUser = JSON.stringify(user).replace(/'/g, "\\'");
290
+ const clientId = this.authConfig.clientId;
291
+ return `
292
+ <!DOCTYPE html>
293
+ <html>
294
+ <head>
295
+ <title>Authentication Complete</title>
296
+ <meta charset="utf-8">
297
+ </head>
298
+ <body>
299
+ <div style="text-align: center; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
300
+ <p>Authentication successful! Completing login...</p>
301
+ </div>
302
+
303
+ <!-- Success signal for SignalObserver -->
304
+ <div id="civic-auth-success-signal" style="display: none;" data-user-info='${escapedUser}'>
305
+ Authentication successful!
306
+ </div>
307
+
308
+ <script>
309
+ // Send postMessage to parent to resolve authentication promise
310
+ if (window.parent && window.parent !== window) {
311
+ console.log('📤 Sending auth success postMessage to parent');
312
+ try {
313
+ window.parent.postMessage({
314
+ type: 'auth_success',
315
+ detail: 'Authentication successful',
316
+ data: {
317
+ user: ${escapedUser}
318
+ }
319
+ }, '*');
320
+ } catch (error) {
321
+ console.error('❌ Failed to send postMessage:', error);
322
+ }
323
+
324
+ // Also send civicloginApp format message for compatibility
325
+ try {
326
+ window.parent.postMessage({
327
+ source: 'civicloginApp',
328
+ type: 'auth_success',
329
+ clientId: '${clientId}',
330
+ data: {
331
+ user: ${escapedUser}
332
+ }
333
+ }, '*');
334
+ } catch (error) {
335
+ console.error('❌ Failed to send civicloginApp message:', error);
336
+ }
337
+ } else {
338
+ console.log('❌ Not in iframe context or no parent window');
339
+ }
340
+ </script>
341
+ </body>
342
+ </html>
343
+ `;
344
+ }
134
345
  }
135
346
  //# sourceMappingURL=session.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AuthStorage,\n type OAuthTokens,\n type User,\n type EmptyObject,\n type UnknownObject,\n type OIDCTokenResponseBody,\n} from \"@/types.js\";\nimport type { AuthConfig } from \"@/server/config.js\";\nimport {\n getUser as getUserFromShared,\n getTokens as getTokensFromShared,\n} from \"@/shared/lib/session.js\";\nimport { clearTokens as clearTokensUtil } from \"@/shared/lib/util.js\";\nimport { resolveOAuthAccessCode } from \"@/server/login.js\";\nimport { buildLoginUrl } from \"@/server/login.js\";\nimport { buildLogoutRedirectUrl } from \"@/server/logout.js\";\nimport { refreshTokens } from \"@/server/refresh.js\";\nimport { getVersion } from \"@/shared/index.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\nimport { DEFAULT_AUTH_SERVER } from \"@/constants.js\";\nimport type { AuthenticationResolver } from \"@/services/types.js\";\n\n/**\n * CivicAuth is the main entry point for server-side authentication operations.\n * It provides a unified interface to all the authentication functions.\n */\nexport class CivicAuth {\n _authResolver: AuthenticationResolver | null = null;\n constructor(\n readonly storage: AuthStorage,\n readonly authConfig: AuthConfig,\n ) {}\n\n get oauthServer(): string {\n return this.authConfig.oauthServer || DEFAULT_AUTH_SERVER;\n }\n\n async getAuthResolver(): Promise<AuthenticationResolver> {\n if (this._authResolver) {\n return Promise.resolve(this._authResolver);\n }\n this._authResolver = await ServerAuthenticationResolver.build(\n {\n ...this.authConfig,\n oauthServer: this.oauthServer,\n },\n this.storage,\n );\n return this._authResolver;\n }\n /**\n * Gets the authenticated user with token validation\n * @returns The user object if authenticated, null otherwise\n */\n async getUser<\n T extends UnknownObject = EmptyObject,\n >(): Promise<User<T> | null> {\n const resolver = await this.getAuthResolver();\n\n try {\n // Validate the session before returning the user\n const session = await resolver.validateExistingSession();\n if (!session?.authenticated) {\n return null;\n }\n\n // If session is valid, use the shared implementation to get the user\n return getUserFromShared<T>(this.storage);\n } catch (error) {\n console.error(\"Token validation failed during getUser\", error);\n return null;\n }\n }\n\n /**\n * Gets the authentication tokens with token validation\n * @returns The tokens if authenticated, null otherwise\n */\n async getTokens(): Promise<OAuthTokens | null> {\n const resolver = await this.getAuthResolver();\n\n try {\n // Validate the session before returning the tokens\n const session = await resolver.validateExistingSession();\n if (!session?.authenticated) {\n return null;\n }\n\n // If session is valid, use the shared implementation to get the tokens\n return getTokensFromShared(this.storage);\n } catch (error) {\n console.error(\"Token validation failed during getTokens\", error);\n return null;\n }\n }\n\n /**\n * Resolve an OAuth access code to a set of OIDC tokens\n * @param code The access code from the query parameter\n * @param state The OAuth state parameter\n * @returns OIDC tokens\n */\n async resolveOAuthAccessCode(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n return resolveOAuthAccessCode(code, state, this.storage, this.authConfig);\n }\n\n /**\n * Check if the user is currently logged in\n * @returns true if logged in, false otherwise\n */\n async isLoggedIn(): Promise<boolean> {\n const resolver = await this.getAuthResolver();\n const session = await resolver.validateExistingSession();\n return session?.authenticated ?? false;\n }\n\n /**\n * Build a login URL to redirect the user to\n * @param options Additional options for building the login URL\n * @returns The login URL\n */\n async buildLoginUrl(options?: {\n scopes?: string[];\n state?: string;\n nonce?: string;\n }): Promise<URL> {\n return buildLoginUrl(\n {\n ...this.authConfig,\n scopes: options?.scopes,\n state: options?.state,\n nonce: options?.nonce,\n framework: \"server\",\n sdkVersion: getVersion(),\n },\n this.storage,\n );\n }\n\n /**\n * Build a logout URL to redirect the user to\n * @param options Additional options for building the logout URL\n * @returns The logout URL\n */\n async buildLogoutRedirectUrl(options?: {\n scopes?: string[];\n state?: string;\n }): Promise<URL> {\n return buildLogoutRedirectUrl(\n {\n ...this.authConfig,\n scopes: options?.scopes,\n state: options?.state,\n },\n this.storage,\n );\n }\n\n /**\n * Refresh the current set of OIDC tokens\n * @returns The refreshed tokens\n */\n async refreshTokens(): Promise<OIDCTokenResponseBody> {\n return refreshTokens(this.storage, this.authConfig);\n }\n\n /**\n * Clear all authentication tokens from storage\n */\n async clearTokens(): Promise<void> {\n return clearTokensUtil(this.storage);\n }\n}\n"]}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/server/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type AuthStorage,\n type OAuthTokens,\n type User,\n type EmptyObject,\n type UnknownObject,\n type OIDCTokenResponseBody,\n tokenKeys,\n} from \"@/types.js\";\nimport type { AuthConfig } from \"@/server/config.js\";\nimport {\n getUser as getUserFromShared,\n getTokens as getTokensFromShared,\n} from \"@/shared/lib/session.js\";\nimport { clearTokens as clearTokensUtil } from \"@/shared/lib/util.js\";\nimport { resolveOAuthAccessCode } from \"@/server/login.js\";\nimport { buildLoginUrl } from \"@/server/login.js\";\nimport { buildLogoutRedirectUrl } from \"@/server/logout.js\";\nimport { refreshTokens } from \"@/server/refresh.js\";\nimport { getVersion } from \"@/shared/index.js\";\nimport { ServerAuthenticationResolver } from \"@/server/ServerAuthenticationResolver.js\";\nimport {\n DEFAULT_AUTH_SERVER,\n JWT_PAYLOAD_KNOWN_CLAIM_KEYS,\n} from \"@/constants.js\";\nimport type { AuthenticationResolver } from \"@/services/types.js\";\nimport { displayModeFromState } from \"@/lib/oauth.js\";\nimport { decodeJwt, type JWTPayload } from \"jose\";\nimport { generateOauthLogoutUrl } from \"@/shared/lib/util.js\";\nexport type HandleCallbackRequest = {\n headers: {\n [key: string]: string | string[] | undefined;\n referer?: string;\n origin?: string;\n \"user-agent\"?: string;\n accept?: string;\n \"sec-fetch-dest\"?: string;\n };\n};\n\nexport type HandleCallbackParams = {\n code: string;\n state: string;\n req: HandleCallbackRequest;\n};\n\n// Function to omit keys from an object\nconst omitKeys = <K extends keyof T, T extends Record<string, unknown>>(\n keys: K[],\n obj: T,\n): Omit<T, K> => {\n const result = { ...obj };\n keys.forEach((key) => {\n delete result[key];\n });\n return result;\n};\n\n/**\n * Extract user information directly from OIDC tokens\n * @param tokens The OIDC tokens response\n * @returns The user object or null if no valid ID token\n */\nfunction getUserFromTokens<T extends UnknownObject = EmptyObject>(\n tokens: OIDCTokenResponseBody,\n): User<T> | null {\n if (!tokens.id_token) return null;\n\n const parsedToken = decodeJwt(tokens.id_token) as JWTPayload & T;\n if (!parsedToken.sub) return null;\n\n // set the user ID from the token sub\n const userWithAdditionalTokenFields = {\n ...(parsedToken as T),\n id: parsedToken.sub,\n };\n\n // Remove the token keys from the user object to stop it getting too large\n return omitKeys(\n [...JWT_PAYLOAD_KNOWN_CLAIM_KEYS, ...tokenKeys],\n userWithAdditionalTokenFields,\n ) as User<T>;\n}\n\n/**\n * CivicAuth is the main entry point for server-side authentication operations.\n * It provides a unified interface to all the authentication functions.\n */\nexport class CivicAuth {\n _authResolver: AuthenticationResolver | null = null;\n constructor(\n readonly storage: AuthStorage,\n readonly authConfig: AuthConfig,\n ) {}\n\n get oauthServer(): string {\n return this.authConfig.oauthServer || DEFAULT_AUTH_SERVER;\n }\n\n async getAuthResolver(): Promise<AuthenticationResolver> {\n if (this._authResolver) {\n return Promise.resolve(this._authResolver);\n }\n this._authResolver = await ServerAuthenticationResolver.build(\n {\n ...this.authConfig,\n oauthServer: this.oauthServer,\n },\n this.storage,\n );\n return this._authResolver;\n }\n /**\n * Gets the authenticated user with token validation\n * @returns The user object if authenticated, null otherwise\n */\n async getUser<\n T extends UnknownObject = EmptyObject,\n >(): Promise<User<T> | null> {\n const resolver = await this.getAuthResolver();\n\n try {\n // Validate the session before returning the user\n const session = await resolver.validateExistingSession();\n if (!session?.authenticated) {\n return null;\n }\n\n // If session is valid, use the shared implementation to get the user\n return getUserFromShared<T>(this.storage);\n } catch (error) {\n console.error(\"Token validation failed during getUser\", error);\n return null;\n }\n }\n\n /**\n * Gets the authentication tokens with token validation\n * @returns The tokens if authenticated, null otherwise\n */\n async getTokens(): Promise<OAuthTokens | null> {\n const resolver = await this.getAuthResolver();\n\n try {\n // Validate the session before returning the tokens\n const session = await resolver.validateExistingSession();\n\n if (!session?.authenticated) {\n return null;\n }\n\n // If session is valid, use the shared implementation to get the tokens\n const tokens = await getTokensFromShared(this.storage);\n return tokens;\n } catch (error) {\n console.error(\"❌ Token validation failed during getTokens\", error);\n return null;\n }\n }\n\n /**\n * Resolve an OAuth access code to a set of OIDC tokens\n * @param code The access code from the query parameter\n * @param state The OAuth state parameter\n * @returns OIDC tokens\n */\n async resolveOAuthAccessCode(\n code: string,\n state: string,\n ): Promise<OIDCTokenResponseBody> {\n return resolveOAuthAccessCode(code, state, this.storage, this.authConfig);\n }\n\n /**\n * Check if the user is currently logged in\n * @returns true if logged in, false otherwise\n */\n async isLoggedIn(): Promise<boolean> {\n const resolver = await this.getAuthResolver();\n const session = await resolver.validateExistingSession();\n return session?.authenticated ?? false;\n }\n\n /**\n * Build a login URL to redirect the user to\n * @param options Additional options for building the login URL\n * @returns The login URL\n */\n async buildLoginUrl(options?: {\n scopes?: string[];\n state?: string;\n nonce?: string;\n }): Promise<URL> {\n return buildLoginUrl(\n {\n ...this.authConfig,\n scopes: options?.scopes,\n state: options?.state,\n nonce: options?.nonce,\n framework: \"server\",\n sdkVersion: getVersion(),\n },\n this.storage,\n );\n }\n\n /**\n * Build a logout URL to redirect the user to\n * @param options Additional options for building the logout URL\n * @returns The logout URL\n */\n async buildLogoutRedirectUrl(options?: {\n scopes?: string[];\n state?: string;\n }): Promise<URL> {\n // For backend flows with HTTP-only cookies, try to get tokens directly\n // For logout, we don't need valid/authenticated tokens - just the ID token to build logout URL\n try {\n // Use the shared getTokens function directly - this bypasses session validation\n // since for logout we just need the raw ID token, not validated tokens\n const tokens = await getTokensFromShared(this.storage);\n\n if (tokens?.idToken) {\n // We have access to the ID token from HTTP-only cookies\n // Build the logout URL manually using the shared utility\n\n const logoutUrl = await generateOauthLogoutUrl({\n clientId: this.authConfig.clientId,\n redirectUrl: this.authConfig.postLogoutRedirectUrl || \"/\",\n idToken: tokens.idToken,\n state: options?.state ?? Math.random().toString(36).substring(2),\n oauthServer: this.oauthServer,\n });\n\n return logoutUrl;\n }\n } catch (error) {\n // If direct token access fails, fall back to the generic function\n console.warn(\n \"❌ Could not get tokens directly from storage, falling back to generic logout method:\",\n error,\n );\n }\n\n // Fallback to the generic function for other storage types or when tokens aren't accessible\n return buildLogoutRedirectUrl(\n {\n ...this.authConfig,\n scopes: options?.scopes,\n state: options?.state,\n },\n this.storage,\n );\n }\n\n /**\n * Refresh the current set of OIDC tokens\n * @returns The refreshed tokens or null for backend flows where tokens are managed in HTTP-only cookies\n */\n async refreshTokens(): Promise<OIDCTokenResponseBody | null> {\n return refreshTokens(this.storage, this.authConfig);\n }\n\n /**\n * Clear all authentication tokens from storage\n */\n async clearTokens(): Promise<void> {\n return clearTokensUtil(this.storage);\n }\n\n /**\n * Smart callback handler that automatically detects frontend vs backend requests\n * and redirects appropriately. Use this instead of resolveOAuthAccessCode + manual redirect.\n *\n * @param params An object containing the authorization code, state, and the incoming request.\n * @param params.code The authorization code from query parameters.\n * @param params.state The OAuth state parameter.\n * @param params.req The incoming request object (e.g., from Express).\n * @param options Configuration options (frontendUrl override, apiResponse flag).\n * @returns Object with redirect information or HTML content for iframe completion.\n *\n * @example\n * ```javascript\n * app.get('/auth/callback', async (req, res) => {\n * const { code, state } = req.query;\n * // The request object 'req' is passed directly\n * const result = await req.civicAuth.handleCallback({ code, state, req });\n *\n * if (result.htmlContent) {\n * res.setHeader('Content-Type', 'text/html');\n * res.send(result.htmlContent);\n * } else if (result.redirectTo) {\n * res.redirect(result.redirectTo);\n * } else {\n * res.json({ success: true, user: result.user });\n * }\n * });\n * ```\n */\n async handleCallback(\n { code, state, req }: HandleCallbackParams,\n options?: {\n frontendUrl?: string;\n apiResponse?: boolean;\n },\n ): Promise<{\n redirectTo?: string;\n content?: string | { success: boolean; user?: User | null };\n }> {\n // First, resolve the OAuth code and create session\n const tokens = await this.resolveOAuthAccessCode(code, state);\n\n // Extract user info directly from tokens\n const user = getUserFromTokens(tokens);\n\n const frontendUrl = options?.frontendUrl || this.authConfig.loginSuccessUrl;\n\n // Priority 1: Check state for display mode configuration\n const stateDisplayMode = displayModeFromState(state, undefined);\n const isConfiguredForIframe = stateDisplayMode === \"iframe\";\n\n // Determine if this should be treated as an iframe request\n // Configuration (from state) takes precedence over auto-detection\n const shouldTreatAsIframe =\n isConfiguredForIframe && !this.authConfig.disableIframeDetection;\n\n const isTopLevelRedirect = req.headers[\"sec-fetch-dest\"] === \"document\";\n const isApiRequest =\n options?.apiResponse || req.headers.accept?.includes(\"application/json\");\n\n // Detect Safari or other browsers where iframe postMessage may fail due to cross-origin restrictions\n const userAgent = req.headers[\"user-agent\"] || \"\";\n const isSafari =\n userAgent.includes(\"Safari\") && !userAgent.includes(\"Chrome\");\n const isLikelyCrossOriginIframe =\n isSafari ||\n (userAgent.includes(\"WebKit\") && !userAgent.includes(\"Chrome\"));\n\n // Case 1: The request should be treated as iframe. Return HTML content.\n // Unless iframe detection is disabled via configuration OR we detect cross-origin issues\n if (\n shouldTreatAsIframe &&\n user &&\n frontendUrl &&\n !isLikelyCrossOriginIframe\n ) {\n const completionHtml = this.generateIframeCompletionHtml(user);\n return { content: completionHtml };\n }\n\n // Case 1b: Safari/cross-origin iframe case - redirect instead of HTML\n if (\n shouldTreatAsIframe &&\n user &&\n frontendUrl &&\n isLikelyCrossOriginIframe\n ) {\n return { redirectTo: frontendUrl };\n }\n\n // Case 2: The request is a top-level navigation. Return redirect URL.\n if (isTopLevelRedirect && frontendUrl) {\n return { redirectTo: frontendUrl };\n }\n\n // Case 3: The request is an API call. Return JSON content.\n if (isApiRequest) {\n return {\n content: {\n success: true,\n user,\n },\n };\n }\n\n // Fallback for older browsers or other contexts: if a frontend URL is configured,\n // assume a redirect to it.\n if (frontendUrl) {\n return { redirectTo: frontendUrl };\n }\n\n // Server-side fallback: if no frontend URL is configured but we have a postLogoutRedirectUrl,\n // redirect there instead of returning JSON content\n if (this.authConfig.postLogoutRedirectUrl) {\n return { redirectTo: this.authConfig.postLogoutRedirectUrl };\n }\n\n // Absolute fallback: return success as JSON content if no other conditions are met.\n // This could happen if no loginSuccessUrl or postLogoutRedirectUrl is configured.\n return {\n content: {\n success: true,\n user,\n },\n };\n }\n\n /**\n * Generate HTML content for iframe completion that sends postMessage to parent\n */\n private generateIframeCompletionHtml(user: User): string {\n const escapedUser = JSON.stringify(user).replace(/'/g, \"\\\\'\");\n const clientId = this.authConfig.clientId;\n\n return `\n <!DOCTYPE html>\n <html>\n <head>\n <title>Authentication Complete</title>\n <meta charset=\"utf-8\">\n </head>\n <body>\n <div style=\"text-align: center; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\">\n <p>Authentication successful! Completing login...</p>\n </div>\n \n <!-- Success signal for SignalObserver -->\n <div id=\"civic-auth-success-signal\" style=\"display: none;\" data-user-info='${escapedUser}'>\n Authentication successful!\n </div>\n \n <script> \n // Send postMessage to parent to resolve authentication promise\n if (window.parent && window.parent !== window) {\n console.log('📤 Sending auth success postMessage to parent');\n try {\n window.parent.postMessage({\n type: 'auth_success',\n detail: 'Authentication successful',\n data: {\n user: ${escapedUser}\n }\n }, '*');\n } catch (error) {\n console.error('❌ Failed to send postMessage:', error);\n }\n \n // Also send civicloginApp format message for compatibility\n try {\n window.parent.postMessage({\n source: 'civicloginApp',\n type: 'auth_success',\n clientId: '${clientId}',\n data: {\n user: ${escapedUser}\n }\n }, '*');\n } catch (error) {\n console.error('❌ Failed to send civicloginApp message:', error);\n }\n } else {\n console.log('❌ Not in iframe context or no parent window');\n }\n </script>\n </body>\n </html>\n `;\n }\n}\n"]}
@@ -0,0 +1,97 @@
1
+ import type { AuthConfig } from "../../server/config.js";
2
+ export interface ExpressRequest {
3
+ cookies: Record<string, string>;
4
+ headers: Record<string, string | string[] | undefined>;
5
+ query: Record<string, string | string[] | undefined>;
6
+ method: string;
7
+ secure: boolean;
8
+ protocol: string;
9
+ storage?: unknown;
10
+ civicAuth?: unknown;
11
+ }
12
+ export interface ExpressResponse {
13
+ header: (name: string, value: string) => void;
14
+ status: (code: number) => ExpressResponse;
15
+ end: () => void;
16
+ redirect: (url: string) => void;
17
+ json: (data: unknown) => void;
18
+ send: (data: string) => void;
19
+ cookie: (name: string, value: string, options?: Record<string, unknown>) => void;
20
+ clearCookie: (name: string) => void;
21
+ }
22
+ export interface ExpressNextFunction {
23
+ (): void;
24
+ }
25
+ export interface ExpressRouter {
26
+ use: (...handlers: Array<(req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => void>) => void;
27
+ get: (path: string, handler: (req: ExpressRequest, res: ExpressResponse, next?: ExpressNextFunction) => void | Promise<void>) => void;
28
+ post: (path: string, handler: (req: ExpressRequest, res: ExpressResponse, next?: ExpressNextFunction) => void | Promise<void>) => void;
29
+ }
30
+ export interface ConditionalLogger {
31
+ info: (message: string, ...args: unknown[]) => void;
32
+ debug: (message: string, ...args: unknown[]) => void;
33
+ error: (message: string, ...args: unknown[]) => void;
34
+ }
35
+ export interface CivicAuthServerOptions {
36
+ /**
37
+ * Custom route overrides - allows users to override specific route handlers
38
+ */
39
+ routes?: {
40
+ login?: (req: ExpressRequest, res: ExpressResponse, next?: ExpressNextFunction) => void | Promise<void>;
41
+ callback?: (req: ExpressRequest, res: ExpressResponse, next?: ExpressNextFunction) => void | Promise<void>;
42
+ logout?: (req: ExpressRequest, res: ExpressResponse, next?: ExpressNextFunction) => void | Promise<void>;
43
+ logoutCallback?: (req: ExpressRequest, res: ExpressResponse, next?: ExpressNextFunction) => void | Promise<void>;
44
+ refresh?: (req: ExpressRequest, res: ExpressResponse, next?: ExpressNextFunction) => void | Promise<void>;
45
+ user?: (req: ExpressRequest, res: ExpressResponse, next?: ExpressNextFunction) => void | Promise<void>;
46
+ };
47
+ /**
48
+ * Custom storage class or factory function - allows users to provide their own storage implementation
49
+ */
50
+ storage?: CookieStorageConstructor | ((req: ExpressRequest, res: ExpressResponse) => CookieStorageInterface);
51
+ }
52
+ export interface ExpressModule {
53
+ Router: () => ExpressRouter;
54
+ }
55
+ export interface CookieStorageConstructor {
56
+ new (options?: {
57
+ secure?: boolean;
58
+ sameSite?: string;
59
+ httpOnly?: boolean;
60
+ path?: string;
61
+ }): CookieStorageInterface;
62
+ }
63
+ export interface CookieStorageInterface {
64
+ settings: {
65
+ secure?: boolean;
66
+ sameSite?: string;
67
+ httpOnly?: boolean;
68
+ path?: string;
69
+ };
70
+ get(key: string): Promise<string | null>;
71
+ set(key: string, value: string): Promise<void>;
72
+ delete(key: string): Promise<void>;
73
+ }
74
+ export interface CivicAuthConstructor {
75
+ new (storage: CookieStorageInterface, config: AuthConfig): CivicAuthInterface;
76
+ }
77
+ export interface CivicAuthInterface {
78
+ buildLoginUrl(): Promise<URL>;
79
+ handleCallback(params: {
80
+ code: string;
81
+ state: string;
82
+ req: ExpressRequest;
83
+ }): Promise<{
84
+ redirectTo?: string;
85
+ content?: string;
86
+ }>;
87
+ buildLogoutRedirectUrl(): Promise<URL>;
88
+ clearTokens(): Promise<void>;
89
+ isLoggedIn(): Promise<boolean>;
90
+ refreshTokens(): Promise<void>;
91
+ getUser(): Promise<unknown>;
92
+ }
93
+ export interface ServerModules {
94
+ CookieStorage: CookieStorageConstructor;
95
+ CivicAuth: CivicAuthConstructor;
96
+ }
97
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../../src/server/types/express.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/server/types/express.ts"],"names":[],"mappings":"","sourcesContent":["import type { AuthConfig } from \"@/server/config.js\";\n\n// Express types - use Express types directly when available\nexport interface ExpressRequest {\n cookies: Record<string, string>;\n headers: Record<string, string | string[] | undefined>;\n query: Record<string, string | string[] | undefined>;\n method: string;\n secure: boolean;\n protocol: string;\n storage?: unknown;\n civicAuth?: unknown;\n}\n\nexport interface ExpressResponse {\n header: (name: string, value: string) => void;\n status: (code: number) => ExpressResponse;\n end: () => void;\n redirect: (url: string) => void;\n json: (data: unknown) => void;\n send: (data: string) => void;\n cookie: (\n name: string,\n value: string,\n options?: Record<string, unknown>,\n ) => void;\n clearCookie: (name: string) => void;\n}\n\nexport interface ExpressNextFunction {\n (): void;\n}\n\nexport interface ExpressRouter {\n use: (\n ...handlers: Array<\n (\n req: ExpressRequest,\n res: ExpressResponse,\n next: ExpressNextFunction,\n ) => void\n >\n ) => void;\n get: (\n path: string,\n handler: (\n req: ExpressRequest,\n res: ExpressResponse,\n next?: ExpressNextFunction,\n ) => void | Promise<void>,\n ) => void;\n post: (\n path: string,\n handler: (\n req: ExpressRequest,\n res: ExpressResponse,\n next?: ExpressNextFunction,\n ) => void | Promise<void>,\n ) => void;\n}\n\n// Logger interface that can be either a real logger or a no-op logger\nexport interface ConditionalLogger {\n info: (message: string, ...args: unknown[]) => void;\n debug: (message: string, ...args: unknown[]) => void;\n error: (message: string, ...args: unknown[]) => void;\n}\n\nexport interface CivicAuthServerOptions {\n /**\n * Custom route overrides - allows users to override specific route handlers\n */\n routes?: {\n login?: (\n req: ExpressRequest,\n res: ExpressResponse,\n next?: ExpressNextFunction,\n ) => void | Promise<void>;\n callback?: (\n req: ExpressRequest,\n res: ExpressResponse,\n next?: ExpressNextFunction,\n ) => void | Promise<void>;\n logout?: (\n req: ExpressRequest,\n res: ExpressResponse,\n next?: ExpressNextFunction,\n ) => void | Promise<void>;\n logoutCallback?: (\n req: ExpressRequest,\n res: ExpressResponse,\n next?: ExpressNextFunction,\n ) => void | Promise<void>;\n refresh?: (\n req: ExpressRequest,\n res: ExpressResponse,\n next?: ExpressNextFunction,\n ) => void | Promise<void>;\n user?: (\n req: ExpressRequest,\n res: ExpressResponse,\n next?: ExpressNextFunction,\n ) => void | Promise<void>;\n };\n /**\n * Custom storage class or factory function - allows users to provide their own storage implementation\n */\n storage?:\n | CookieStorageConstructor\n | ((req: ExpressRequest, res: ExpressResponse) => CookieStorageInterface);\n}\n\n// Interfaces for dynamically imported modules\nexport interface ExpressModule {\n Router: () => ExpressRouter;\n}\n\nexport interface CookieStorageConstructor {\n new (options?: {\n secure?: boolean;\n sameSite?: string;\n httpOnly?: boolean;\n path?: string;\n }): CookieStorageInterface;\n}\n\nexport interface CookieStorageInterface {\n settings: {\n secure?: boolean;\n sameSite?: string;\n httpOnly?: boolean;\n path?: string;\n };\n get(key: string): Promise<string | null>;\n set(key: string, value: string): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\nexport interface CivicAuthConstructor {\n new (storage: CookieStorageInterface, config: AuthConfig): CivicAuthInterface;\n}\n\nexport interface CivicAuthInterface {\n buildLoginUrl(): Promise<URL>;\n handleCallback(params: {\n code: string;\n state: string;\n req: ExpressRequest;\n }): Promise<{\n redirectTo?: string;\n content?: string;\n }>;\n buildLogoutRedirectUrl(): Promise<URL>;\n clearTokens(): Promise<void>;\n isLoggedIn(): Promise<boolean>;\n refreshTokens(): Promise<void>;\n getUser(): Promise<unknown>;\n}\n\nexport interface ServerModules {\n CookieStorage: CookieStorageConstructor;\n CivicAuth: CivicAuthConstructor;\n}\n"]}
@@ -15,9 +15,11 @@ export type BrowserAuthenticationInitiatorConfig = Omit<GenericAuthenticationIni
15
15
  logoutUrl?: string;
16
16
  logoutRedirectUrl: string;
17
17
  displayMode: DisplayMode;
18
+ iframeDisplayMode?: "modal" | "embedded";
18
19
  basePath?: string;
19
20
  loginSuccessUrl?: string;
20
21
  framework?: FrameworkType;
22
+ autoRedirect?: boolean;
21
23
  };
22
24
  /**
23
25
  * An authentication initiator that works on a browser. Since this is just triggering
@@ -44,14 +46,24 @@ export declare class BrowserAuthenticationInitiator implements AuthenticationIni
44
46
  readonly setDesignOptions: (value: LoginAppDesignOptions) => void;
45
47
  private postMessageHandler;
46
48
  protected config: BrowserAuthenticationInitiatorConfig;
49
+ static browserCorsFailsSilentlyRedirectUrl: string;
50
+ private _iframeRef;
47
51
  setDisplayMode(displayMode: DisplayMode): void;
48
52
  get displayMode(): DisplayMode;
49
53
  get isServerTokenExchange(): boolean;
50
54
  get state(): string;
51
55
  instanceId: string;
56
+ browserCorsFailsSilentlyRedirectUrl: string;
52
57
  constructor(config: typeof this.config, setDesignOptions?: (value: LoginAppDesignOptions) => void);
53
58
  handleLoginAppPopupFailed(redirectUrl: string): Promise<void>;
59
+ handleBrowserCorsFailsSilently(redirectUrl: string): Promise<{
60
+ isRedirecting: boolean;
61
+ } | undefined>;
62
+ handleUserInteractionBrowserCorsFailsSilently(): Promise<{
63
+ isRedirecting: boolean;
64
+ }>;
54
65
  handleLoginAppDesignUpdate(options: LoginAppDesignOptions): Promise<void>;
66
+ getSignInUrl(): Promise<URL>;
55
67
  signIn(iframeRef: HTMLIFrameElement | null): Promise<URL>;
56
68
  signOut(idToken: string | undefined, iframeRef: HTMLIFrameElement | null): Promise<URL>;
57
69
  cleanup(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"AuthenticationService.d.ts","sourceRoot":"","sources":["../../src/services/AuthenticationService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AuthenticationService.d.ts","sourceRoot":"","sources":["../../src/services/AuthenticationService.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@ import { DEFAULT_AUTH_SERVER, DEFAULT_OAUTH_GET_PARAMS, LOGOUT_STATE, } from "..
11
11
  import { validateLoginAppPostMessage } from "../lib/postMessage.js";
12
12
  import { getUser } from "../shared/lib/session.js";
13
13
  import { GenericUserSession } from "../shared/lib/UserSession.js";
14
- import { getIframeRef } from "../shared/lib/iframeUtils.js";
14
+ import { getIframeRef, iframeIsVisible, isEmbeddedIframeMode, } from "../shared/lib/iframeUtils.js";
15
15
  import { v4 as uuid } from "uuid";
16
16
  import { CodeVerifier } from "../shared/lib/types.js";
17
17
  import { BrowserAuthenticationRefresher } from "../shared/lib/BrowserAuthenticationRefresher.js";
@@ -50,6 +50,8 @@ export class BrowserAuthenticationInitiator {
50
50
  setDesignOptions;
51
51
  postMessageHandler = null;
52
52
  config;
53
+ static browserCorsFailsSilentlyRedirectUrl;
54
+ _iframeRef = null;
53
55
  setDisplayMode(displayMode) {
54
56
  this.config.displayMode = displayMode;
55
57
  }
@@ -62,6 +64,7 @@ export class BrowserAuthenticationInitiator {
62
64
  get state() {
63
65
  return generateState({
64
66
  displayMode: this.config.displayMode,
67
+ iframeDisplayMode: this.config.iframeDisplayMode,
65
68
  serverTokenExchange: this.isServerTokenExchange,
66
69
  loginSuccessUrl: this.config.loginSuccessUrl,
67
70
  framework: this.config.framework,
@@ -69,6 +72,7 @@ export class BrowserAuthenticationInitiator {
69
72
  });
70
73
  }
71
74
  instanceId;
75
+ browserCorsFailsSilentlyRedirectUrl = "";
72
76
  constructor(config, setDesignOptions = defaultSetDesignOptions) {
73
77
  this.setDesignOptions = setDesignOptions;
74
78
  this.instanceId = uuid();
@@ -85,6 +89,11 @@ export class BrowserAuthenticationInitiator {
85
89
  this.handleLoginAppPopupFailed(loginMessage.data.url);
86
90
  return;
87
91
  }
92
+ if (loginMessage.type === "browserCorsFailsSilently" &&
93
+ this.displayMode === "iframe") {
94
+ this.handleBrowserCorsFailsSilently(loginMessage.data.url);
95
+ return;
96
+ }
88
97
  if (loginMessage.type === "design") {
89
98
  // TODO handle the design message
90
99
  this.handleLoginAppDesignUpdate(loginMessage.data);
@@ -98,19 +107,62 @@ export class BrowserAuthenticationInitiator {
98
107
  console.warn("Login app popup failed open a popup, using redirect mode instead...", redirectUrl);
99
108
  window.location.href = redirectUrl;
100
109
  }
110
+ async handleBrowserCorsFailsSilently(redirectUrl) {
111
+ console.warn("Login app opened in a browser where popups fail silently...", {
112
+ redirectUrl,
113
+ iframeIsVisible: iframeIsVisible(),
114
+ autoRedirect: this.config.autoRedirect,
115
+ });
116
+ BrowserAuthenticationInitiator.browserCorsFailsSilentlyRedirectUrl =
117
+ redirectUrl;
118
+ // Check autoRedirect config before proceeding
119
+ if (this.config.autoRedirect !== false && iframeIsVisible()) {
120
+ // hide the iframe as we're in redirect mode
121
+ // to avoid it loading then immediately disappearing
122
+ const iframe = document.getElementById("civic-auth-iframe-container");
123
+ if (iframe) {
124
+ iframe.style.display = "none";
125
+ }
126
+ return this.handleUserInteractionBrowserCorsFailsSilently();
127
+ }
128
+ }
129
+ async handleUserInteractionBrowserCorsFailsSilently() {
130
+ const isInEmbeddedIframeMode = isEmbeddedIframeMode();
131
+ // check if the iframe is visible
132
+ if (!isInEmbeddedIframeMode &&
133
+ this.config.autoRedirect !== false && // Add this check
134
+ BrowserAuthenticationInitiator.browserCorsFailsSilentlyRedirectUrl) {
135
+ console.warn("Browser CORS failed silently, redirecting...", BrowserAuthenticationInitiator.browserCorsFailsSilentlyRedirectUrl);
136
+ this.config.displayMode = "redirect";
137
+ collectAndSendSDKAnalytics(this.config.clientId, this.config.oauthServer, this.config.framework);
138
+ const signInUrl = await this.getSignInUrl();
139
+ window.dispatchEvent(new CustomEvent("locationWillChange", {
140
+ detail: { newUrl: signInUrl.toString() },
141
+ }));
142
+ setTimeout(() => {
143
+ window.location.href = signInUrl.toString();
144
+ }, 100);
145
+ return { isRedirecting: true };
146
+ }
147
+ return { isRedirecting: false };
148
+ }
101
149
  async handleLoginAppDesignUpdate(options) {
102
150
  this.setDesignOptions(options);
103
151
  }
152
+ async getSignInUrl() {
153
+ const val = await generateOauthLoginUrl({
154
+ ...this.config,
155
+ state: this.state,
156
+ });
157
+ return val;
158
+ }
104
159
  // Use the config (Client ID, scopes OAuth Server, Endpoints, PKCEConsumer) to generate a new login url
105
160
  // and then use the display mode to decide how to send the user there
106
161
  async signIn(iframeRef) {
107
162
  // Send SDK analytics when authentication starts
108
163
  // Fire and forget - don't block authentication if analytics fails
109
164
  collectAndSendSDKAnalytics(this.config.clientId, this.config.oauthServer, this.config.framework);
110
- const url = await generateOauthLoginUrl({
111
- ...this.config,
112
- state: this.state,
113
- });
165
+ const url = await this.getSignInUrl();
114
166
  if (this.config.displayMode === "iframe") {
115
167
  const ref = getIframeRef(iframeRef);
116
168
  ref.setAttribute("src", url.toString());
@@ -312,10 +364,14 @@ export class BrowserAuthenticationService extends BrowserAuthenticationInitiator
312
364
  try {
313
365
  // Perform token refresh (no need to call init explicitly)
314
366
  const tokenResponse = await refresher.refreshAccessToken();
367
+ // For backend flows, tokenResponse might be null since tokens are in HTTP-only cookies
368
+ if (tokenResponse) {
369
+ // Store tokens for SPA flows where tokens are accessible
370
+ await this.storeTokensOnLogin(tokenResponse);
371
+ }
315
372
  // Return a new session with the refreshed tokens
316
373
  const refreshedSession = await this.getSessionData();
317
374
  if (refreshedSession && refreshedSession.authenticated) {
318
- await this.storeTokensOnLogin(tokenResponse);
319
375
  return {
320
376
  ...refreshedSession,
321
377
  authenticated: true,