@civic/auth 0.9.6-beta.1 โ†’ 0.10.0-beta.0

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 (223) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/nextjs/actions.d.ts +12 -0
  3. package/dist/nextjs/actions.d.ts.map +1 -0
  4. package/dist/nextjs/actions.js +26 -0
  5. package/dist/nextjs/actions.js.map +1 -0
  6. package/dist/nextjs/config.d.ts +2 -0
  7. package/dist/nextjs/config.d.ts.map +1 -1
  8. package/dist/nextjs/config.js +3 -2
  9. package/dist/nextjs/config.js.map +1 -1
  10. package/dist/nextjs/cookies.d.ts.map +1 -1
  11. package/dist/nextjs/cookies.js +45 -3
  12. package/dist/nextjs/cookies.js.map +1 -1
  13. package/dist/nextjs/hooks/useInitialAuthConfig.d.ts +31 -0
  14. package/dist/nextjs/hooks/useInitialAuthConfig.d.ts.map +1 -0
  15. package/dist/nextjs/hooks/useInitialAuthConfig.js +113 -0
  16. package/dist/nextjs/hooks/useInitialAuthConfig.js.map +1 -0
  17. package/dist/nextjs/index.d.ts +1 -0
  18. package/dist/nextjs/index.d.ts.map +1 -1
  19. package/dist/nextjs/index.js +13 -3
  20. package/dist/nextjs/index.js.map +1 -1
  21. package/dist/nextjs/providers/NextAuthProvider.d.ts +6 -7
  22. package/dist/nextjs/providers/NextAuthProvider.d.ts.map +1 -1
  23. package/dist/nextjs/providers/NextAuthProvider.js +19 -138
  24. package/dist/nextjs/providers/NextAuthProvider.js.map +1 -1
  25. package/dist/nextjs/providers/NextAuthProviderClient.d.ts +11 -0
  26. package/dist/nextjs/providers/NextAuthProviderClient.d.ts.map +1 -0
  27. package/dist/nextjs/providers/NextAuthProviderClient.js +62 -0
  28. package/dist/nextjs/providers/NextAuthProviderClient.js.map +1 -0
  29. package/dist/nextjs/providers/ServerUserContext.d.ts +2 -0
  30. package/dist/nextjs/providers/ServerUserContext.d.ts.map +1 -0
  31. package/dist/nextjs/providers/ServerUserContext.js +5 -0
  32. package/dist/nextjs/providers/ServerUserContext.js.map +1 -0
  33. package/dist/nextjs/routeHandler.d.ts.map +1 -1
  34. package/dist/nextjs/routeHandler.js +240 -341
  35. package/dist/nextjs/routeHandler.js.map +1 -1
  36. package/dist/react-router-7/components/UserButton.js +1 -1
  37. package/dist/react-router-7/components/UserButton.js.map +1 -1
  38. package/dist/react-router-7/routeHandler.d.ts.map +1 -1
  39. package/dist/react-router-7/routeHandler.js +1 -0
  40. package/dist/react-router-7/routeHandler.js.map +1 -1
  41. package/dist/react-router-7/useUser.d.ts.map +1 -1
  42. package/dist/react-router-7/useUser.js +13 -2
  43. package/dist/react-router-7/useUser.js.map +1 -1
  44. package/dist/reactjs/components/ButtonContentOrLoader.d.ts.map +1 -1
  45. package/dist/reactjs/components/ButtonContentOrLoader.js +1 -3
  46. package/dist/reactjs/components/ButtonContentOrLoader.js.map +1 -1
  47. package/dist/reactjs/components/CivicAuthIframeContainer.d.ts +2 -0
  48. package/dist/reactjs/components/CivicAuthIframeContainer.d.ts.map +1 -0
  49. package/dist/reactjs/components/CivicAuthIframeContainer.js +26 -0
  50. package/dist/reactjs/components/CivicAuthIframeContainer.js.map +1 -0
  51. package/dist/reactjs/components/SignInButton.d.ts.map +1 -1
  52. package/dist/reactjs/components/SignInButton.js +11 -1
  53. package/dist/reactjs/components/SignInButton.js.map +1 -1
  54. package/dist/reactjs/components/UserButton.d.ts +9 -2
  55. package/dist/reactjs/components/UserButton.d.ts.map +1 -1
  56. package/dist/reactjs/components/UserButton.js +41 -9
  57. package/dist/reactjs/components/UserButton.js.map +1 -1
  58. package/dist/reactjs/components/index.d.ts +1 -0
  59. package/dist/reactjs/components/index.d.ts.map +1 -1
  60. package/dist/reactjs/components/index.js +1 -0
  61. package/dist/reactjs/components/index.js.map +1 -1
  62. package/dist/reactjs/core/GlobalAuthManager.d.ts +26 -0
  63. package/dist/reactjs/core/GlobalAuthManager.d.ts.map +1 -1
  64. package/dist/reactjs/core/GlobalAuthManager.js +76 -5
  65. package/dist/reactjs/core/GlobalAuthManager.js.map +1 -1
  66. package/dist/reactjs/hooks/useUser.d.ts +19 -2
  67. package/dist/reactjs/hooks/useUser.d.ts.map +1 -1
  68. package/dist/reactjs/hooks/useUser.js +95 -7
  69. package/dist/reactjs/hooks/useUser.js.map +1 -1
  70. package/dist/reactjs/index.d.ts +1 -2
  71. package/dist/reactjs/index.d.ts.map +1 -1
  72. package/dist/reactjs/index.js +1 -2
  73. package/dist/reactjs/index.js.map +1 -1
  74. package/dist/server/ServerAuthenticationResolver.d.ts.map +1 -1
  75. package/dist/server/ServerAuthenticationResolver.js +18 -0
  76. package/dist/server/ServerAuthenticationResolver.js.map +1 -1
  77. package/dist/server/index.d.ts +1 -1
  78. package/dist/server/index.d.ts.map +1 -1
  79. package/dist/server/index.js.map +1 -1
  80. package/dist/server/logout.d.ts.map +1 -1
  81. package/dist/server/logout.js +11 -2
  82. package/dist/server/logout.js.map +1 -1
  83. package/dist/server/session.d.ts +51 -0
  84. package/dist/server/session.d.ts.map +1 -1
  85. package/dist/server/session.js +296 -17
  86. package/dist/server/session.js.map +1 -1
  87. package/dist/shared/components/SVGLoading.js +1 -1
  88. package/dist/shared/components/SVGLoading.js.map +1 -1
  89. package/dist/shared/components/UserButtonPresentation.d.ts.map +1 -0
  90. package/dist/shared/components/UserButtonPresentation.js.map +1 -0
  91. package/dist/shared/hooks/index.d.ts +1 -2
  92. package/dist/shared/hooks/index.d.ts.map +1 -1
  93. package/dist/shared/hooks/index.js +1 -2
  94. package/dist/shared/hooks/index.js.map +1 -1
  95. package/dist/shared/hooks/useBfcacheHandler.d.ts +23 -0
  96. package/dist/shared/hooks/useBfcacheHandler.d.ts.map +1 -0
  97. package/dist/shared/hooks/useBfcacheHandler.js +65 -0
  98. package/dist/shared/hooks/useBfcacheHandler.js.map +1 -0
  99. package/dist/shared/index.d.ts +1 -0
  100. package/dist/shared/index.d.ts.map +1 -1
  101. package/dist/shared/index.js +1 -0
  102. package/dist/shared/index.js.map +1 -1
  103. package/dist/shared/lib/util.d.ts +32 -0
  104. package/dist/shared/lib/util.d.ts.map +1 -1
  105. package/dist/shared/lib/util.js +79 -0
  106. package/dist/shared/lib/util.js.map +1 -1
  107. package/dist/shared/providers/AuthStatusContext.d.ts.map +1 -1
  108. package/dist/shared/providers/AuthStatusContext.js +2 -1
  109. package/dist/shared/providers/AuthStatusContext.js.map +1 -1
  110. package/dist/shared/providers/CivicAuthConfigContext.d.ts +2 -1
  111. package/dist/shared/providers/CivicAuthConfigContext.d.ts.map +1 -1
  112. package/dist/shared/providers/CivicAuthConfigContext.js +5 -2
  113. package/dist/shared/providers/CivicAuthConfigContext.js.map +1 -1
  114. package/dist/shared/providers/types.d.ts +1 -0
  115. package/dist/shared/providers/types.d.ts.map +1 -1
  116. package/dist/shared/providers/types.js.map +1 -1
  117. package/dist/shared/utils/locationChange.d.ts +34 -0
  118. package/dist/shared/utils/locationChange.d.ts.map +1 -0
  119. package/dist/shared/utils/locationChange.js +28 -0
  120. package/dist/shared/utils/locationChange.js.map +1 -0
  121. package/dist/shared/version.d.ts +1 -1
  122. package/dist/shared/version.d.ts.map +1 -1
  123. package/dist/shared/version.js +1 -1
  124. package/dist/shared/version.js.map +1 -1
  125. package/dist/vanillajs/auth/AuthenticationEvents.d.ts +10 -1
  126. package/dist/vanillajs/auth/AuthenticationEvents.d.ts.map +1 -1
  127. package/dist/vanillajs/auth/AuthenticationEvents.js +29 -0
  128. package/dist/vanillajs/auth/AuthenticationEvents.js.map +1 -1
  129. package/dist/vanillajs/auth/BackendAuthenticationRefresher.d.ts.map +1 -1
  130. package/dist/vanillajs/auth/BackendAuthenticationRefresher.js +2 -2
  131. package/dist/vanillajs/auth/BackendAuthenticationRefresher.js.map +1 -1
  132. package/dist/vanillajs/auth/CivicAuth.d.ts +32 -0
  133. package/dist/vanillajs/auth/CivicAuth.d.ts.map +1 -1
  134. package/dist/vanillajs/auth/CivicAuth.js +270 -55
  135. package/dist/vanillajs/auth/CivicAuth.js.map +1 -1
  136. package/dist/vanillajs/auth/SessionManager.d.ts +3 -2
  137. package/dist/vanillajs/auth/SessionManager.d.ts.map +1 -1
  138. package/dist/vanillajs/auth/SessionManager.js +33 -7
  139. package/dist/vanillajs/auth/SessionManager.js.map +1 -1
  140. package/dist/vanillajs/auth/config/ConfigProcessor.d.ts.map +1 -1
  141. package/dist/vanillajs/auth/config/ConfigProcessor.js +2 -14
  142. package/dist/vanillajs/auth/config/ConfigProcessor.js.map +1 -1
  143. package/dist/vanillajs/auth/handlers/IframeAuthHandler.d.ts.map +1 -1
  144. package/dist/vanillajs/auth/handlers/IframeAuthHandler.js +64 -11
  145. package/dist/vanillajs/auth/handlers/IframeAuthHandler.js.map +1 -1
  146. package/dist/vanillajs/auth/handlers/MessageHandler.d.ts.map +1 -1
  147. package/dist/vanillajs/auth/handlers/MessageHandler.js +4 -1
  148. package/dist/vanillajs/auth/handlers/MessageHandler.js.map +1 -1
  149. package/dist/vanillajs/auth/handlers/PopupHandler.d.ts.map +1 -1
  150. package/dist/vanillajs/auth/handlers/PopupHandler.js +3 -1
  151. package/dist/vanillajs/auth/handlers/PopupHandler.js.map +1 -1
  152. package/dist/vanillajs/auth/types/AuthTypes.d.ts +11 -1
  153. package/dist/vanillajs/auth/types/AuthTypes.d.ts.map +1 -1
  154. package/dist/vanillajs/auth/types/AuthTypes.js.map +1 -1
  155. package/dist/vanillajs/iframe/IframeManager.d.ts +22 -1
  156. package/dist/vanillajs/iframe/IframeManager.d.ts.map +1 -1
  157. package/dist/vanillajs/iframe/IframeManager.js +184 -22
  158. package/dist/vanillajs/iframe/IframeManager.js.map +1 -1
  159. package/dist/vanillajs/types/index.d.ts +1 -1
  160. package/dist/vanillajs/types/index.d.ts.map +1 -1
  161. package/dist/vanillajs/types/index.js +1 -1
  162. package/dist/vanillajs/types/index.js.map +1 -1
  163. package/dist/vanillajs/ui/LoadingComponents.d.ts +4 -0
  164. package/dist/vanillajs/ui/LoadingComponents.d.ts.map +1 -1
  165. package/dist/vanillajs/ui/LoadingComponents.js +51 -1
  166. package/dist/vanillajs/ui/LoadingComponents.js.map +1 -1
  167. package/package.json +3 -3
  168. package/dist/nextjs/hooks/index.d.ts +0 -2
  169. package/dist/nextjs/hooks/index.d.ts.map +0 -1
  170. package/dist/nextjs/hooks/index.js +0 -2
  171. package/dist/nextjs/hooks/index.js.map +0 -1
  172. package/dist/nextjs/hooks/usePrevious.d.ts +0 -2
  173. package/dist/nextjs/hooks/usePrevious.d.ts.map +0 -1
  174. package/dist/nextjs/hooks/usePrevious.js +0 -9
  175. package/dist/nextjs/hooks/usePrevious.js.map +0 -1
  176. package/dist/nextjs/hooks/useUserCookie.d.ts +0 -9
  177. package/dist/nextjs/hooks/useUserCookie.d.ts.map +0 -1
  178. package/dist/nextjs/hooks/useUserCookie.js +0 -109
  179. package/dist/nextjs/hooks/useUserCookie.js.map +0 -1
  180. package/dist/react-router-7/components/UserButtonPresentation.d.ts.map +0 -1
  181. package/dist/react-router-7/components/UserButtonPresentation.js.map +0 -1
  182. package/dist/shared/components/BlockDisplay.d.ts +0 -6
  183. package/dist/shared/components/BlockDisplay.d.ts.map +0 -1
  184. package/dist/shared/components/BlockDisplay.js +0 -30
  185. package/dist/shared/components/BlockDisplay.js.map +0 -1
  186. package/dist/shared/components/CivicAuthIframe.d.ts +0 -10
  187. package/dist/shared/components/CivicAuthIframe.d.ts.map +0 -1
  188. package/dist/shared/components/CivicAuthIframe.js +0 -49
  189. package/dist/shared/components/CivicAuthIframe.js.map +0 -1
  190. package/dist/shared/components/CivicAuthIframeContainer.d.ts +0 -15
  191. package/dist/shared/components/CivicAuthIframeContainer.d.ts.map +0 -1
  192. package/dist/shared/components/CivicAuthIframeContainer.js +0 -177
  193. package/dist/shared/components/CivicAuthIframeContainer.js.map +0 -1
  194. package/dist/shared/components/CivicAuthLogoutIframeContainer.d.ts +0 -6
  195. package/dist/shared/components/CivicAuthLogoutIframeContainer.d.ts.map +0 -1
  196. package/dist/shared/components/CivicAuthLogoutIframeContainer.js +0 -51
  197. package/dist/shared/components/CivicAuthLogoutIframeContainer.js.map +0 -1
  198. package/dist/shared/components/IFrameAndLoading.d.ts +0 -7
  199. package/dist/shared/components/IFrameAndLoading.d.ts.map +0 -1
  200. package/dist/shared/components/IFrameAndLoading.js +0 -66
  201. package/dist/shared/components/IFrameAndLoading.js.map +0 -1
  202. package/dist/shared/hooks/useAuth.d.ts +0 -3
  203. package/dist/shared/hooks/useAuth.d.ts.map +0 -1
  204. package/dist/shared/hooks/useAuth.js +0 -12
  205. package/dist/shared/hooks/useAuth.js.map +0 -1
  206. package/dist/shared/hooks/useIframe.d.ts +0 -3
  207. package/dist/shared/hooks/useIframe.d.ts.map +0 -1
  208. package/dist/shared/hooks/useIframe.js +0 -13
  209. package/dist/shared/hooks/useIframe.js.map +0 -1
  210. package/dist/shared/hooks/useIsInIframe.d.ts +0 -7
  211. package/dist/shared/hooks/useIsInIframe.d.ts.map +0 -1
  212. package/dist/shared/hooks/useIsInIframe.js +0 -23
  213. package/dist/shared/hooks/useIsInIframe.js.map +0 -1
  214. package/dist/shared/hooks/useSignIn.d.ts +0 -20
  215. package/dist/shared/hooks/useSignIn.d.ts.map +0 -1
  216. package/dist/shared/hooks/useSignIn.js +0 -358
  217. package/dist/shared/hooks/useSignIn.js.map +0 -1
  218. package/dist/shared/providers/IframeProvider.d.ts +0 -28
  219. package/dist/shared/providers/IframeProvider.d.ts.map +0 -1
  220. package/dist/shared/providers/IframeProvider.js +0 -64
  221. package/dist/shared/providers/IframeProvider.js.map +0 -1
  222. /package/dist/{react-router-7 โ†’ shared}/components/UserButtonPresentation.d.ts +0 -0
  223. /package/dist/{react-router-7 โ†’ shared}/components/UserButtonPresentation.js +0 -0
