@civic/auth 0.0.1-beta.0 → 0.0.1-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.
package/dist/react.mjs CHANGED
@@ -52,7 +52,7 @@ var __async = (__this, __arguments, generator) => {
52
52
  };
53
53
 
54
54
  // src/react/hooks/useUser.tsx
55
- import { useContext as useContext3 } from "react";
55
+ import { useContext as useContext4 } from "react";
56
56
 
57
57
  // src/react/providers/UserProvider.tsx
58
58
  import { createContext } from "react";
@@ -79,25 +79,24 @@ var useToken = () => {
79
79
  };
80
80
 
81
81
  // src/react/providers/UserProvider.tsx
82
- import { parseJWT } from "oslo/jwt";
83
82
  import { jsx } from "react/jsx-runtime";
84
83
  var UserContext = createContext(null);
85
84
  var UserProvider = ({
86
- children
85
+ children,
86
+ userInfoService
87
87
  }) => {
88
88
  const { isLoading: authLoading, error: authError } = useAuth();
89
89
  const session = useSession();
90
90
  const { forwardedTokens, idToken, accessToken, refreshToken } = useToken();
91
- const { isAuthenticated, signIn, signOut } = useAuth();
91
+ const { signIn, signOut } = useAuth();
92
92
  const fetchUser = () => __async(void 0, null, function* () {
93
- if (!(session == null ? void 0 : session.idToken)) {
93
+ if (!accessToken || !userInfoService) {
94
94
  return null;
95
95
  }
96
- const parsedJWT = parseJWT(session.idToken);
97
- if (!parsedJWT) {
96
+ const user2 = yield userInfoService == null ? void 0 : userInfoService.getUserInfo(accessToken, idToken);
97
+ if (!user2) {
98
98
  return null;
99
99
  }
100
- const user2 = parsedJWT.payload;
101
100
  return __spreadProps(__spreadValues({}, user2), {
102
101
  forwardedTokens,
103
102
  idToken,
@@ -108,12 +107,11 @@ var UserProvider = ({
108
107
  const {
109
108
  data: user,
110
109
  isLoading: userLoading,
111
- error: userError,
112
- refetch
110
+ error: userError
113
111
  } = useQuery({
114
- queryKey: ["user", session == null ? void 0 : session.accessToken],
112
+ queryKey: ["user", session == null ? void 0 : session.idToken],
115
113
  queryFn: fetchUser,
116
- enabled: !!(session == null ? void 0 : session.accessToken)
114
+ enabled: !!(session == null ? void 0 : session.idToken)
117
115
  // Only run the query if we have an access token
118
116
  });
119
117
  const isLoading = authLoading || userLoading;
@@ -122,11 +120,9 @@ var UserProvider = ({
122
120
  UserContext.Provider,
123
121
  {
124
122
  value: {
125
- user,
123
+ user: user != null ? user : null,
126
124
  isLoading,
127
125
  error,
128
- refetch,
129
- isAuthenticated,
130
126
  signIn,
131
127
  signOut
132
128
  },
@@ -138,27 +134,41 @@ var UserProvider = ({
138
134
  // src/react/providers/TokenProvider.tsx
139
135
  import { createContext as createContext2, useMemo } from "react";
140
136
  import { useMutation, useQueryClient } from "@tanstack/react-query";
141
- import { parseJWT as parseJWT2 } from "oslo/jwt";
137
+ import { parseJWT } from "oslo/jwt";
138
+
139
+ // src/lib/jwt.ts
140
+ var convertForwardedTokenFormat = (inputTokens) => Object.fromEntries(
141
+ Object.entries(inputTokens).map(([source, tokens]) => [
142
+ source,
143
+ {
144
+ idToken: tokens == null ? void 0 : tokens.id_token,
145
+ accessToken: tokens == null ? void 0 : tokens.access_token,
146
+ refreshToken: tokens == null ? void 0 : tokens.refresh_token
147
+ }
148
+ ])
149
+ );
150
+
151
+ // src/react/providers/TokenProvider.tsx
142
152
  import { jsx as jsx2 } from "react/jsx-runtime";
143
153
  var TokenContext = createContext2(void 0);
144
154
  var TokenProvider = ({ children }) => {
145
155
  const { isLoading, error: authError } = useAuth();
146
156
  const session = useSession();
147
- const queryClient2 = useQueryClient();
157
+ const queryClient3 = useQueryClient();
148
158
  const refreshTokenMutation = useMutation({
149
159
  mutationFn: () => __async(void 0, null, function* () {
150
160
  throw new Error("Method not implemented.");
151
161
  }),
152
162
  onSuccess: () => {
153
- queryClient2.invalidateQueries({ queryKey: ["session"] });
163
+ queryClient3.invalidateQueries({ queryKey: ["session"] });
154
164
  }
155
165
  });
156
166
  const decodeTokens = useMemo(() => {
157
167
  if (!(session == null ? void 0 : session.idToken)) return null;
158
- const parsedJWT = parseJWT2(session.idToken);
168
+ const parsedJWT = parseJWT(session.idToken);
159
169
  if (!parsedJWT) return null;
160
170
  const { forwardedTokens } = parsedJWT.payload;
161
- return forwardedTokens;
171
+ return forwardedTokens ? convertForwardedTokenFormat(forwardedTokens) : null;
162
172
  }, [session == null ? void 0 : session.idToken]);
163
173
  const value = useMemo(
164
174
  () => ({
@@ -182,35 +192,39 @@ var TokenProvider = ({ children }) => {
182
192
  return /* @__PURE__ */ jsx2(TokenContext.Provider, { value, children });
183
193
  };
184
194
 
185
- // src/react/providers/AuthProvider.tsx
195
+ // src/shared/AuthProvider.tsx
186
196
  import {
187
- createContext as createContext5,
197
+ createContext as createContext4,
188
198
  useState as useState3,
189
199
  useEffect as useEffect3,
190
200
  useMemo as useMemo2,
191
- useCallback as useCallback2
201
+ useCallback as useCallback3,
202
+ useRef as useRef2
192
203
  } from "react";
193
204
  import { useQuery as useQuery2, useMutation as useMutation2, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
194
205
 
195
206
  // src/services/UserInfoService.ts
207
+ import { parseJWT as parseJWT2 } from "oslo/jwt";
196
208
  var UserInfoServiceImpl = class {
197
209
  constructor(endpoints) {
198
210
  this.endpoints = endpoints;
199
211
  }
200
- getUserInfo(accessToken) {
212
+ extractUserFromIdToken(idToken) {
213
+ const parsedJWT = parseJWT2(idToken);
214
+ if (!parsedJWT) {
215
+ return null;
216
+ }
217
+ return parsedJWT.payload;
218
+ }
219
+ getUserInfo(accessToken, idToken) {
201
220
  return __async(this, null, function* () {
202
- return {
203
- id: "user123",
204
- name: "John Doe",
205
- email: "john@example.com",
206
- picture: "https://example.com/john.jpg",
207
- given_name: "John",
208
- family_name: "Doe",
209
- created_at: /* @__PURE__ */ new Date(),
210
- updated_at: /* @__PURE__ */ new Date(),
211
- country: "US",
212
- accessToken
213
- };
221
+ if (idToken) {
222
+ return this.extractUserFromIdToken(idToken);
223
+ }
224
+ const userInfo = yield fetch(this.endpoints.userinfo, {
225
+ headers: { Authorization: `Bearer ${accessToken}` }
226
+ });
227
+ return userInfo.json();
214
228
  });
215
229
  }
216
230
  };
@@ -244,13 +258,14 @@ var generateState = (displayMode) => {
244
258
  uuid: uuid(),
245
259
  displayMode
246
260
  });
247
- return Buffer.from(jsonString).toString("base64");
261
+ return btoa(jsonString);
248
262
  };
249
263
  var displayModeFromState = (state, sessionDisplayMode) => {
250
264
  try {
251
- const jsonString = Buffer.from(state, "base64").toString();
265
+ const jsonString = btoa(state);
252
266
  return JSON.parse(jsonString).displayMode;
253
- } catch (_e) {
267
+ } catch (e) {
268
+ console.error("Failed to parse displayMode from state:", e);
254
269
  return sessionDisplayMode;
255
270
  }
256
271
  };
@@ -283,44 +298,49 @@ var AuthSessionServiceImpl = class {
283
298
  this.clientId = clientId;
284
299
  this.redirectUrl = redirectUrl;
285
300
  this.oauthServer = oauthServer;
301
+ this.inputEndpoints = inputEndpoints;
286
302
  this.codeVerifier = void 0;
287
- this.state = void 0;
288
- this.codeVerifier = generateCodeVerifier();
289
- this._endpoints = inputEndpoints;
303
+ this.refreshTokenTimeout = null;
304
+ this.codeVerifier = this.getCodeVerifier();
305
+ this.endpoints = inputEndpoints;
306
+ }
307
+ getCodeVerifier() {
308
+ return generateCodeVerifier();
290
309
  }
291
310
  getUserInfoService() {
292
311
  return __async(this, null, function* () {
293
- if (this._userInfoService) {
294
- return this._userInfoService;
312
+ if (this.userInfoService) {
313
+ return this.userInfoService;
295
314
  }
296
315
  const endpoints = yield this.getEndpoints();
297
- this._userInfoService = new UserInfoServiceImpl(endpoints);
298
- return this._userInfoService;
316
+ this.userInfoService = new UserInfoServiceImpl(endpoints);
317
+ return this.userInfoService;
299
318
  });
300
319
  }
301
320
  getEndpoints() {
302
321
  return __async(this, null, function* () {
303
- if (this._endpoints) {
304
- return this._endpoints;
322
+ var _a;
323
+ if ((_a = this.endpoints) == null ? void 0 : _a.auth) {
324
+ return this.endpoints;
305
325
  }
306
- this._endpoints = yield getOauthEndpoints(this.oauthServer);
307
- return this._endpoints;
326
+ const jwksEndpoints = yield getOauthEndpoints(this.oauthServer);
327
+ return this.endpoints ? __spreadValues(__spreadValues({}, this.endpoints), jwksEndpoints) : jwksEndpoints;
308
328
  });
309
329
  }
310
330
  getOauth2Client() {
311
331
  return __async(this, null, function* () {
312
- if (this._oauth2Client) {
313
- return this._oauth2Client;
332
+ if (this.oauth2Client) {
333
+ return this.oauth2Client;
314
334
  }
315
335
  const endpoints = yield this.getEndpoints();
316
- this._oauth2Client = new OAuth2Client(
336
+ this.oauth2Client = new OAuth2Client(
317
337
  this.clientId,
318
338
  endpoints.auth,
319
339
  endpoints.token,
320
340
  // this
321
341
  { redirectURI: this.redirectUrl }
322
342
  );
323
- return this._oauth2Client;
343
+ return this.oauth2Client;
324
344
  });
325
345
  }
326
346
  getSessionData() {
@@ -334,25 +354,58 @@ var AuthSessionServiceImpl = class {
334
354
  JSON.stringify(__spreadValues({}, data))
335
355
  );
336
356
  }
337
- getAuthorizationUrl(scopes, displayMode, nonce) {
357
+ getUser() {
358
+ return JSON.parse(
359
+ localStorage.getItem(`civic-auth:${this.clientId}:user`) || "{}"
360
+ );
361
+ }
362
+ setUser(data) {
363
+ localStorage.setItem(
364
+ `civic-auth:${this.clientId}:user`,
365
+ JSON.stringify(data === null ? {} : data)
366
+ );
367
+ }
368
+ clearSessionData() {
369
+ localStorage.setItem(`civic-auth:${this.clientId}`, JSON.stringify({}));
370
+ }
371
+ getAuthorizationUrlWithChallenge(state, scopes) {
338
372
  return __async(this, null, function* () {
339
- const state = generateState(displayMode);
340
- this.state = state;
341
- const existingSessionData = this.getSessionData();
342
- this.updateSessionData(__spreadProps(__spreadValues({}, existingSessionData), {
343
- codeVerifier: this.codeVerifier,
344
- displayMode
345
- }));
373
+ var _a;
346
374
  const oauth2Client = yield this.getOauth2Client();
375
+ if ((_a = this.endpoints) == null ? void 0 : _a.challenge) {
376
+ const challenge = yield fetch(this.endpoints.challenge).then(
377
+ (res) => res.json().then((data) => data.challenge)
378
+ );
379
+ const oAuthUrl2 = yield oauth2Client.createAuthorizationURL({
380
+ state,
381
+ scopes
382
+ });
383
+ oAuthUrl2.searchParams.append("code_challenge", challenge);
384
+ oAuthUrl2.searchParams.append("code_challenge_method", "S256");
385
+ return oAuthUrl2;
386
+ }
347
387
  const oAuthUrl = yield oauth2Client.createAuthorizationURL({
348
388
  state,
349
389
  codeVerifier: this.codeVerifier,
350
390
  codeChallengeMethod: "S256",
351
391
  scopes
352
392
  });
393
+ return oAuthUrl;
394
+ });
395
+ }
396
+ getAuthorizationUrl(scopes, displayMode, nonce) {
397
+ return __async(this, null, function* () {
398
+ const state = generateState(displayMode);
399
+ const existingSessionData = this.getSessionData();
400
+ this.updateSessionData(__spreadProps(__spreadValues({}, existingSessionData), {
401
+ codeVerifier: this.codeVerifier,
402
+ displayMode
403
+ }));
404
+ const oAuthUrl = yield this.getAuthorizationUrlWithChallenge(state, scopes);
353
405
  if (nonce) {
354
406
  oAuthUrl.searchParams.append("nonce", nonce);
355
407
  }
408
+ oAuthUrl.searchParams.append("prompt", "consent");
356
409
  return oAuthUrl.toString();
357
410
  });
358
411
  }
@@ -402,7 +455,7 @@ var AuthSessionServiceImpl = class {
402
455
  if (!authorizationCode || !returnedState) {
403
456
  throw new Error("Invalid authorization response");
404
457
  }
405
- const codeVerifier = this.getSessionData().codeVerifier;
458
+ const codeVerifier = session.codeVerifier;
406
459
  const oauth2Client = yield this.getOauth2Client();
407
460
  const tokens = yield oauth2Client.validateAuthorizationCode(
408
461
  authorizationCode,
@@ -429,10 +482,14 @@ var AuthSessionServiceImpl = class {
429
482
  state: returnedState,
430
483
  accessToken: tokens.access_token,
431
484
  refreshToken: tokens.refresh_token,
485
+ timestamp: Date.now(),
432
486
  expiresIn: tokens.expires_in
433
487
  });
434
488
  this.updateSessionData(session);
489
+ const user = yield (yield this.getUserInfoService()).getUserInfo(tokens.access_token, tokens.id_token || null);
490
+ this.setUser(user);
435
491
  }
492
+ this.setupTokenRefresh(session);
436
493
  if (session.displayMode === "new_tab") {
437
494
  window.close();
438
495
  } else if (session.displayMode === "redirect") {
@@ -440,6 +497,24 @@ var AuthSessionServiceImpl = class {
440
497
  return session;
441
498
  });
442
499
  }
500
+ setupTokenRefresh(session) {
501
+ if (this.refreshTokenTimeout) {
502
+ clearTimeout(this.refreshTokenTimeout);
503
+ }
504
+ if (session.expiresIn) {
505
+ const elapsedTime = Date.now() - (session.timestamp || 0);
506
+ const remainingTime = session.expiresIn * 1e3 - elapsedTime;
507
+ const refreshTime = Math.max(0, remainingTime - 6e4);
508
+ this.refreshTokenTimeout = setTimeout(() => {
509
+ this.refreshToken().then((newSession) => {
510
+ console.log("Token refreshed successfully", newSession);
511
+ }).catch((error) => {
512
+ console.error("Failed to refresh token:", error);
513
+ this.updateSessionData({});
514
+ });
515
+ }, refreshTime);
516
+ }
517
+ }
443
518
  refreshToken() {
444
519
  return __async(this, null, function* () {
445
520
  const sessionData = this.getSessionData();
@@ -455,9 +530,11 @@ var AuthSessionServiceImpl = class {
455
530
  authenticated: true,
456
531
  accessToken: tokens.access_token,
457
532
  refreshToken: tokens.refresh_token,
533
+ timestamp: Date.now(),
458
534
  expiresIn: tokens.expires_in
459
535
  });
460
536
  this.updateSessionData(session);
537
+ this.setupTokenRefresh(session);
461
538
  return session;
462
539
  });
463
540
  }
@@ -468,7 +545,10 @@ var AuthSessionServiceImpl = class {
468
545
  throw new Error("No access token available");
469
546
  }
470
547
  const userInfoService = yield this.getUserInfoService();
471
- return userInfoService.getUserInfo(sessionData.accessToken);
548
+ return userInfoService.getUserInfo(
549
+ sessionData.accessToken,
550
+ sessionData.idToken || null
551
+ );
472
552
  });
473
553
  }
474
554
  /**
@@ -497,10 +577,7 @@ var AuthSessionServiceImpl = class {
497
577
  );
498
578
  returnPayload.accessToken = accessTokenResponse.payload;
499
579
  if (tokens.refresh_token) {
500
- const refreshResponse = yield jose.jwtVerify(tokens.refresh_token, JWKS, {
501
- issuer: getIssuerVariations(this.oauthServer)
502
- });
503
- returnPayload.refreshToken = refreshResponse.payload;
580
+ returnPayload.refreshToken = tokens.refresh_token;
504
581
  }
505
582
  return returnPayload;
506
583
  });
@@ -523,7 +600,9 @@ var AuthSessionServiceImpl = class {
523
600
  return sessionData;
524
601
  } catch (error) {
525
602
  console.warn("Failed to validate existing tokens", error);
526
- const unAuthenticatedSession = __spreadProps(__spreadValues({}, sessionData), { authenticated: false });
603
+ const unAuthenticatedSession = {
604
+ authenticated: false
605
+ };
527
606
  this.updateSessionData(unAuthenticatedSession);
528
607
  return unAuthenticatedSession;
529
608
  }
@@ -531,11 +610,40 @@ var AuthSessionServiceImpl = class {
531
610
  }
532
611
  };
533
612
 
613
+ // src/constants.ts
614
+ var DEFAULT_SCOPES = [
615
+ "openid",
616
+ "profile",
617
+ "email",
618
+ "forwardedTokens",
619
+ "offline_access"
620
+ ];
621
+ var IFRAME_ID = "civic-auth-iframe";
622
+
534
623
  // src/react/components/CivicAuthIframe.tsx
624
+ import { forwardRef } from "react";
625
+ import { jsx as jsx3 } from "react/jsx-runtime";
626
+ var CivicAuthIframe = forwardRef(
627
+ ({ authUrl, onLoad }, ref) => {
628
+ return /* @__PURE__ */ jsx3(
629
+ "iframe",
630
+ {
631
+ id: IFRAME_ID,
632
+ ref,
633
+ src: authUrl,
634
+ className: "h-96 w-80 border-none",
635
+ onLoad
636
+ }
637
+ );
638
+ }
639
+ );
640
+ CivicAuthIframe.displayName = "CivicAuthIframe";
641
+
642
+ // src/react/components/CivicAuthIframeModal.tsx
535
643
  import { useCallback, useEffect, useRef, useState } from "react";
536
644
 
537
645
  // src/react/components/LoadingIcon.tsx
538
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
646
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
539
647
  var LoadingIcon = () => /* @__PURE__ */ jsxs("div", { role: "status", children: [
540
648
  /* @__PURE__ */ jsxs(
541
649
  "svg",
@@ -546,14 +654,14 @@ var LoadingIcon = () => /* @__PURE__ */ jsxs("div", { role: "status", children:
546
654
  fill: "none",
547
655
  xmlns: "http://www.w3.org/2000/svg",
548
656
  children: [
549
- /* @__PURE__ */ jsx3(
657
+ /* @__PURE__ */ jsx4(
550
658
  "path",
551
659
  {
552
660
  d: "M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z",
553
661
  fill: "currentColor"
554
662
  }
555
663
  ),
556
- /* @__PURE__ */ jsx3(
664
+ /* @__PURE__ */ jsx4(
557
665
  "path",
558
666
  {
559
667
  d: "M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z",
@@ -563,11 +671,11 @@ var LoadingIcon = () => /* @__PURE__ */ jsxs("div", { role: "status", children:
563
671
  ]
564
672
  }
565
673
  ),
566
- /* @__PURE__ */ jsx3("span", { className: "sr-only", children: "Loading..." })
674
+ /* @__PURE__ */ jsx4("span", { className: "sr-only", children: "Loading..." })
567
675
  ] });
568
676
 
569
- // src/react/components/CivicAuthIframe.tsx
570
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
677
+ // src/react/components/CloseIcon.tsx
678
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
571
679
  var CloseIcon = () => /* @__PURE__ */ jsxs2(
572
680
  "svg",
573
681
  {
@@ -582,19 +690,23 @@ var CloseIcon = () => /* @__PURE__ */ jsxs2(
582
690
  strokeLinejoin: "round",
583
691
  className: "lucide lucide-x",
584
692
  children: [
585
- /* @__PURE__ */ jsx4("path", { d: "M18 6 6 18" }),
586
- /* @__PURE__ */ jsx4("path", { d: "m6 6 12 12" })
693
+ /* @__PURE__ */ jsx5("path", { d: "M18 6 6 18" }),
694
+ /* @__PURE__ */ jsx5("path", { d: "m6 6 12 12" })
587
695
  ]
588
696
  }
589
697
  );
590
- var IFRAME_ID = "civic-auth-iframe";
591
- var CivicAuthIframe = ({
698
+
699
+ // src/react/components/CivicAuthIframeModal.tsx
700
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
701
+ var CivicAuthIframeModal = ({
592
702
  authUrl,
593
703
  redirectUri,
594
704
  setAuthResponseUrl,
595
- onClose
705
+ onClose,
706
+ iframeRef,
707
+ redirectInProgress = false,
708
+ closeOnRedirect = true
596
709
  }) => {
597
- const iframeRef = useRef(null);
598
710
  const [isLoading, setIsLoading] = useState(true);
599
711
  const processIframeUrl = useCallback(() => {
600
712
  if (iframeRef.current && iframeRef.current.contentWindow) {
@@ -602,6 +714,7 @@ var CivicAuthIframe = ({
602
714
  const iframeUrl = iframeRef.current.contentWindow.location.href;
603
715
  if (iframeUrl.startsWith(redirectUri)) {
604
716
  setAuthResponseUrl(iframeUrl);
717
+ if (closeOnRedirect) onClose();
605
718
  return true;
606
719
  }
607
720
  } catch (e) {
@@ -609,14 +722,17 @@ var CivicAuthIframe = ({
609
722
  }
610
723
  }
611
724
  return false;
612
- }, [redirectUri, setAuthResponseUrl]);
725
+ }, [closeOnRedirect, iframeRef, onClose, redirectUri, setAuthResponseUrl]);
613
726
  const intervalId = useRef();
614
- useEffect(() => {
615
- const handleEscape = (event) => {
727
+ const handleEscape = useCallback(
728
+ (event) => {
616
729
  if (event.key === "Escape") {
617
730
  onClose();
618
731
  }
619
- };
732
+ },
733
+ [onClose]
734
+ );
735
+ useEffect(() => {
620
736
  window.addEventListener("keydown", handleEscape);
621
737
  return () => window.removeEventListener("keydown", handleEscape);
622
738
  });
@@ -627,33 +743,31 @@ var CivicAuthIframe = ({
627
743
  clearInterval(intervalId.current);
628
744
  }
629
745
  };
630
- return /* @__PURE__ */ jsx4(
746
+ return /* @__PURE__ */ jsx6(
631
747
  "div",
632
748
  {
633
- className: "absolute left-0 top-0 z-[9999] flex h-screen w-screen items-center justify-center bg-neutral-950 bg-opacity-50",
749
+ className: "absolute left-0 top-0 z-50 flex h-screen w-screen items-center justify-center bg-neutral-950 bg-opacity-50",
634
750
  onClick: onClose,
635
- children: /* @__PURE__ */ jsxs2(
751
+ children: /* @__PURE__ */ jsxs3(
636
752
  "div",
637
753
  {
638
754
  className: "relative rounded-3xl bg-white p-6 shadow-lg",
639
755
  onClick: (e) => e.stopPropagation(),
640
756
  children: [
641
- /* @__PURE__ */ jsx4(
757
+ /* @__PURE__ */ jsx6(
642
758
  "button",
643
759
  {
644
760
  className: "absolute right-4 top-4 flex cursor-pointer items-center justify-center border-none bg-transparent p-1 text-neutral-400",
645
761
  onClick: onClose,
646
- children: /* @__PURE__ */ jsx4(CloseIcon, {})
762
+ children: /* @__PURE__ */ jsx6(CloseIcon, {})
647
763
  }
648
764
  ),
649
- isLoading && /* @__PURE__ */ jsx4("div", { className: "absolute inset-0 flex items-center justify-center rounded-3xl bg-neutral-100", children: /* @__PURE__ */ jsx4(LoadingIcon, {}) }),
650
- /* @__PURE__ */ jsx4(
651
- "iframe",
765
+ (isLoading || redirectInProgress) && /* @__PURE__ */ jsx6("div", { className: "absolute inset-0 flex items-center justify-center rounded-3xl bg-neutral-100", children: /* @__PURE__ */ jsx6(LoadingIcon, {}) }),
766
+ /* @__PURE__ */ jsx6(
767
+ CivicAuthIframe,
652
768
  {
653
- id: IFRAME_ID,
654
769
  ref: iframeRef,
655
- src: authUrl,
656
- className: "h-48 w-80 border-none",
770
+ authUrl,
657
771
  onLoad: handleIframeLoad
658
772
  }
659
773
  )
@@ -665,9 +779,9 @@ var CivicAuthIframe = ({
665
779
  };
666
780
 
667
781
  // src/react/components/UserButton.tsx
668
- import { useEffect as useEffect2, useState as useState2 } from "react";
669
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
670
- var ChevronDown = () => /* @__PURE__ */ jsx5(
782
+ import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
783
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
784
+ var ChevronDown = () => /* @__PURE__ */ jsx7(
671
785
  "svg",
672
786
  {
673
787
  xmlns: "http://www.w3.org/2000/svg",
@@ -680,10 +794,10 @@ var ChevronDown = () => /* @__PURE__ */ jsx5(
680
794
  strokeLinecap: "round",
681
795
  strokeLinejoin: "round",
682
796
  className: "lucide lucide-chevron-down",
683
- children: /* @__PURE__ */ jsx5("path", { d: "m6 9 6 6 6-6" })
797
+ children: /* @__PURE__ */ jsx7("path", { d: "m6 9 6 6 6-6" })
684
798
  }
685
799
  );
686
- var ChevronUp = () => /* @__PURE__ */ jsx5(
800
+ var ChevronUp = () => /* @__PURE__ */ jsx7(
687
801
  "svg",
688
802
  {
689
803
  xmlns: "http://www.w3.org/2000/svg",
@@ -696,7 +810,7 @@ var ChevronUp = () => /* @__PURE__ */ jsx5(
696
810
  strokeLinecap: "round",
697
811
  strokeLinejoin: "round",
698
812
  className: "lucide lucide-chevron-up",
699
- children: /* @__PURE__ */ jsx5("path", { d: "m18 15-6-6-6 6" })
813
+ children: /* @__PURE__ */ jsx7("path", { d: "m18 15-6-6-6 6" })
700
814
  }
701
815
  );
702
816
  var UserButton = ({
@@ -706,41 +820,38 @@ var UserButton = ({
706
820
  const [isOpen, setIsOpen] = useState2(false);
707
821
  const { signIn, isAuthenticated, signOut } = useAuth();
708
822
  const { user } = useUser();
823
+ const handleClickOutside = useCallback2((event) => {
824
+ const target = event.target;
825
+ if (!target.closest("#civic-dropdown-container")) {
826
+ setIsOpen(false);
827
+ }
828
+ }, []);
829
+ const handleSignOut = useCallback2(() => __async(void 0, null, function* () {
830
+ yield signOut();
831
+ setIsOpen(false);
832
+ }), [signOut]);
833
+ const handleSignIn = useCallback2(() => __async(void 0, null, function* () {
834
+ yield signIn(displayMode);
835
+ setIsOpen(false);
836
+ }), [signIn, displayMode]);
837
+ const handleEscape = useCallback2((event) => {
838
+ if (event.key === "Escape") {
839
+ setIsOpen(false);
840
+ }
841
+ }, []);
709
842
  useEffect2(() => {
710
- const handleEscape = (event) => {
711
- if (event.key === "Escape") {
712
- setIsOpen(false);
713
- }
714
- };
715
843
  if (isOpen) {
844
+ window.addEventListener("click", handleClickOutside);
716
845
  window.addEventListener("keydown", handleEscape);
717
846
  }
718
847
  return () => {
848
+ window.removeEventListener("click", handleClickOutside);
719
849
  window.removeEventListener("keydown", handleEscape);
720
850
  };
721
- }, [isOpen]);
722
- useEffect2(() => {
723
- const handleClick = (event) => {
724
- const target = event.target;
725
- if (!target.closest("#civic-dropdown-container")) {
726
- setIsOpen(false);
727
- }
728
- };
729
- if (isOpen) {
730
- window.addEventListener("click", handleClick);
731
- }
732
- return () => {
733
- window.removeEventListener("click", handleClick);
734
- };
735
- }, [isOpen]);
736
- useEffect2(() => {
737
- if (!isAuthenticated) {
738
- setIsOpen(false);
739
- }
740
- }, [isAuthenticated]);
851
+ }, [handleClickOutside, handleEscape, isOpen]);
741
852
  if (isAuthenticated) {
742
- return /* @__PURE__ */ jsxs3("div", { className: "relative", id: "civic-dropdown-container", children: [
743
- /* @__PURE__ */ jsxs3(
853
+ return /* @__PURE__ */ jsxs4("div", { className: "relative", id: "civic-dropdown-container", children: [
854
+ /* @__PURE__ */ jsxs4(
744
855
  "button",
745
856
  {
746
857
  className: cn(
@@ -749,28 +860,28 @@ var UserButton = ({
749
860
  ),
750
861
  onClick: () => setIsOpen((isOpen2) => !isOpen2),
751
862
  children: [
752
- (user == null ? void 0 : user.picture) ? /* @__PURE__ */ jsx5("span", { className: "relative flex h-10 w-10 shrink-0 gap-2 overflow-hidden rounded-full", children: /* @__PURE__ */ jsx5(
863
+ (user == null ? void 0 : user.picture) ? /* @__PURE__ */ jsx7("span", { className: "relative flex h-10 w-10 shrink-0 gap-2 overflow-hidden rounded-full", children: /* @__PURE__ */ jsx7(
753
864
  "img",
754
865
  {
755
866
  className: "h-full w-full object-cover",
756
867
  src: user.picture,
757
868
  alt: (user == null ? void 0 : user.name) || (user == null ? void 0 : user.email)
758
869
  }
759
- ) }) : /* @__PURE__ */ jsx5("div", {}),
760
- /* @__PURE__ */ jsx5("span", { children: (user == null ? void 0 : user.name) || (user == null ? void 0 : user.email) }),
761
- isOpen ? /* @__PURE__ */ jsx5(ChevronUp, {}) : /* @__PURE__ */ jsx5(ChevronDown, {})
870
+ ) }) : /* @__PURE__ */ jsx7("div", {}),
871
+ /* @__PURE__ */ jsx7("span", { children: (user == null ? void 0 : user.name) || (user == null ? void 0 : user.email) }),
872
+ isOpen ? /* @__PURE__ */ jsx7(ChevronUp, {}) : /* @__PURE__ */ jsx7(ChevronDown, {})
762
873
  ]
763
874
  }
764
875
  ),
765
- /* @__PURE__ */ jsx5(
876
+ /* @__PURE__ */ jsx7(
766
877
  "div",
767
878
  {
768
879
  className: isOpen ? "absolute right-0 mt-2 w-full rounded-lg bg-white py-2 text-neutral-500 shadow-xl" : "hidden",
769
- children: /* @__PURE__ */ jsx5("ul", { children: /* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsx5(
880
+ children: /* @__PURE__ */ jsx7("ul", { children: /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(
770
881
  "button",
771
882
  {
772
883
  className: "block w-full px-4 py-2 transition-colors hover:bg-neutral-200 hover:bg-opacity-50",
773
- onClick: () => signOut(),
884
+ onClick: handleSignOut,
774
885
  children: "Logout"
775
886
  }
776
887
  ) }) })
@@ -778,53 +889,186 @@ var UserButton = ({
778
889
  )
779
890
  ] });
780
891
  }
781
- return /* @__PURE__ */ jsx5(
892
+ return /* @__PURE__ */ jsx7(
782
893
  "button",
783
894
  {
784
895
  className: cn(
785
896
  "rounded-full border border-neutral-500 px-3 py-2 transition-colors hover:bg-neutral-200 hover:bg-opacity-50",
786
897
  className
787
898
  ),
788
- onClick: () => signIn(displayMode),
899
+ onClick: handleSignIn,
789
900
  children: "Sign in"
790
901
  }
791
902
  );
792
903
  };
793
904
 
794
- // src/react/providers/ParamsProvider.tsx
795
- import { createContext as createContext3 } from "react";
796
- import { jsx as jsx6 } from "react/jsx-runtime";
797
- var ParamsContext = createContext3(null);
798
- var ParamsProvider = ({
799
- children,
800
- clientId,
801
- redirectUrl,
802
- config,
803
- nonce
905
+ // src/react/components/SignInButton.tsx
906
+ import { jsx as jsx8 } from "react/jsx-runtime";
907
+ var SignInButton = ({
908
+ displayMode,
909
+ className
804
910
  }) => {
805
- const value = {
806
- clientId,
807
- redirectUrl,
808
- config,
809
- nonce
810
- };
811
- return /* @__PURE__ */ jsx6(ParamsContext.Provider, { value, children });
911
+ const { signIn } = useAuth();
912
+ return /* @__PURE__ */ jsx8(
913
+ "button",
914
+ {
915
+ className: cn(
916
+ "rounded-full border border-neutral-500 px-3 py-2 transition-colors hover:bg-neutral-200 hover:bg-opacity-50",
917
+ className
918
+ ),
919
+ onClick: () => signIn(displayMode),
920
+ children: "Sign In"
921
+ }
922
+ );
923
+ };
924
+
925
+ // src/react/components/SignOutButton.tsx
926
+ import { jsx as jsx9 } from "react/jsx-runtime";
927
+ var SignOutButton = ({ className }) => {
928
+ const { signOut } = useAuth();
929
+ return /* @__PURE__ */ jsx9(
930
+ "button",
931
+ {
932
+ className: cn(
933
+ "rounded-full border border-neutral-500 px-3 py-2 transition-colors hover:bg-neutral-200 hover:bg-opacity-50",
934
+ className
935
+ ),
936
+ onClick: () => signOut(),
937
+ children: "Sign Out"
938
+ }
939
+ );
940
+ };
941
+
942
+ // src/lib/logger.ts
943
+ import debug from "debug";
944
+ var PACKAGE_NAME = "@civic/auth";
945
+ var DebugLogger = class {
946
+ constructor(namespace) {
947
+ this.debugLogger = debug(`${PACKAGE_NAME}:${namespace}:debug`);
948
+ this.infoLogger = debug(`${PACKAGE_NAME}:${namespace}:info`);
949
+ this.warnLogger = debug(`${PACKAGE_NAME}:${namespace}:warn`);
950
+ this.errorLogger = debug(`${PACKAGE_NAME}:${namespace}:error`);
951
+ this.debugLogger.color = "4";
952
+ this.infoLogger.color = "2";
953
+ this.warnLogger.color = "3";
954
+ this.errorLogger.color = "1";
955
+ }
956
+ debug(message, ...args) {
957
+ this.debugLogger(message, ...args);
958
+ }
959
+ info(message, ...args) {
960
+ this.infoLogger(message, ...args);
961
+ }
962
+ warn(message, ...args) {
963
+ this.warnLogger(message, ...args);
964
+ }
965
+ error(message, ...args) {
966
+ this.errorLogger(message, ...args);
967
+ }
968
+ };
969
+ var createLogger = (namespace) => new DebugLogger(namespace);
970
+ var loggers = {
971
+ // Next.js specific loggers
972
+ nextjs: {
973
+ routes: createLogger("api:routes"),
974
+ middleware: createLogger("api:middleware"),
975
+ handlers: {
976
+ auth: createLogger("api:handlers:auth")
977
+ }
978
+ },
979
+ // React specific loggers
980
+ react: {
981
+ components: createLogger("react:components"),
982
+ hooks: createLogger("react:hooks"),
983
+ context: createLogger("react:context")
984
+ },
985
+ // Shared utilities loggers
986
+ services: {
987
+ validation: createLogger("utils:validation"),
988
+ network: createLogger("utils:network")
989
+ }
990
+ };
991
+
992
+ // src/nextjs/config.ts
993
+ var logger = loggers.nextjs.handlers.auth;
994
+ var defaultAuthConfig = {
995
+ oauthServer: "https://auth-dev.civic.com/oauth",
996
+ callbackUrl: "/api/auth/callback",
997
+ challengeUrl: "/api/auth/challenge",
998
+ logoutUrl: "/api/auth/logout",
999
+ loginUrl: "/",
1000
+ include: ["/*"],
1001
+ exclude: [],
1002
+ cookies: {
1003
+ tokens: {
1004
+ sameSite: "strict",
1005
+ path: "/",
1006
+ maxAge: 60 * 60
1007
+ // 1 hour
1008
+ },
1009
+ user: {
1010
+ sameSite: "strict",
1011
+ path: "/",
1012
+ maxAge: 60 * 60
1013
+ // 1 hour
1014
+ }
1015
+ }
1016
+ };
1017
+ var withoutUndefined = (obj) => {
1018
+ const result = {};
1019
+ for (const key in obj) {
1020
+ if (obj[key] !== void 0) {
1021
+ result[key] = obj[key];
1022
+ }
1023
+ }
1024
+ return result;
1025
+ };
1026
+ var resolveAuthConfig = (config = {}) => {
1027
+ var _a, _b, _c, _d;
1028
+ const configFromEnv = withoutUndefined({
1029
+ clientId: process.env._civic_auth_client_id,
1030
+ oauthServer: process.env._civic_oauth_server,
1031
+ callbackUrl: process.env._civic_auth_callback_url,
1032
+ loginUrl: process.env._civic_auth_login_url,
1033
+ logoutUrl: process.env._civic_auth_logout_url,
1034
+ include: (_a = process.env._civic_auth_includes) == null ? void 0 : _a.split(","),
1035
+ exclude: (_b = process.env._civic_auth_excludes) == null ? void 0 : _b.split(","),
1036
+ cookies: process.env._civic_auth_cookie_config ? JSON.parse(process.env._civic_auth_cookie_config) : void 0
1037
+ });
1038
+ const mergedConfig = __spreadProps(__spreadValues(__spreadValues(__spreadValues({}, defaultAuthConfig), configFromEnv), config), {
1039
+ // Override with directly passed config
1040
+ cookies: {
1041
+ tokens: __spreadValues(__spreadValues({}, defaultAuthConfig.cookies.tokens), ((_c = config.cookies) == null ? void 0 : _c.tokens) || {}),
1042
+ user: __spreadValues(__spreadValues({}, defaultAuthConfig.cookies.user), ((_d = config.cookies) == null ? void 0 : _d.user) || {})
1043
+ }
1044
+ });
1045
+ logger.debug("Config from environment:", configFromEnv);
1046
+ logger.debug("Resolved config:", mergedConfig);
1047
+ if (mergedConfig.clientId === void 0) {
1048
+ throw new Error("Civic Auth client ID is required");
1049
+ }
1050
+ return mergedConfig;
1051
+ };
1052
+
1053
+ // src/react/components/NextLogOut.tsx
1054
+ import { jsx as jsx10 } from "react/jsx-runtime";
1055
+ var NextLogOut = ({ children }) => {
1056
+ const config = resolveAuthConfig();
1057
+ const logoutUrl = `${config.logoutUrl}`;
1058
+ return /* @__PURE__ */ jsx10("a", { href: logoutUrl, children });
812
1059
  };
813
1060
 
814
1061
  // src/react/providers/SessionProvider.tsx
815
- import { createContext as createContext4 } from "react";
816
- import { jsx as jsx7 } from "react/jsx-runtime";
1062
+ import { createContext as createContext3 } from "react";
1063
+ import { jsx as jsx11 } from "react/jsx-runtime";
817
1064
  var defaultSession = {
818
1065
  authenticated: false,
819
1066
  idToken: void 0,
820
1067
  accessToken: void 0,
821
1068
  displayMode: "iframe"
822
1069
  };
823
- var SessionContext = createContext4(defaultSession);
824
- var SessionProvider = ({ children, session }) => /* @__PURE__ */ jsx7(SessionContext.Provider, { value: session || defaultSession, children });
825
-
826
- // src/constants.ts
827
- var DEFAULT_SCOPES = ["openid", "profile", "email", "forwardedTokens"];
1070
+ var SessionContext = createContext3(defaultSession);
1071
+ var SessionProvider = ({ children, session }) => /* @__PURE__ */ jsx11(SessionContext.Provider, { value: session || defaultSession, children });
828
1072
 
829
1073
  // src/config.ts
830
1074
  var authConfig = {
@@ -832,9 +1076,24 @@ var authConfig = {
832
1076
  oauthServer: "https://auth-dev.civic.com/oauth/"
833
1077
  };
834
1078
 
835
- // src/react/providers/AuthProvider.tsx
836
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
837
- var AuthContext = createContext5(null);
1079
+ // src/lib/windowUtil.ts
1080
+ var isWindowInIframe = (window2) => {
1081
+ var _a;
1082
+ if (typeof window2 !== "undefined") {
1083
+ try {
1084
+ if (((_a = window2 == null ? void 0 : window2.frameElement) == null ? void 0 : _a.id) === "civic-auth-iframe") {
1085
+ return true;
1086
+ }
1087
+ } catch (_e) {
1088
+ return false;
1089
+ }
1090
+ }
1091
+ return false;
1092
+ };
1093
+
1094
+ // src/shared/AuthProvider.tsx
1095
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
1096
+ var AuthContext = createContext4(null);
838
1097
  var globalThisObject;
839
1098
  if (typeof window !== "undefined") {
840
1099
  globalThisObject = window;
@@ -851,28 +1110,21 @@ var AuthProvider = ({
851
1110
  config = authConfig,
852
1111
  nonce,
853
1112
  onSignIn,
854
- onSignOut
1113
+ onSignOut,
1114
+ authServiceImpl,
1115
+ serverSideTokenExchange
855
1116
  }) => {
856
1117
  const [iframeUrl, setIframeUrl] = useState3(null);
857
1118
  const [currentUrl, setCurrentUrl] = useState3(null);
858
1119
  const [isInIframe, setIsInIframe] = useState3(false);
859
1120
  const [authResponseUrl, setAuthResponseUrl] = useState3(null);
860
- const [tokenExchangeInProgress, setTokenExchangeInProgress] = useState3(false);
861
1121
  const [tokenExchangeError, setTokenExchangeError] = useState3();
862
- const queryClient2 = useQueryClient2();
1122
+ const queryClient3 = useQueryClient2();
1123
+ const iframeRef = useRef2(null);
863
1124
  useEffect3(() => {
864
- var _a, _b;
865
1125
  if (typeof globalThis.window !== "undefined") {
866
1126
  setCurrentUrl(globalThis.window.location.href);
867
- let isInIframeVal = false;
868
- try {
869
- if (((_b = (_a = globalThis.window) == null ? void 0 : _a.frameElement) == null ? void 0 : _b.id) === "civic-auth-iframe") {
870
- isInIframeVal = true;
871
- }
872
- } catch (_e) {
873
- isInIframeVal = false;
874
- }
875
- console.log("isInIframeVal", isInIframeVal);
1127
+ const isInIframeVal = isWindowInIframe(globalThis.window);
876
1128
  setIsInIframe(isInIframeVal);
877
1129
  }
878
1130
  }, []);
@@ -881,39 +1133,37 @@ var AuthProvider = ({
881
1133
  [currentUrl, inputRedirectUrl]
882
1134
  );
883
1135
  const authService = useMemo2(
884
- () => currentUrl ? new AuthSessionServiceImpl(
1136
+ () => currentUrl ? authServiceImpl || new AuthSessionServiceImpl(
885
1137
  clientId,
886
1138
  redirectUrl,
887
1139
  config == null ? void 0 : config.oauthServer,
888
1140
  config == null ? void 0 : config.endpoints
889
1141
  ) : null,
890
- [currentUrl, clientId, redirectUrl, config]
1142
+ [currentUrl, clientId, redirectUrl, config, authServiceImpl]
891
1143
  );
1144
+ const [userInfoService, setUserInfoService] = useState3();
1145
+ useEffect3(() => {
1146
+ if (!authService) return;
1147
+ authService.getUserInfoService().then(setUserInfoService);
1148
+ }, [authService]);
892
1149
  const {
893
1150
  data: session,
894
1151
  isLoading,
895
1152
  error
896
1153
  } = useQuery2({
897
- queryKey: ["session"],
1154
+ queryKey: ["session", authResponseUrl, iframeUrl, currentUrl, isInIframe],
898
1155
  queryFn: () => __async(void 0, null, function* () {
899
- const url = new URL(globalThis.window.location.href || "");
900
- console.log("AuthProvider useQuery", { isInIframe, url, authService });
901
1156
  if (!authService) {
902
1157
  return { authenticated: false };
903
1158
  }
904
- const existingSessionData = yield authService.validateExistingSession();
905
- if (existingSessionData.authenticated) {
906
- return existingSessionData;
907
- }
1159
+ const url = new URL(
1160
+ authResponseUrl ? authResponseUrl : globalThis.window.location.href || ""
1161
+ );
908
1162
  const code = url.searchParams.get("code");
909
- if (code && !isInIframe) {
1163
+ if (code && !isInIframe && !serverSideTokenExchange) {
910
1164
  try {
911
1165
  console.log("AuthProvider useQuery code", { isInIframe, code });
912
- setTokenExchangeInProgress(true);
913
- const newSession = yield authService.tokenExchange(
914
- globalThis.window.location.href
915
- );
916
- setTokenExchangeInProgress(false);
1166
+ const newSession = yield authService.tokenExchange(url.toString());
917
1167
  onSignIn == null ? void 0 : onSignIn();
918
1168
  return newSession;
919
1169
  } catch (error2) {
@@ -924,21 +1174,28 @@ var AuthProvider = ({
924
1174
  return { authenticated: false };
925
1175
  }
926
1176
  }
1177
+ const existingSessionData = yield authService.validateExistingSession();
1178
+ if (existingSessionData.authenticated) {
1179
+ return existingSessionData;
1180
+ }
927
1181
  return existingSessionData;
928
- }),
929
- enabled: !!authService && !!currentUrl && !isInIframe
1182
+ })
930
1183
  });
931
1184
  const signOutMutation = useMutation2({
932
1185
  mutationFn: () => __async(void 0, null, function* () {
933
1186
  authService == null ? void 0 : authService.updateSessionData({});
1187
+ setIframeUrl(null);
934
1188
  setAuthResponseUrl(null);
935
1189
  onSignOut == null ? void 0 : onSignOut();
936
1190
  }),
937
1191
  onSuccess: () => {
938
- queryClient2.setQueryData(["session"], null);
1192
+ queryClient3.setQueryData(
1193
+ ["session", authResponseUrl, iframeUrl, currentUrl, isInIframe],
1194
+ null
1195
+ );
939
1196
  }
940
1197
  });
941
- const signIn = useCallback2(
1198
+ const signIn = useCallback3(
942
1199
  (overrideDisplayMode = "iframe") => __async(void 0, null, function* () {
943
1200
  if (!authService) return;
944
1201
  const url = yield authService.getAuthorizationUrl(
@@ -959,118 +1216,151 @@ var AuthProvider = ({
959
1216
  () => session ? session.authenticated : false,
960
1217
  [session]
961
1218
  );
962
- useEffect3(() => {
963
- if (!authService || !authResponseUrl) return;
964
- const url = new URL(authResponseUrl);
965
- const code = url.searchParams.get("code");
966
- console.log("AuthProvider useEffect code", {
967
- isAuthenticated,
968
- code,
969
- tokenExchangeInProgress,
970
- isInIframe
971
- });
972
- if (code && !isAuthenticated && !tokenExchangeInProgress && !isInIframe) {
973
- try {
974
- setTokenExchangeInProgress(true);
975
- authService.tokenExchange(authResponseUrl).then((newSession) => {
976
- queryClient2.setQueryData(["session"], newSession);
977
- setIframeUrl(null);
978
- onSignIn == null ? void 0 : onSignIn();
979
- }).catch((error2) => {
980
- setTokenExchangeError(error2);
981
- }).finally(() => {
982
- setTokenExchangeInProgress(false);
983
- });
984
- } catch (error2) {
985
- setTokenExchangeInProgress(false);
986
- onSignIn == null ? void 0 : onSignIn(
987
- error2 instanceof Error ? error2 : new Error("Failed to sign in")
988
- );
989
- }
990
- }
991
- }, [
992
- authService,
993
- onSignIn,
994
- queryClient2,
995
- isAuthenticated,
996
- authResponseUrl,
997
- tokenExchangeInProgress,
998
- isInIframe
999
- ]);
1000
1219
  const value = useMemo2(
1001
1220
  () => ({
1002
1221
  isLoading,
1003
1222
  error,
1004
- signOut: signOutMutation.mutate,
1223
+ signOut: () => __async(void 0, null, function* () {
1224
+ yield signOutMutation.mutateAsync();
1225
+ }),
1005
1226
  isAuthenticated,
1006
1227
  signIn
1007
1228
  }),
1008
- [isLoading, error, signOutMutation.mutate, isAuthenticated, signIn]
1009
- );
1010
- return /* @__PURE__ */ jsx8(
1011
- AuthContext.Provider,
1012
- {
1013
- value: __spreadProps(__spreadValues({}, value), {
1014
- signOut: () => __async(void 0, null, function* () {
1015
- yield signOutMutation.mutateAsync();
1016
- })
1017
- }),
1018
- children: /* @__PURE__ */ jsx8(
1019
- ParamsProvider,
1020
- {
1021
- clientId,
1022
- redirectUrl,
1023
- config,
1024
- nonce,
1025
- children: /* @__PURE__ */ jsx8(SessionProvider, { session, children: /* @__PURE__ */ jsx8(TokenProvider, { children: /* @__PURE__ */ jsxs4(UserProvider, { children: [
1026
- !isInIframe && iframeUrl && !(session == null ? void 0 : session.authenticated) && /* @__PURE__ */ jsx8(
1027
- CivicAuthIframe,
1028
- {
1029
- authUrl: iframeUrl,
1030
- redirectUri: redirectUrl,
1031
- setAuthResponseUrl,
1032
- onClose: () => setIframeUrl(null)
1033
- }
1034
- ),
1035
- (tokenExchangeInProgress || tokenExchangeError || isLoading || isInIframe) && /* @__PURE__ */ jsxs4("div", { className: "absolute left-0 top-0 z-[9999] flex h-screen w-screen items-center justify-center bg-white", children: [
1036
- " ",
1037
- /* @__PURE__ */ jsx8("div", { className: "absolute inset-0 flex items-center justify-center bg-white", children: tokenExchangeError ? /* @__PURE__ */ jsxs4("div", { children: [
1038
- "Error: ",
1039
- tokenExchangeError.message
1040
- ] }) : /* @__PURE__ */ jsx8(LoadingIcon, {}) })
1041
- ] }),
1042
- children
1043
- ] }) }) })
1044
- }
1045
- )
1046
- }
1229
+ [isLoading, error, signOutMutation, isAuthenticated, signIn]
1047
1230
  );
1231
+ return /* @__PURE__ */ jsx12(AuthContext.Provider, { value, children: /* @__PURE__ */ jsx12(SessionProvider, { session, children: /* @__PURE__ */ jsx12(TokenProvider, { children: /* @__PURE__ */ jsxs5(UserProvider, { userInfoService, children: [
1232
+ !isInIframe && iframeUrl && !(session == null ? void 0 : session.authenticated) && /* @__PURE__ */ jsx12(
1233
+ CivicAuthIframeModal,
1234
+ {
1235
+ iframeRef,
1236
+ authUrl: iframeUrl,
1237
+ redirectUri: redirectUrl,
1238
+ setAuthResponseUrl,
1239
+ onClose: () => setIframeUrl(null)
1240
+ }
1241
+ ),
1242
+ (tokenExchangeError || isLoading || error || isInIframe && !(tokenExchangeError || error)) && /* @__PURE__ */ jsx12("div", { className: "absolute left-0 top-0 z-50 flex h-screen w-screen items-center justify-center bg-white", children: /* @__PURE__ */ jsx12("div", { className: "absolute inset-0 flex items-center justify-center bg-white", children: tokenExchangeError || error ? /* @__PURE__ */ jsxs5("div", { children: [
1243
+ "Error:",
1244
+ " ",
1245
+ (tokenExchangeError || error).message
1246
+ ] }) : /* @__PURE__ */ jsx12(LoadingIcon, {}) }) }),
1247
+ children
1248
+ ] }) }) }) });
1048
1249
  };
1049
1250
 
1050
- // src/react/providers/CivicProvider.tsx
1251
+ // src/shared/CivicAuthProvider.tsx
1051
1252
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
1052
- import { jsx as jsx9 } from "react/jsx-runtime";
1253
+ import "@civic/auth/styles.css";
1254
+ import { jsx as jsx13 } from "react/jsx-runtime";
1053
1255
  var queryClient = new QueryClient();
1054
- var CivicProvider = (_a) => {
1256
+ var CivicAuthProvider = (_a) => {
1055
1257
  var _b = _a, { children } = _b, props = __objRest(_b, ["children"]);
1056
- return /* @__PURE__ */ jsx9(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx9(AuthProvider, __spreadProps(__spreadValues({}, props), { children })) });
1258
+ return /* @__PURE__ */ jsx13(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx13(AuthProvider, __spreadProps(__spreadValues({}, props), { children })) });
1057
1259
  };
1058
1260
 
1059
- // src/react/hooks/useUser.tsx
1060
- var useUser = () => {
1061
- const context = useContext3(UserContext);
1062
- if (!context) {
1063
- throw new Error("useUser must be used within a UserProvider");
1261
+ // src/react/providers/NextAuthProvider.tsx
1262
+ import { createContext as createContext5, useContext as useContext3, useEffect as useEffect5, useMemo as useMemo3, useState as useState4 } from "react";
1263
+
1264
+ // src/react/hooks/useUserCookie.ts
1265
+ import { useEffect as useEffect4, useRef as useRef3 } from "react";
1266
+ import { useRouter } from "next/navigation.js";
1267
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
1268
+
1269
+ // src/lib/cookies.ts
1270
+ var getCookieValue = (key, window2) => {
1271
+ const cookie = window2.document.cookie;
1272
+ if (!cookie) return null;
1273
+ const cookies = cookie.split(";");
1274
+ for (const c of cookies) {
1275
+ const [name, value] = c.trim().split("=");
1276
+ if (name === key) {
1277
+ try {
1278
+ return JSON.parse(decodeURIComponent(value));
1279
+ } catch (e) {
1280
+ console.log("Error parsing cookie value", e);
1281
+ return value;
1282
+ }
1283
+ }
1064
1284
  }
1065
- return context;
1285
+ return null;
1066
1286
  };
1067
1287
 
1068
- // src/react/hooks/useParams.tsx
1069
- import { useContext as useContext4 } from "react";
1070
- var useParams = () => {
1071
- const context = useContext4(ParamsContext);
1288
+ // src/react/hooks/useUserCookie.ts
1289
+ var getUserFromCookie = () => {
1290
+ const userCookie = getCookieValue("user", globalThis.window);
1291
+ return userCookie;
1292
+ };
1293
+ var useUserCookie = () => {
1294
+ const hasRunRef = useRef3(false);
1295
+ const router = useRouter();
1296
+ const { data: user } = useQuery3({
1297
+ queryKey: ["user"],
1298
+ queryFn: () => getUserFromCookie(),
1299
+ refetchInterval: 2e3,
1300
+ refetchIntervalInBackground: true,
1301
+ enabled: !hasRunRef.current,
1302
+ refetchOnWindowFocus: true
1303
+ });
1304
+ useEffect4(() => {
1305
+ if (user) {
1306
+ if (!hasRunRef.current) {
1307
+ hasRunRef.current = true;
1308
+ router.refresh();
1309
+ }
1310
+ } else {
1311
+ hasRunRef.current = false;
1312
+ }
1313
+ }, [user, router]);
1314
+ return user;
1315
+ };
1316
+
1317
+ // src/react/providers/NextAuthProvider.tsx
1318
+ import { QueryClient as QueryClient2, QueryClientProvider as QueryClientProvider2 } from "@tanstack/react-query";
1319
+ import { jsx as jsx14 } from "react/jsx-runtime";
1320
+ var queryClient2 = new QueryClient2();
1321
+ var defaultUserContext = { user: null };
1322
+ var UserContext2 = createContext5(defaultUserContext);
1323
+ var CivicNextAuthProvider = (_a) => {
1324
+ var _b = _a, {
1325
+ children
1326
+ } = _b, props = __objRest(_b, [
1327
+ "children"
1328
+ ]);
1329
+ const user = useUserCookie();
1330
+ const [redirectUrl, setRedirectUrl] = useState4("");
1331
+ const { clientId, oauthServer, callbackUrl, challengeUrl } = resolveAuthConfig();
1332
+ useEffect5(() => {
1333
+ if (typeof globalThis.window !== "undefined") {
1334
+ const currentUrl = globalThis.window.location.href;
1335
+ setRedirectUrl(new URL(callbackUrl, currentUrl).toString());
1336
+ }
1337
+ }, [callbackUrl]);
1338
+ const authService = useMemo3(() => {
1339
+ if (redirectUrl && clientId && oauthServer) {
1340
+ return new AuthSessionServiceImpl(clientId, redirectUrl, oauthServer, {
1341
+ challenge: challengeUrl
1342
+ });
1343
+ }
1344
+ return void 0;
1345
+ }, [redirectUrl, clientId, oauthServer, challengeUrl]);
1346
+ return /* @__PURE__ */ jsx14(QueryClientProvider2, { client: queryClient2, children: /* @__PURE__ */ jsx14(
1347
+ AuthProvider,
1348
+ __spreadProps(__spreadValues({}, props), {
1349
+ config: { oauthServer },
1350
+ clientId,
1351
+ authServiceImpl: authService,
1352
+ serverSideTokenExchange: true,
1353
+ children: /* @__PURE__ */ jsx14(UserContext2.Provider, { value: user, children })
1354
+ })
1355
+ ) });
1356
+ };
1357
+ var useNextUser = () => useContext3(UserContext2);
1358
+
1359
+ // src/react/hooks/useUser.tsx
1360
+ var useUser = () => {
1361
+ const context = useContext4(UserContext);
1072
1362
  if (!context) {
1073
- throw new Error("useParams must be used within an ParamsProvider");
1363
+ throw new Error("useUser must be used within a UserProvider");
1074
1364
  }
1075
1365
  return context;
1076
1366
  };
@@ -1085,12 +1375,17 @@ var useSession = () => {
1085
1375
  return context;
1086
1376
  };
1087
1377
  export {
1088
- CivicProvider,
1378
+ CivicAuthProvider,
1379
+ CivicNextAuthProvider,
1380
+ NextLogOut,
1381
+ SignInButton,
1382
+ SignOutButton,
1089
1383
  UserButton,
1090
1384
  useAuth,
1091
- useParams,
1385
+ useNextUser,
1092
1386
  useSession,
1093
1387
  useToken,
1094
- useUser
1388
+ useUser,
1389
+ useUserCookie
1095
1390
  };
1096
1391
  //# sourceMappingURL=react.mjs.map