@@ -8,7 +8,7 @@ import { AuthenticationEvents } from "./AuthenticationEvents.js";
8
8
  import { PopupError } from "../../services/types.js";
9
9
  import { getVersion } from "../../shared/index.js";
10
10
  import { handleOAuthRedirectPage } from "./handlers/OAuthCallbackHandler.js";
11
- import { generateOauthLogoutUrl, clearTokens, retrieveTokens, getBackendEndpoints, } from "../../shared/lib/util.js";
11
+ import { generateOauthLogoutUrl, clearTokens, retrieveTokens, getBackendEndpoints, resolveEndpointUrl, } from "../../shared/lib/util.js";
12
12
  import { LOGOUT_STATE } from "../../constants.js";
13
13
  import { getOauthEndpoints } from "../../lib/oauth.js";
14
14
  import { collectAndSendSDKAnalytics } from "../../lib/analytics.js";
@@ -74,7 +74,7 @@ export class CivicAuth {
74
74
  // Always initialize events - use provided events or create default instance
75
75
  this.events = config.events || new AuthenticationEvents();
76
76
  // Always initialize SessionManager
77
- this.sessionManager = new SessionManager(this.storage, this.events, this.config);
77
+ this.sessionManager = new SessionManager(this.storage, this.events, this.config, this.config.initialUser);
78
78
  // Initialize handlers
79
79
  this.initializeHandlers();
80
80
  }
@@ -172,13 +172,33 @@ export class CivicAuth {
172
172
  else {
173
173
  this.logger.info("๐Ÿ  Not a callback page, initialization complete");
174
174
  }
175
- // Automatically preload iframe if enabled and user is not authenticated
176
- // This runs after both callback processing and normal initialization
177
- this.logger.info("๐Ÿ” Checking auto-preload conditions", {
178
- preloadIframe: this.config.preloadIframe,
175
+ // Check if we're in embedded mode - we need to show the iframe
176
+ // Note: After config processing, "embedded" displayMode becomes "iframe" + iframeDisplayMode: "embedded"
177
+ const isEmbeddedMode = this.config.displayMode === "iframe" &&
178
+ this.config.iframeDisplayMode === "embedded";
179
+ this.logger.info("๐Ÿ” Checking display mode conditions", {
179
180
  displayMode: this.config.displayMode,
181
+ iframeDisplayMode: this.config.iframeDisplayMode,
182
+ isEmbeddedMode,
183
+ preloadIframe: this.config.preloadIframe,
180
184
  });
181
- if (this.config.preloadIframe && this.config.displayMode === "iframe") {
185
+ if (isEmbeddedMode) {
186
+ // For embedded mode, we need to start authentication to show the iframe
187
+ try {
188
+ this.logger.info("๐Ÿ–ผ๏ธ Starting authentication for embedded mode to show iframe...");
189
+ await this.startAuthentication();
190
+ this.logger.info("โœ… Embedded iframe authentication started successfully");
191
+ }
192
+ catch (error) {
193
+ // Don't fail initialization if embedded authentication fails - graceful degradation
194
+ this.logger.warn("โš ๏ธ Embedded authentication failed (graceful degradation):", {
195
+ error: error instanceof Error ? error.message : String(error),
196
+ });
197
+ }
198
+ }
199
+ else if (this.config.preloadIframe &&
200
+ this.config.displayMode === "iframe") {
201
+ // Regular iframe preloading logic for modal mode
182
202
  try {
183
203
  const isAuthenticated = await this.isAuthenticated();
184
204
  this.logger.info("๐Ÿ” Authentication check for preload", {
@@ -204,9 +224,12 @@ export class CivicAuth {
204
224
  this.logger.info("โ„น๏ธ Auto-preload conditions not met", {
205
225
  preloadIframe: this.config.preloadIframe,
206
226
  displayMode: this.config.displayMode,
207
- reason: !this.config.preloadIframe
208
- ? "preloadIframe is disabled"
209
- : "displayMode is not iframe",
227
+ isEmbeddedMode,
228
+ reason: isEmbeddedMode
229
+ ? "embedded mode uses startAuthentication instead"
230
+ : !this.config.preloadIframe
231
+ ? "preloadIframe is disabled"
232
+ : "displayMode is not iframe",
210
233
  });
211
234
  }
212
235
  }
@@ -298,6 +321,7 @@ export class CivicAuth {
298
321
  iframeDisplayMode: this.config.iframeDisplayMode,
299
322
  framework: this.config.framework || "vanillajs",
300
323
  sdkVersion: getVersion(),
324
+ loginSuccessUrl: this.config.loginSuccessUrl,
301
325
  });
302
326
  // Append state as query parameter to loginUrl
303
327
  const url = new URL(this.loginUrl, window.location.origin);
@@ -324,6 +348,7 @@ export class CivicAuth {
324
348
  iframeDisplayMode: this.config.iframeDisplayMode,
325
349
  framework: this.config.framework || "vanillajs",
326
350
  sdkVersion: getVersion(),
351
+ loginSuccessUrl: this.config.loginSuccessUrl,
327
352
  });
328
353
  return buildAuthUrl({
329
354
  endpoints: this.endpoints,
@@ -474,6 +499,82 @@ export class CivicAuth {
474
499
  });
475
500
  return this.authPromise;
476
501
  }
502
+ /**
503
+ * Reloads embedded authentication interface
504
+ *
505
+ * This method is specifically designed for embedded mode scenarios where the iframe
506
+ * needs to be recreated after navigation or DOM changes. It performs a complete
507
+ * re-initialization by calling the full init() method.
508
+ *
509
+ * @returns Promise that resolves when the embedded iframe is created
510
+ * @throws {CivicAuthError} If not in embedded mode or if iframe creation fails
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * // After navigation in a SPA
515
+ * await civicAuth.reloadEmbedded();
516
+ * ```
517
+ */
518
+ async reloadEmbedded() {
519
+ this.logger.info("๐Ÿ”„ Reloading embedded authentication interface", {
520
+ displayMode: this.config.displayMode,
521
+ iframeDisplayMode: this.config.iframeDisplayMode,
522
+ authPromise: !!this.authPromise,
523
+ });
524
+ this.config.displayMode = "iframe";
525
+ this.config.iframeDisplayMode = "embedded";
526
+ // Check if container element exists
527
+ const container = this.getContainerElement();
528
+ this.logger.info("๐ŸŽฏ Container element check", {
529
+ containerFound: !!container,
530
+ containerId: container?.id,
531
+ containerClass: container?.className,
532
+ });
533
+ if (!container) {
534
+ const error = new CivicAuthError("Container element for embedded iframe not found. Ensure an element with id 'civic-login-container' exists or provide targetContainerElement in config.", CivicAuthErrorCode.CONTAINER_NOT_FOUND);
535
+ this.logger.error(error.message);
536
+ throw error;
537
+ }
538
+ // Clear any existing auth promise to ensure fresh start
539
+ if (this.authPromise) {
540
+ this.logger.info("๐Ÿงน Clearing existing auth promise for fresh start");
541
+ this.authPromise = undefined;
542
+ this.authPromiseResolve = undefined;
543
+ this.authPromiseReject = undefined;
544
+ }
545
+ // Clean up any existing handlers before re-initialization
546
+ if (this.messageHandler || this.popupHandler || this.iframeAuthHandler) {
547
+ this.logger.info("๐Ÿงน Cleaning up existing handlers before re-initialization");
548
+ this.cleanup();
549
+ }
550
+ this.logger.info("๐Ÿ”„ Re-initializing handlers and starting embedded auth");
551
+ try {
552
+ // Don't call init() - that would re-process config based on current page
553
+ // Instead, do minimal re-initialization with original config
554
+ // Ensure OAuth endpoints are available (they should be from original init)
555
+ if (!this.endpoints) {
556
+ this.logger.info("๐Ÿ”— Getting OAuth endpoints");
557
+ this.endpoints = await getOauthEndpoints(this.config.oauthServerBaseUrl);
558
+ }
559
+ // Re-initialize all handlers (creates new instances)
560
+ this.logger.info("๐Ÿ”„ Re-initializing handlers");
561
+ this.initializeHandlers();
562
+ // Start authentication to create the embedded iframe
563
+ this.logger.info("๐ŸŽฌ Starting embedded authentication");
564
+ await this.startAuthentication();
565
+ this.logger.info("โœ… Embedded iframe reloaded successfully");
566
+ }
567
+ catch (error) {
568
+ const errorMessage = error instanceof Error
569
+ ? error.message
570
+ : "Failed to reload embedded iframe";
571
+ this.logger.error("โŒ Failed to reload embedded iframe", {
572
+ error: errorMessage,
573
+ stack: error instanceof Error ? error.stack : undefined,
574
+ });
575
+ throw error;
576
+ }
577
+ }
477
578
  async handleBrowserCorsFailsSilently() {
478
579
  this.logger.warn("๐Ÿšจ Browser CORS fails silently - switching to redirect mode", {
479
580
  displayMode: this.config.displayMode,
@@ -510,7 +611,7 @@ export class CivicAuth {
510
611
  switch (this.config.displayMode) {
511
612
  case "redirect":
512
613
  this.logger.info("๐ŸŒ Using redirect mode");
513
- window.location.href = fullAuthUrl;
614
+ this.events.locationChange(fullAuthUrl, "Redirecting to auth URL", "login");
514
615
  break;
515
616
  case "new_tab":
516
617
  this.logger.info("๐Ÿ“ฑ Using new_tab mode");
@@ -614,7 +715,19 @@ export class CivicAuth {
614
715
  handleAuthSuccess(result) {
615
716
  this.logger.info("โœ… Authentication successful");
616
717
  this.authPromiseResolve?.(result);
617
- this.cleanup();
718
+ // For embedded mode, preserve the iframe completely - don't cleanup
719
+ // The iframe should stay visible showing the successful authentication state
720
+ const isEmbeddedMode = this.config.displayMode === "iframe" &&
721
+ this.config.iframeDisplayMode === "embedded";
722
+ if (isEmbeddedMode) {
723
+ this.logger.info("๐Ÿ”ง Embedded mode - preserving iframe, skipping cleanup");
724
+ // Only clean up timeouts and reset promises, but keep iframe visible
725
+ this.cleanupTimeoutsAndState();
726
+ }
727
+ else {
728
+ // For modal/redirect modes, do full cleanup as before
729
+ this.cleanup();
730
+ }
618
731
  }
619
732
  /**
620
733
  * Handle authentication error
@@ -622,7 +735,19 @@ export class CivicAuth {
622
735
  handleAuthError(error) {
623
736
  this.logger.error("โŒ Authentication failed", { error: error.message });
624
737
  this.authPromiseReject?.(error);
625
- this.cleanup();
738
+ // For embedded mode, preserve the iframe to show error state
739
+ // For other modes, do full cleanup as before
740
+ const isEmbeddedMode = this.config.displayMode === "iframe" &&
741
+ this.config.iframeDisplayMode === "embedded";
742
+ if (isEmbeddedMode) {
743
+ this.logger.info("๐Ÿ”ง Embedded mode - preserving iframe to show error state");
744
+ // Only clean up timeouts and reset promises, but keep iframe visible
745
+ this.cleanupTimeoutsAndState();
746
+ }
747
+ else {
748
+ // For modal/redirect modes, do full cleanup as before
749
+ this.cleanup();
750
+ }
626
751
  }
627
752
  /**
628
753
  * Handle popup failure - simplified like React implementation
@@ -638,21 +763,22 @@ export class CivicAuth {
638
763
  }
639
764
  // Always redirect to the failed URL or build a new one
640
765
  if (failedUrl) {
641
- window.location.href = failedUrl;
766
+ // this.config.displayMode = "redirect";
767
+ // Use centralized location change handler
768
+ this.events.locationChange(failedUrl, "Popup blocked, falling back to redirect mode", "login");
769
+ return;
642
770
  }
643
- else {
644
- this.buildAuthUrl()
645
- .then((authUrl) => {
646
- window.location.href = authUrl;
647
- })
648
- .catch((error) => {
649
- this.logger.error("Failed to build auth URL for redirect fallback", {
650
- error,
651
- });
652
- const fallbackError = new CivicAuthError("Failed to redirect for authentication", CivicAuthErrorCode.INIT_FAILED);
653
- this.handleAuthError(fallbackError);
771
+ this.buildAuthUrl()
772
+ .then((authUrl) => {
773
+ this.events.locationChange(authUrl, "Redirecting to auth URL", "login");
774
+ })
775
+ .catch((error) => {
776
+ this.logger.error("Failed to build auth URL for redirect fallback", {
777
+ error,
654
778
  });
655
- }
779
+ const fallbackError = new CivicAuthError("Failed to redirect for authentication", CivicAuthErrorCode.INIT_FAILED);
780
+ this.handleAuthError(fallbackError);
781
+ });
656
782
  }
657
783
  /**
658
784
  * Show popup failure message to user
@@ -716,7 +842,16 @@ export class CivicAuth {
716
842
  }
717
843
  return element;
718
844
  }
719
- return this.config.targetContainerElement ?? null;
845
+ if (this.config.targetContainerElement) {
846
+ return this.config.targetContainerElement;
847
+ }
848
+ // Fallback: Look for element with id "civic-login-container"
849
+ const fallbackContainer = document.getElementById("civic-login-container");
850
+ if (fallbackContainer) {
851
+ this.logger.debug('Using fallback container with id "civic-login-container"');
852
+ return fallbackContainer;
853
+ }
854
+ return null;
720
855
  }
721
856
  /**
722
857
  * Handle OAuth callback
@@ -810,6 +945,27 @@ export class CivicAuth {
810
945
  window.removeEventListener("message", this.messageHandler.handleMessage);
811
946
  }
812
947
  }
948
+ /**
949
+ * Clean up only timeouts and state without touching iframe (for embedded mode)
950
+ */
951
+ cleanupTimeoutsAndState() {
952
+ this.logger.info("Cleaning up timeouts and state (preserving iframe for embedded mode)");
953
+ // Clean up timeouts
954
+ if (this.authProcessTimeoutHandle) {
955
+ window.clearTimeout(this.authProcessTimeoutHandle);
956
+ this.authProcessTimeoutHandle = undefined;
957
+ }
958
+ if (this.popupFailureTimeoutHandle) {
959
+ window.clearTimeout(this.popupFailureTimeoutHandle);
960
+ this.popupFailureTimeoutHandle = undefined;
961
+ }
962
+ // Reset state
963
+ this.hasPopupFailed = false;
964
+ this.authPromise = undefined;
965
+ this.authPromiseResolve = undefined;
966
+ this.authPromiseReject = undefined;
967
+ // Keep message handler and iframe for embedded mode
968
+ }
813
969
  /**
814
970
  * Get the current session
815
971
  */
@@ -917,6 +1073,37 @@ export class CivicAuth {
917
1073
  getIframeDisplayMode() {
918
1074
  return this.config.iframeDisplayMode;
919
1075
  }
1076
+ /**
1077
+ * Reset authentication state when page is restored from back-forward cache (bfcache)
1078
+ * This clears any pending authentication promises that may have been interrupted
1079
+ * by the browser caching the page, allowing fresh authentication attempts
1080
+ */
1081
+ resetOnBfcache() {
1082
+ this.logger.info("๐Ÿ”„ Resetting authentication state due to bfcache restore");
1083
+ // Clear pending authentication promise and related state
1084
+ if (this.authPromise) {
1085
+ this.logger.debug("Clearing stale authentication promise from bfcache");
1086
+ this.authPromise = undefined;
1087
+ this.authPromiseResolve = undefined;
1088
+ this.authPromiseReject = undefined;
1089
+ }
1090
+ // Clear timeouts that may no longer be valid
1091
+ if (this.authProcessTimeoutHandle) {
1092
+ window.clearTimeout(this.authProcessTimeoutHandle);
1093
+ this.authProcessTimeoutHandle = undefined;
1094
+ }
1095
+ if (this.popupFailureTimeoutHandle) {
1096
+ window.clearTimeout(this.popupFailureTimeoutHandle);
1097
+ this.popupFailureTimeoutHandle = undefined;
1098
+ }
1099
+ // Reset auth flags
1100
+ this.hasPopupFailed = false;
1101
+ this.hasSignInStarted = false;
1102
+ // Reset iframe handler state if needed
1103
+ if (this.iframeAuthHandler) {
1104
+ this.iframeAuthHandler.cleanupIframe();
1105
+ }
1106
+ }
920
1107
  /**
921
1108
  * Destroy the auth client and clean up all resources
922
1109
  */
@@ -940,17 +1127,33 @@ export class CivicAuth {
940
1127
  this.logger.info("๐Ÿšช Redirecting to backend for logout in backend-integrated flow");
941
1128
  const backendUrl = new URL(this.loginUrl).origin;
942
1129
  const endpoints = getBackendEndpoints(this.config.backendEndpoints);
943
- const backendLogoutUrl = `${backendUrl}${endpoints.logout}`;
1130
+ const backendLogoutUrl = resolveEndpointUrl(backendUrl, endpoints.logout);
944
1131
  // Clear local SDK session state before redirecting
945
1132
  await this.sessionManager.clearSession();
946
1133
  this.events.emit(AuthEvent.SIGN_OUT_COMPLETE, {
947
1134
  detail: "Local session cleared, redirecting to backend for logout.",
948
1135
  });
1136
+ // Generate state for backend integration to enable iframe mode detection
1137
+ const state = generateState({
1138
+ displayMode: this.config.displayMode || "iframe",
1139
+ framework: this.config.framework || "vanillajs",
1140
+ sdkVersion: getVersion(),
1141
+ });
949
1142
  // Perform top-level redirect to the backend logout endpoint
950
- this.logger.info("๐ŸŒ Redirecting to backend logout endpoint", {
951
- url: backendLogoutUrl,
1143
+ // Include logoutRedirectUrl and state as query parameters
1144
+ const logoutUrl = new URL(backendLogoutUrl);
1145
+ if (this.config.logoutRedirectUrl) {
1146
+ logoutUrl.searchParams.set("logoutRedirectUrl", this.config.logoutRedirectUrl);
1147
+ }
1148
+ // Include state for iframe mode detection on server
1149
+ logoutUrl.searchParams.set("state", state);
1150
+ this.logger.info("๐ŸŒ Loading backend logout endpoint in iframe", {
1151
+ url: logoutUrl.toString(),
1152
+ logoutRedirectUrl: this.config.logoutRedirectUrl,
1153
+ displayMode: this.config.displayMode,
952
1154
  });
953
- window.location.href = backendLogoutUrl;
1155
+ // Use iframe for SPA logout to avoid full page reload
1156
+ this.loadLogoutInIframe(logoutUrl);
954
1157
  return;
955
1158
  }
956
1159
  // --- Existing SPA redirect-based logout flow ---
@@ -1002,30 +1205,7 @@ export class CivicAuth {
1002
1205
  detail: "Logout successful",
1003
1206
  });
1004
1207
  // Use hidden iframe instead of redirect to avoid full page reload in SPA mode
1005
- this.logger.info("๐ŸŒ Loading logout URL in hidden iframe");
1006
- const logoutIframe = document.createElement("iframe");
1007
- logoutIframe.style.display = "none";
1008
- // Clean up iframe when logout completes (or errors)
1009
- const cleanupIframe = () => {
1010
- this.logger.info("๐Ÿงน Cleaning up logout iframe");
1011
- if (!logoutIframe.parentNode) {
1012
- this.logger.warn("๐Ÿงน Logout iframe not found, skipping iframe cleanup");
1013
- return;
1014
- }
1015
- logoutIframe.parentNode.removeChild(logoutIframe);
1016
- if (this.config.logoutRedirectUrl) {
1017
- window.location.href = this.config.logoutRedirectUrl;
1018
- }
1019
- };
1020
- logoutIframe.onload = () => {
1021
- cleanupIframe();
1022
- };
1023
- logoutIframe.onerror = (error) => {
1024
- this.logger.error("โŒ Logout iframe failed to load", { error });
1025
- cleanupIframe();
1026
- };
1027
- logoutIframe.src = logoutUrl.toString();
1028
- document.body.appendChild(logoutIframe);
1208
+ this.loadLogoutInIframe(logoutUrl);
1029
1209
  }
1030
1210
  catch (error) {
1031
1211
  this.logger.error("โŒ Logout failed", { error });
@@ -1035,6 +1215,41 @@ export class CivicAuth {
1035
1215
  throw new CivicAuthError("Logout failed", CivicAuthErrorCode.LOGOUT_FAILED);
1036
1216
  }
1037
1217
  }
1218
+ /**
1219
+ * Load logout URL in a hidden iframe to avoid full page reload
1220
+ * @param logoutUrl The URL to load in the iframe
1221
+ */
1222
+ loadLogoutInIframe(logoutUrl) {
1223
+ this.logger.info("๐ŸŒ Loading logout URL in hidden iframe");
1224
+ const logoutIframe = document.createElement("iframe");
1225
+ logoutIframe.style.display = "none";
1226
+ // Clean up iframe when logout completes (or errors)
1227
+ const cleanupIframe = () => {
1228
+ this.logger.info("๐Ÿงน Cleaning up logout iframe");
1229
+ if (!logoutIframe.parentNode) {
1230
+ this.logger.warn("๐Ÿงน Logout iframe not found, skipping iframe cleanup");
1231
+ return;
1232
+ }
1233
+ logoutIframe.parentNode.removeChild(logoutIframe);
1234
+ if (this.config.logoutRedirectUrl) {
1235
+ this.logger.info("๐ŸŒ Redirecting to logoutRedirectUrl after logout iframe cleanup", {
1236
+ logoutRedirectUrl: this.config.logoutRedirectUrl,
1237
+ });
1238
+ // Use direct redirect for logout to ensure frontend state is cleared
1239
+ // This bypasses any URL_CHANGE event listeners that might interfere
1240
+ window.location.href = this.config.logoutRedirectUrl;
1241
+ }
1242
+ };
1243
+ logoutIframe.onload = () => {
1244
+ cleanupIframe();
1245
+ };
1246
+ logoutIframe.onerror = (error) => {
1247
+ this.logger.error("โŒ Logout iframe failed to load", { error });
1248
+ cleanupIframe();
1249
+ };
1250
+ logoutIframe.src = logoutUrl.toString();
1251
+ document.body.appendChild(logoutIframe);
1252
+ }
1038
1253
  /**
1039
1254
  * Handle logout state cleanup
1040
1255
  * This mirrors the logic from useSignIn.ts to properly clean up after logout