@civic/auth 0.0.1-beta.1 → 0.0.1-beta.10

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 (45) hide show
  1. package/README.md +26 -0
  2. package/dist/chunk-CRTRMMJ7.js +59 -0
  3. package/dist/chunk-CRTRMMJ7.js.map +1 -0
  4. package/dist/chunk-EAANLFR5.mjs +148 -0
  5. package/dist/chunk-EAANLFR5.mjs.map +1 -0
  6. package/dist/chunk-EGFTMH5S.mjs +214 -0
  7. package/dist/chunk-EGFTMH5S.mjs.map +1 -0
  8. package/dist/chunk-KCSGIIPA.js +214 -0
  9. package/dist/chunk-KCSGIIPA.js.map +1 -0
  10. package/dist/chunk-MVO4UZ2A.js +148 -0
  11. package/dist/chunk-MVO4UZ2A.js.map +1 -0
  12. package/dist/chunk-PMDIR5XE.mjs +502 -0
  13. package/dist/chunk-PMDIR5XE.mjs.map +1 -0
  14. package/dist/chunk-RGHW4PYM.mjs +59 -0
  15. package/dist/chunk-RGHW4PYM.mjs.map +1 -0
  16. package/dist/chunk-YNLXRD5L.js +502 -0
  17. package/dist/chunk-YNLXRD5L.js.map +1 -0
  18. package/dist/{index-DFVNodC9.d.mts → index-Bfi0hVMZ.d.mts} +5 -13
  19. package/dist/{index-DFVNodC9.d.ts → index-Bfi0hVMZ.d.ts} +5 -13
  20. package/dist/index.css +63 -63
  21. package/dist/index.css.map +1 -1
  22. package/dist/index.d.mts +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +1 -19
  25. package/dist/index.js.map +1 -1
  26. package/dist/index.mjs +1 -1
  27. package/dist/nextjs.d.mts +22 -37
  28. package/dist/nextjs.d.ts +22 -37
  29. package/dist/nextjs.js +166 -848
  30. package/dist/nextjs.js.map +1 -1
  31. package/dist/nextjs.mjs +162 -805
  32. package/dist/nextjs.mjs.map +1 -1
  33. package/dist/react.d.mts +42 -58
  34. package/dist/react.d.ts +42 -58
  35. package/dist/react.js +668 -1103
  36. package/dist/react.js.map +1 -1
  37. package/dist/react.mjs +608 -1005
  38. package/dist/react.mjs.map +1 -1
  39. package/dist/server.d.mts +56 -0
  40. package/dist/server.d.ts +56 -0
  41. package/dist/server.js +20 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/server.mjs +20 -0
  44. package/dist/server.mjs.map +1 -0
  45. package/package.json +28 -18
package/dist/react.mjs CHANGED
@@ -1,65 +1,43 @@
1
- 'use client'
2
- var __defProp = Object.defineProperty;
3
- var __defProps = Object.defineProperties;
4
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
- var __spreadValues = (a, b) => {
10
- for (var prop in b || (b = {}))
11
- if (__hasOwnProp.call(b, prop))
12
- __defNormalProp(a, prop, b[prop]);
13
- if (__getOwnPropSymbols)
14
- for (var prop of __getOwnPropSymbols(b)) {
15
- if (__propIsEnum.call(b, prop))
16
- __defNormalProp(a, prop, b[prop]);
17
- }
18
- return a;
19
- };
20
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
- var __objRest = (source, exclude) => {
22
- var target = {};
23
- for (var prop in source)
24
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
25
- target[prop] = source[prop];
26
- if (source != null && __getOwnPropSymbols)
27
- for (var prop of __getOwnPropSymbols(source)) {
28
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
29
- target[prop] = source[prop];
30
- }
31
- return target;
32
- };
33
- var __async = (__this, __arguments, generator) => {
34
- return new Promise((resolve, reject) => {
35
- var fulfilled = (value) => {
36
- try {
37
- step(generator.next(value));
38
- } catch (e) {
39
- reject(e);
40
- }
41
- };
42
- var rejected = (value) => {
43
- try {
44
- step(generator.throw(value));
45
- } catch (e) {
46
- reject(e);
47
- }
48
- };
49
- var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
50
- step((generator = generator.apply(__this, __arguments)).next());
51
- });
52
- };
1
+ import {
2
+ resolveAuthConfig,
3
+ resolveCallbackUrl
4
+ } from "./chunk-EAANLFR5.mjs";
5
+ import {
6
+ BrowserAuthenticationInitiator,
7
+ BrowserAuthenticationService,
8
+ BrowserPublicClientPKCEProducer,
9
+ ConfidentialClientPKCEConsumer,
10
+ DEFAULT_SCOPES,
11
+ GenericUserSession,
12
+ IFRAME_ID,
13
+ LocalStorageAdapter,
14
+ cn,
15
+ generateState,
16
+ getUser,
17
+ isWindowInIframe
18
+ } from "./chunk-PMDIR5XE.mjs";
19
+ import {
20
+ __async,
21
+ __objRest,
22
+ __spreadProps,
23
+ __spreadValues
24
+ } from "./chunk-RGHW4PYM.mjs";
53
25
 
54
26
  // src/react/hooks/useUser.tsx
55
- import { useContext as useContext4 } from "react";
27
+ import { useContext as useContext5 } from "react";
56
28
 
57
29
  // src/react/providers/UserProvider.tsx
58
- import { createContext } from "react";
30
+ import { createContext as createContext4 } from "react";
59
31
  import { useQuery } from "@tanstack/react-query";
60
32
 
61
33
  // src/react/hooks/useAuth.tsx
62
34
  import { useContext } from "react";
35
+
36
+ // src/shared/AuthContext.tsx
37
+ import { createContext } from "react";
38
+ var AuthContext = createContext(null);
39
+
40
+ // src/react/hooks/useAuth.tsx
63
41
  var useAuth = () => {
64
42
  const context = useContext(AuthContext);
65
43
  if (!context) {
@@ -69,71 +47,53 @@ var useAuth = () => {
69
47
  };
70
48
 
71
49
  // src/react/hooks/useToken.tsx
50
+ import { useContext as useContext3 } from "react";
51
+
52
+ // src/react/providers/TokenProvider.tsx
53
+ import { createContext as createContext3, useMemo } from "react";
54
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
55
+
56
+ // src/react/hooks/useSession.tsx
72
57
  import { useContext as useContext2 } from "react";
73
- var useToken = () => {
74
- const context = useContext2(TokenContext);
75
- if (!context) {
76
- throw new Error("useToken must be used within a TokenProvider");
77
- }
78
- return context;
79
- };
80
58
 
81
- // src/react/providers/UserProvider.tsx
59
+ // src/react/providers/SessionProvider.tsx
60
+ import {
61
+ createContext as createContext2
62
+ } from "react";
82
63
  import { jsx } from "react/jsx-runtime";
83
- var UserContext = createContext(null);
84
- var UserProvider = ({
64
+ var defaultSession = {
65
+ authenticated: false,
66
+ idToken: void 0,
67
+ accessToken: void 0,
68
+ displayMode: "iframe",
69
+ iframeRef: null,
70
+ setAuthResponseUrl: () => {
71
+ }
72
+ };
73
+ var SessionContext = createContext2(defaultSession);
74
+ var SessionProvider = ({
85
75
  children,
86
- userInfoService
87
- }) => {
88
- const { isLoading: authLoading, error: authError } = useAuth();
89
- const session = useSession();
90
- const { forwardedTokens, idToken, accessToken, refreshToken } = useToken();
91
- const { signIn, signOut } = useAuth();
92
- const fetchUser = () => __async(void 0, null, function* () {
93
- if (!accessToken || !userInfoService) {
94
- return null;
95
- }
96
- const user2 = yield userInfoService == null ? void 0 : userInfoService.getUserInfo(accessToken, idToken);
97
- if (!user2) {
98
- return null;
99
- }
100
- return __spreadProps(__spreadValues({}, user2), {
101
- forwardedTokens,
102
- idToken,
103
- accessToken,
104
- refreshToken
105
- });
106
- });
107
- const {
108
- data: user,
109
- isLoading: userLoading,
110
- error: userError
111
- } = useQuery({
112
- queryKey: ["user", session == null ? void 0 : session.idToken],
113
- queryFn: fetchUser,
114
- enabled: !!(session == null ? void 0 : session.idToken)
115
- // Only run the query if we have an access token
116
- });
117
- const isLoading = authLoading || userLoading;
118
- const error = authError || userError;
119
- return /* @__PURE__ */ jsx(
120
- UserContext.Provider,
121
- {
122
- value: {
123
- user: user != null ? user : null,
124
- isLoading,
125
- error,
126
- signIn,
127
- signOut
128
- },
129
- children
130
- }
131
- );
76
+ session,
77
+ iframeRef,
78
+ setAuthResponseUrl
79
+ }) => /* @__PURE__ */ jsx(
80
+ SessionContext.Provider,
81
+ {
82
+ value: __spreadProps(__spreadValues({}, session || defaultSession), { iframeRef, setAuthResponseUrl }),
83
+ children
84
+ }
85
+ );
86
+
87
+ // src/react/hooks/useSession.tsx
88
+ var useSession = () => {
89
+ const context = useContext2(SessionContext);
90
+ if (!context) {
91
+ throw new Error("useSession must be used within an SessionProvider");
92
+ }
93
+ return context;
132
94
  };
133
95
 
134
96
  // src/react/providers/TokenProvider.tsx
135
- import { createContext as createContext2, useMemo } from "react";
136
- import { useMutation, useQueryClient } from "@tanstack/react-query";
137
97
  import { parseJWT } from "oslo/jwt";
138
98
 
139
99
  // src/lib/jwt.ts
@@ -150,7 +110,7 @@ var convertForwardedTokenFormat = (inputTokens) => Object.fromEntries(
150
110
 
151
111
  // src/react/providers/TokenProvider.tsx
152
112
  import { jsx as jsx2 } from "react/jsx-runtime";
153
- var TokenContext = createContext2(void 0);
113
+ var TokenContext = createContext3(void 0);
154
114
  var TokenProvider = ({ children }) => {
155
115
  const { isLoading, error: authError } = useAuth();
156
116
  const session = useSession();
@@ -192,454 +152,71 @@ var TokenProvider = ({ children }) => {
192
152
  return /* @__PURE__ */ jsx2(TokenContext.Provider, { value, children });
193
153
  };
194
154
 
195
- // src/shared/AuthProvider.tsx
196
- import {
197
- createContext as createContext4,
198
- useState as useState3,
199
- useEffect as useEffect3,
200
- useMemo as useMemo2,
201
- useCallback as useCallback3,
202
- useRef as useRef2
203
- } from "react";
204
- import { useQuery as useQuery2, useMutation as useMutation2, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
205
-
206
- // src/services/UserInfoService.ts
207
- import { parseJWT as parseJWT2 } from "oslo/jwt";
208
- var UserInfoServiceImpl = class {
209
- constructor(endpoints) {
210
- this.endpoints = endpoints;
211
- }
212
- extractUserFromIdToken(idToken) {
213
- const parsedJWT = parseJWT2(idToken);
214
- if (!parsedJWT) {
215
- return null;
216
- }
217
- return parsedJWT.payload;
218
- }
219
- getUserInfo(accessToken, idToken) {
220
- return __async(this, null, function* () {
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();
228
- });
229
- }
230
- };
231
-
232
- // src/services/SessionService.ts
233
- import { OAuth2Client, generateCodeVerifier } from "oslo/oauth2";
234
- import * as jose from "jose";
235
-
236
- // src/lib/oauth.ts
237
- import { v4 as uuid } from "uuid";
238
- var getIssuerVariations = (issuer) => {
239
- const issuerWithoutSlash = issuer.endsWith("/") ? issuer.slice(0, issuer.length - 1) : issuer;
240
- const issuerWithSlash = `${issuerWithoutSlash}/`;
241
- return [issuerWithoutSlash, issuerWithSlash];
242
- };
243
- var addSlashIfNeeded = (url) => url.endsWith("/") ? url : `${url}/`;
244
- var getOauthEndpoints = (oauthServer) => __async(void 0, null, function* () {
245
- const openIdConfigResponse = yield fetch(
246
- `${addSlashIfNeeded(oauthServer)}.well-known/openid-configuration`
247
- );
248
- const openIdConfig = yield openIdConfigResponse.json();
249
- return {
250
- jwks: openIdConfig.jwks_uri,
251
- auth: openIdConfig.authorization_endpoint,
252
- token: openIdConfig.token_endpoint,
253
- userinfo: openIdConfig.userinfo_endpoint
254
- };
255
- });
256
- var generateState = (displayMode) => {
257
- const jsonString = JSON.stringify({
258
- uuid: uuid(),
259
- displayMode
260
- });
261
- return btoa(jsonString);
262
- };
263
- var displayModeFromState = (state, sessionDisplayMode) => {
264
- try {
265
- const jsonString = btoa(state);
266
- return JSON.parse(jsonString).displayMode;
267
- } catch (e) {
268
- console.error("Failed to parse displayMode from state:", e);
269
- return sessionDisplayMode;
270
- }
271
- };
272
-
273
- // src/utils.ts
274
- import { clsx } from "clsx";
275
- import { twMerge } from "tailwind-merge";
276
- var isPopupBlocked = () => {
277
- const popup = window.open("", "", "width=1,height=1");
278
- if (!popup) {
279
- return true;
280
- }
281
- try {
282
- if (typeof popup.closed === "undefined") {
283
- throw new Error("Popup is blocked");
284
- }
285
- } catch (e) {
286
- return true;
155
+ // src/react/hooks/useToken.tsx
156
+ var useToken = () => {
157
+ const context = useContext3(TokenContext);
158
+ if (!context) {
159
+ throw new Error("useToken must be used within a TokenProvider");
287
160
  }
288
- popup.close();
289
- return false;
290
- };
291
- var cn = (...inputs) => {
292
- return twMerge(clsx(inputs));
161
+ return context;
293
162
  };
294
163
 
295
- // src/services/SessionService.ts
296
- var AuthSessionServiceImpl = class {
297
- constructor(clientId, redirectUrl, oauthServer, inputEndpoints) {
298
- this.clientId = clientId;
299
- this.redirectUrl = redirectUrl;
300
- this.oauthServer = oauthServer;
301
- this.inputEndpoints = inputEndpoints;
302
- this.codeVerifier = void 0;
303
- this.refreshTokenTimeout = null;
304
- this.codeVerifier = this.getCodeVerifier();
305
- this.endpoints = inputEndpoints;
306
- }
307
- getCodeVerifier() {
308
- return generateCodeVerifier();
309
- }
310
- getUserInfoService() {
311
- return __async(this, null, function* () {
312
- if (this.userInfoService) {
313
- return this.userInfoService;
314
- }
315
- const endpoints = yield this.getEndpoints();
316
- this.userInfoService = new UserInfoServiceImpl(endpoints);
317
- return this.userInfoService;
318
- });
319
- }
320
- getEndpoints() {
321
- return __async(this, null, function* () {
322
- var _a;
323
- if ((_a = this.endpoints) == null ? void 0 : _a.auth) {
324
- return this.endpoints;
325
- }
326
- const jwksEndpoints = yield getOauthEndpoints(this.oauthServer);
327
- return this.endpoints ? __spreadValues(__spreadValues({}, this.endpoints), jwksEndpoints) : jwksEndpoints;
328
- });
329
- }
330
- getOauth2Client() {
331
- return __async(this, null, function* () {
332
- if (this.oauth2Client) {
333
- return this.oauth2Client;
334
- }
335
- const endpoints = yield this.getEndpoints();
336
- this.oauth2Client = new OAuth2Client(
337
- this.clientId,
338
- endpoints.auth,
339
- endpoints.token,
340
- // this
341
- { redirectURI: this.redirectUrl }
342
- );
343
- return this.oauth2Client;
344
- });
345
- }
346
- getSessionData() {
347
- return JSON.parse(
348
- localStorage.getItem(`civic-auth:${this.clientId}`) || "{}"
349
- );
350
- }
351
- updateSessionData(data) {
352
- localStorage.setItem(
353
- `civic-auth:${this.clientId}`,
354
- JSON.stringify(__spreadValues({}, data))
355
- );
356
- }
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) {
372
- return __async(this, null, function* () {
373
- var _a;
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
- }
387
- const oAuthUrl = yield oauth2Client.createAuthorizationURL({
388
- state,
389
- codeVerifier: this.codeVerifier,
390
- codeChallengeMethod: "S256",
391
- scopes
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);
405
- if (nonce) {
406
- oAuthUrl.searchParams.append("nonce", nonce);
407
- }
408
- oAuthUrl.searchParams.append("prompt", "consent");
409
- return oAuthUrl.toString();
410
- });
411
- }
412
- // TODO fix the Window reference
413
- loadAuthorizationUrl(authorizationURL, displayMode) {
414
- switch (displayMode) {
415
- case "iframe":
416
- break;
417
- case "redirect":
418
- window.location.href = authorizationURL;
419
- break;
420
- case "new_tab":
421
- window.open(authorizationURL, "_blank");
422
- break;
423
- case "custom_tab":
424
- break;
425
- }
426
- }
427
- init() {
428
- return __async(this, null, function* () {
429
- this.updateSessionData({ authenticated: false });
430
- });
431
- }
432
- determineDisplayMode(displayMode) {
433
- if (isPopupBlocked() && displayMode === "iframe") {
434
- displayMode = "redirect";
435
- }
436
- return displayMode;
437
- }
438
- signIn(displayMode, scopes, nonce) {
439
- return __async(this, null, function* () {
440
- const authorizationURL = yield this.getAuthorizationUrl(
441
- scopes,
442
- displayMode,
443
- nonce
444
- );
445
- this.loadAuthorizationUrl(authorizationURL, displayMode);
446
- });
447
- }
448
- tokenExchange(responseUrl) {
449
- return __async(this, null, function* () {
450
- let session = this.getSessionData();
451
- if (!session.authenticated) {
452
- const url = new URL(responseUrl);
453
- const authorizationCode = url.searchParams.get("code");
454
- const returnedState = url.searchParams.get("state");
455
- if (!authorizationCode || !returnedState) {
456
- throw new Error("Invalid authorization response");
457
- }
458
- const codeVerifier = session.codeVerifier;
459
- const oauth2Client = yield this.getOauth2Client();
460
- const tokens = yield oauth2Client.validateAuthorizationCode(
461
- authorizationCode,
462
- {
463
- codeVerifier
464
- }
465
- );
466
- try {
467
- yield this.validateTokens(tokens);
468
- } catch (error) {
469
- console.error("tokenExchange tokens", { error, tokens });
470
- throw new Error(
471
- `OIDC tokens validation failed: ${error.message}`
472
- );
473
- }
474
- const parsedDisplayMode = displayModeFromState(
475
- returnedState,
476
- session.displayMode
477
- );
478
- session = __spreadProps(__spreadValues({}, session), {
479
- displayMode: parsedDisplayMode,
480
- idToken: tokens.id_token,
481
- authenticated: true,
482
- state: returnedState,
483
- accessToken: tokens.access_token,
484
- refreshToken: tokens.refresh_token,
485
- timestamp: Date.now(),
486
- expiresIn: tokens.expires_in
487
- });
488
- this.updateSessionData(session);
489
- const user = yield (yield this.getUserInfoService()).getUserInfo(tokens.access_token, tokens.id_token || null);
490
- this.setUser(user);
491
- }
492
- this.setupTokenRefresh(session);
493
- if (session.displayMode === "new_tab") {
494
- window.close();
495
- } else if (session.displayMode === "redirect") {
496
- }
497
- return session;
498
- });
499
- }
500
- setupTokenRefresh(session) {
501
- if (this.refreshTokenTimeout) {
502
- clearTimeout(this.refreshTokenTimeout);
164
+ // src/react/providers/UserProvider.tsx
165
+ import { jsx as jsx3 } from "react/jsx-runtime";
166
+ var UserContext = createContext4(null);
167
+ var UserProvider = ({
168
+ children,
169
+ storage
170
+ }) => {
171
+ const { isLoading: authLoading, error: authError } = useAuth();
172
+ const session = useSession();
173
+ const { accessToken } = useToken();
174
+ const { signIn, signOut } = useAuth();
175
+ const fetchUser = () => __async(void 0, null, function* () {
176
+ if (!accessToken) {
177
+ return null;
503
178
  }
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);
179
+ const userSession = new GenericUserSession(storage);
180
+ return userSession.get();
181
+ });
182
+ const {
183
+ data: user,
184
+ isLoading: userLoading,
185
+ error: userError
186
+ } = useQuery({
187
+ queryKey: ["user", session == null ? void 0 : session.idToken],
188
+ queryFn: fetchUser,
189
+ enabled: !!(session == null ? void 0 : session.idToken)
190
+ // Only run the query if we have an access token
191
+ });
192
+ const isLoading = authLoading || userLoading;
193
+ const error = authError || userError;
194
+ return /* @__PURE__ */ jsx3(
195
+ UserContext.Provider,
196
+ {
197
+ value: {
198
+ user: user != null ? user : null,
199
+ isLoading,
200
+ error,
201
+ signIn,
202
+ signOut
203
+ },
204
+ children
516
205
  }
517
- }
518
- refreshToken() {
519
- return __async(this, null, function* () {
520
- const sessionData = this.getSessionData();
521
- if (!sessionData.refreshToken) {
522
- throw new Error("No refresh token available");
523
- }
524
- const oauth2Client = yield this.getOauth2Client();
525
- const tokens = yield oauth2Client.refreshAccessToken(
526
- sessionData.refreshToken
527
- );
528
- const session = __spreadProps(__spreadValues({}, sessionData), {
529
- idToken: tokens.id_token,
530
- authenticated: true,
531
- accessToken: tokens.access_token,
532
- refreshToken: tokens.refresh_token,
533
- timestamp: Date.now(),
534
- expiresIn: tokens.expires_in
535
- });
536
- this.updateSessionData(session);
537
- this.setupTokenRefresh(session);
538
- return session;
539
- });
540
- }
541
- getUserInfo() {
542
- return __async(this, null, function* () {
543
- const sessionData = this.getSessionData();
544
- if (!sessionData.accessToken) {
545
- throw new Error("No access token available");
546
- }
547
- const userInfoService = yield this.getUserInfoService();
548
- return userInfoService.getUserInfo(
549
- sessionData.accessToken,
550
- sessionData.idToken || null
551
- );
552
- });
553
- }
554
- /**
555
- * Uses the jose library to validate a JWT token using the OAuth JWKS endpoint
556
- * @param {string} token
557
- * @returns {Promise<jose.JWTPayload>}
558
- * @throws {Error} if the token is invalid
559
- */
560
- validateTokens(tokens) {
561
- return __async(this, null, function* () {
562
- const endpoints = yield this.getEndpoints();
563
- const JWKS = jose.createRemoteJWKSet(new URL(endpoints.jwks));
564
- const returnPayload = {};
565
- console.log("issuer", getIssuerVariations(this.oauthServer));
566
- const idTokenResponse = yield jose.jwtVerify(tokens.id_token, JWKS, {
567
- issuer: getIssuerVariations(this.oauthServer),
568
- audience: this.clientId
569
- });
570
- returnPayload.idToken = idTokenResponse.payload;
571
- const accessTokenResponse = yield jose.jwtVerify(
572
- tokens.access_token,
573
- JWKS,
574
- {
575
- issuer: getIssuerVariations(this.oauthServer)
576
- }
577
- );
578
- returnPayload.accessToken = accessTokenResponse.payload;
579
- if (tokens.refresh_token) {
580
- returnPayload.refreshToken = tokens.refresh_token;
581
- }
582
- return returnPayload;
583
- });
584
- }
585
- validateExistingSession() {
586
- return __async(this, null, function* () {
587
- const sessionData = this.getSessionData();
588
- try {
589
- if (!sessionData.idToken || !sessionData.accessToken) {
590
- const unAuthenticatedSession = __spreadProps(__spreadValues({}, sessionData), { authenticated: false });
591
- this.updateSessionData(unAuthenticatedSession);
592
- return unAuthenticatedSession;
593
- }
594
- yield this.validateTokens({
595
- id_token: sessionData.idToken,
596
- access_token: sessionData.accessToken,
597
- refresh_token: sessionData.refreshToken
598
- });
599
- sessionData.authenticated = true;
600
- return sessionData;
601
- } catch (error) {
602
- console.warn("Failed to validate existing tokens", error);
603
- const unAuthenticatedSession = {
604
- authenticated: false
605
- };
606
- this.updateSessionData(unAuthenticatedSession);
607
- return unAuthenticatedSession;
608
- }
609
- });
610
- }
206
+ );
611
207
  };
612
208
 
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
-
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";
209
+ // src/shared/AuthProvider.tsx
210
+ import {
211
+ useCallback as useCallback2,
212
+ useEffect as useEffect2,
213
+ useMemo as useMemo2,
214
+ useRef as useRef2,
215
+ useState as useState2
216
+ } from "react";
217
+ import { useMutation as useMutation2, useQuery as useQuery2, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
641
218
 
642
- // src/react/components/CivicAuthIframeModal.tsx
219
+ // src/react/components/CivicAuthIframeContainer.tsx
643
220
  import { useCallback, useEffect, useRef, useState } from "react";
644
221
 
645
222
  // src/react/components/LoadingIcon.tsx
@@ -649,7 +226,7 @@ var LoadingIcon = () => /* @__PURE__ */ jsxs("div", { role: "status", children:
649
226
  "svg",
650
227
  {
651
228
  "aria-hidden": "true",
652
- className: "inline h-8 w-8 animate-spin fill-neutral-600 text-neutral-200 dark:fill-neutral-300 dark:text-neutral-600",
229
+ className: "cac-inline cac-h-8 cac-w-8 cac-animate-spin cac-fill-neutral-600 cac-text-neutral-200 dark:cac-fill-neutral-300 dark:cac-text-neutral-600",
653
230
  viewBox: "0 0 100 101",
654
231
  fill: "none",
655
232
  xmlns: "http://www.w3.org/2000/svg",
@@ -671,7 +248,7 @@ var LoadingIcon = () => /* @__PURE__ */ jsxs("div", { role: "status", children:
671
248
  ]
672
249
  }
673
250
  ),
674
- /* @__PURE__ */ jsx4("span", { className: "sr-only", children: "Loading..." })
251
+ /* @__PURE__ */ jsx4("span", { className: "cac-sr-only", children: "Loading..." })
675
252
  ] });
676
253
 
677
254
  // src/react/components/CloseIcon.tsx
@@ -696,379 +273,126 @@ var CloseIcon = () => /* @__PURE__ */ jsxs2(
696
273
  }
697
274
  );
698
275
 
699
- // src/react/components/CivicAuthIframeModal.tsx
700
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
701
- var CivicAuthIframeModal = ({
702
- authUrl,
703
- redirectUri,
704
- setAuthResponseUrl,
705
- onClose,
706
- iframeRef,
707
- redirectInProgress = false,
708
- closeOnRedirect = true
709
- }) => {
710
- const [isLoading, setIsLoading] = useState(true);
711
- const processIframeUrl = useCallback(() => {
712
- if (iframeRef.current && iframeRef.current.contentWindow) {
713
- try {
714
- const iframeUrl = iframeRef.current.contentWindow.location.href;
715
- if (iframeUrl.startsWith(redirectUri)) {
716
- setAuthResponseUrl(iframeUrl);
717
- if (closeOnRedirect) onClose();
718
- return true;
719
- }
720
- } catch (e) {
721
- console.log("Waiting for redirect...");
722
- }
723
- }
724
- return false;
725
- }, [closeOnRedirect, iframeRef, onClose, redirectUri, setAuthResponseUrl]);
726
- const intervalId = useRef();
727
- const handleEscape = useCallback(
728
- (event) => {
729
- if (event.key === "Escape") {
730
- onClose();
276
+ // src/react/components/CivicAuthIframe.tsx
277
+ import { forwardRef } from "react";
278
+ import { jsx as jsx6 } from "react/jsx-runtime";
279
+ var CivicAuthIframe = forwardRef(
280
+ ({ onLoad }, ref) => {
281
+ return /* @__PURE__ */ jsx6(
282
+ "iframe",
283
+ {
284
+ id: IFRAME_ID,
285
+ ref,
286
+ className: "cac-h-96 cac-w-80 cac-border-none",
287
+ onLoad
731
288
  }
732
- },
733
- [onClose]
734
- );
735
- useEffect(() => {
736
- window.addEventListener("keydown", handleEscape);
737
- return () => window.removeEventListener("keydown", handleEscape);
738
- });
739
- const handleIframeLoad = () => {
740
- setIsLoading(false);
741
- console.log("handleIframeLoad");
742
- if (processIframeUrl() && intervalId.current) {
743
- clearInterval(intervalId.current);
744
- }
745
- };
746
- return /* @__PURE__ */ jsx6(
289
+ );
290
+ }
291
+ );
292
+ CivicAuthIframe.displayName = "CivicAuthIframe";
293
+
294
+ // src/react/components/CivicAuthIframeContainer.tsx
295
+ import { Fragment, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
296
+ function NoChrome({
297
+ children
298
+ }) {
299
+ return /* @__PURE__ */ jsx7(Fragment, { children });
300
+ }
301
+ function IframeChrome({
302
+ children,
303
+ onClose
304
+ }) {
305
+ return /* @__PURE__ */ jsx7(
747
306
  "div",
748
307
  {
749
- className: "absolute left-0 top-0 z-50 flex h-screen w-screen items-center justify-center bg-neutral-950 bg-opacity-50",
308
+ className: "cac-absolute cac-left-0 cac-top-0 cac-z-50 cac-flex cac-h-screen cac-w-screen cac-items-center cac-justify-center cac-bg-neutral-950 cac-bg-opacity-50",
750
309
  onClick: onClose,
751
310
  children: /* @__PURE__ */ jsxs3(
752
311
  "div",
753
312
  {
754
- className: "relative rounded-3xl bg-white p-6 shadow-lg",
313
+ className: "cac-relative cac-rounded-3xl cac-bg-white cac-p-6 cac-shadow-lg",
755
314
  onClick: (e) => e.stopPropagation(),
756
315
  children: [
757
- /* @__PURE__ */ jsx6(
316
+ /* @__PURE__ */ jsx7(
758
317
  "button",
759
318
  {
760
- className: "absolute right-4 top-4 flex cursor-pointer items-center justify-center border-none bg-transparent p-1 text-neutral-400",
319
+ className: "cac-absolute cac-right-4 cac-top-4 cac-flex cac-cursor-pointer cac-items-center cac-justify-center cac-border-none cac-bg-transparent cac-p-1 cac-text-neutral-400",
761
320
  onClick: onClose,
762
- children: /* @__PURE__ */ jsx6(CloseIcon, {})
321
+ children: /* @__PURE__ */ jsx7(CloseIcon, {})
763
322
  }
764
323
  ),
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,
768
- {
769
- ref: iframeRef,
770
- authUrl,
771
- onLoad: handleIframeLoad
772
- }
773
- )
324
+ children
774
325
  ]
775
326
  }
776
327
  )
777
328
  }
778
329
  );
779
- };
780
-
781
- // src/react/components/UserButton.tsx
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(
785
- "svg",
786
- {
787
- xmlns: "http://www.w3.org/2000/svg",
788
- width: "24",
789
- height: "24",
790
- viewBox: "0 0 24 24",
791
- fill: "none",
792
- stroke: "currentColor",
793
- strokeWidth: "2",
794
- strokeLinecap: "round",
795
- strokeLinejoin: "round",
796
- className: "lucide lucide-chevron-down",
797
- children: /* @__PURE__ */ jsx7("path", { d: "m6 9 6 6 6-6" })
798
- }
799
- );
800
- var ChevronUp = () => /* @__PURE__ */ jsx7(
801
- "svg",
802
- {
803
- xmlns: "http://www.w3.org/2000/svg",
804
- width: "24",
805
- height: "24",
806
- viewBox: "0 0 24 24",
807
- fill: "none",
808
- stroke: "currentColor",
809
- strokeWidth: "2",
810
- strokeLinecap: "round",
811
- strokeLinejoin: "round",
812
- className: "lucide lucide-chevron-up",
813
- children: /* @__PURE__ */ jsx7("path", { d: "m18 15-6-6-6 6" })
814
- }
815
- );
816
- var UserButton = ({
817
- displayMode,
818
- className
330
+ }
331
+ var CivicAuthIframeContainer = ({
332
+ onClose,
333
+ closeOnRedirect = true
819
334
  }) => {
820
- const [isOpen, setIsOpen] = useState2(false);
821
- const { signIn, isAuthenticated, signOut } = useAuth();
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
- }, []);
842
- useEffect2(() => {
843
- if (isOpen) {
844
- window.addEventListener("click", handleClickOutside);
845
- window.addEventListener("keydown", handleEscape);
846
- }
847
- return () => {
848
- window.removeEventListener("click", handleClickOutside);
849
- window.removeEventListener("keydown", handleEscape);
850
- };
851
- }, [handleClickOutside, handleEscape, isOpen]);
852
- if (isAuthenticated) {
853
- return /* @__PURE__ */ jsxs4("div", { className: "relative", id: "civic-dropdown-container", children: [
854
- /* @__PURE__ */ jsxs4(
855
- "button",
856
- {
857
- className: cn(
858
- "flex w-full items-center justify-between gap-2 rounded-full border border-neutral-500 px-3 py-2 text-neutral-500 transition-colors hover:bg-neutral-200 hover:bg-opacity-50",
859
- className
860
- ),
861
- onClick: () => setIsOpen((isOpen2) => !isOpen2),
862
- children: [
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(
864
- "img",
865
- {
866
- className: "h-full w-full object-cover",
867
- src: user.picture,
868
- alt: (user == null ? void 0 : user.name) || (user == null ? void 0 : user.email)
869
- }
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, {})
873
- ]
874
- }
875
- ),
876
- /* @__PURE__ */ jsx7(
877
- "div",
878
- {
879
- className: isOpen ? "absolute right-0 mt-2 w-full rounded-lg bg-white py-2 text-neutral-500 shadow-xl" : "hidden",
880
- children: /* @__PURE__ */ jsx7("ul", { children: /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(
881
- "button",
882
- {
883
- className: "block w-full px-4 py-2 transition-colors hover:bg-neutral-200 hover:bg-opacity-50",
884
- onClick: handleSignOut,
885
- children: "Logout"
886
- }
887
- ) }) })
335
+ var _a;
336
+ const [isLoading, setIsLoading] = useState(true);
337
+ const { isLoading: isAuthLoading } = useAuth();
338
+ const config = useConfig();
339
+ const { serverTokenExchange } = config;
340
+ const { setAuthResponseUrl, iframeRef } = useSession();
341
+ const processIframeUrl = useCallback(() => {
342
+ if (iframeRef && iframeRef.current && iframeRef.current.contentWindow) {
343
+ try {
344
+ const iframeUrl = iframeRef.current.contentWindow.location.href;
345
+ if (iframeUrl.startsWith(config.redirectUrl)) {
346
+ if (serverTokenExchange) {
347
+ const params = new URL(iframeUrl).searchParams;
348
+ params.set("tokenExchange", "true");
349
+ fetch(`${config.redirectUrl}?${params.toString()}`);
350
+ } else {
351
+ setAuthResponseUrl(iframeUrl);
352
+ }
353
+ if (closeOnRedirect) onClose == null ? void 0 : onClose();
354
+ return true;
888
355
  }
889
- )
890
- ] });
891
- }
892
- return /* @__PURE__ */ jsx7(
893
- "button",
894
- {
895
- className: cn(
896
- "rounded-full border border-neutral-500 px-3 py-2 transition-colors hover:bg-neutral-200 hover:bg-opacity-50",
897
- className
898
- ),
899
- onClick: handleSignIn,
900
- children: "Sign in"
901
- }
902
- );
903
- };
904
-
905
- // src/react/components/SignInButton.tsx
906
- import { jsx as jsx8 } from "react/jsx-runtime";
907
- var SignInButton = ({
908
- displayMode,
909
- className
910
- }) => {
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")
356
+ } catch (e) {
357
+ console.log("Waiting for redirect...");
358
+ }
977
359
  }
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
360
+ return false;
361
+ }, [
362
+ closeOnRedirect,
363
+ config.redirectUrl,
364
+ iframeRef,
365
+ onClose,
366
+ serverTokenExchange,
367
+ setAuthResponseUrl
368
+ ]);
369
+ const intervalId = useRef();
370
+ const handleEscape = useCallback(
371
+ (event) => {
372
+ if (event.key === "Escape") {
373
+ onClose == null ? void 0 : onClose();
374
+ }
1008
375
  },
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
376
+ [onClose]
377
+ );
378
+ useEffect(() => {
379
+ window.addEventListener("keydown", handleEscape);
380
+ return () => window.removeEventListener("keydown", handleEscape);
1037
381
  });
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) || {})
382
+ const handleIframeLoad = () => {
383
+ setIsLoading(false);
384
+ console.log("handleIframeLoad");
385
+ if (processIframeUrl() && intervalId.current) {
386
+ clearInterval(intervalId.current);
1043
387
  }
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 });
1059
- };
1060
-
1061
- // src/react/providers/SessionProvider.tsx
1062
- import { createContext as createContext3 } from "react";
1063
- import { jsx as jsx11 } from "react/jsx-runtime";
1064
- var defaultSession = {
1065
- authenticated: false,
1066
- idToken: void 0,
1067
- accessToken: void 0,
1068
- displayMode: "iframe"
388
+ };
389
+ const showLoadingIcon = isLoading || isAuthLoading || !((_a = iframeRef == null ? void 0 : iframeRef.current) == null ? void 0 : _a.getAttribute("src"));
390
+ const WrapperComponent = config.modalIframe ? IframeChrome : NoChrome;
391
+ return /* @__PURE__ */ jsxs3(WrapperComponent, { onClose, children: [
392
+ showLoadingIcon && /* @__PURE__ */ jsx7("div", { className: "cac-absolute cac-inset-0 cac-flex cac-items-center cac-justify-center cac-rounded-3xl cac-bg-neutral-100", children: /* @__PURE__ */ jsx7(LoadingIcon, {}) }),
393
+ /* @__PURE__ */ jsx7(CivicAuthIframe, { ref: iframeRef, onLoad: handleIframeLoad })
394
+ ] });
1069
395
  };
1070
- var SessionContext = createContext3(defaultSession);
1071
- var SessionProvider = ({ children, session }) => /* @__PURE__ */ jsx11(SessionContext.Provider, { value: session || defaultSession, children });
1072
396
 
1073
397
  // src/config.ts
1074
398
  var authConfig = {
@@ -1076,24 +400,37 @@ var authConfig = {
1076
400
  oauthServer: "https://auth-dev.civic.com/oauth/"
1077
401
  };
1078
402
 
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
- }
403
+ // src/react/providers/ConfigProvider.tsx
404
+ import { createContext as createContext5 } from "react";
405
+ import { jsx as jsx8 } from "react/jsx-runtime";
406
+ var defaultConfig = {
407
+ config: authConfig,
408
+ redirectUrl: "",
409
+ modalIframe: true,
410
+ serverTokenExchange: false
411
+ };
412
+ var ConfigContext = createContext5(defaultConfig);
413
+ var ConfigProvider = ({
414
+ children,
415
+ config,
416
+ redirectUrl,
417
+ modalIframe,
418
+ serverTokenExchange
419
+ }) => /* @__PURE__ */ jsx8(
420
+ ConfigContext.Provider,
421
+ {
422
+ value: {
423
+ config,
424
+ redirectUrl,
425
+ modalIframe: !!modalIframe,
426
+ serverTokenExchange
427
+ },
428
+ children
1090
429
  }
1091
- return false;
1092
- };
430
+ );
1093
431
 
1094
432
  // src/shared/AuthProvider.tsx
1095
- import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
1096
- var AuthContext = createContext4(null);
433
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
1097
434
  var globalThisObject;
1098
435
  if (typeof window !== "undefined") {
1099
436
  globalThisObject = window;
@@ -1103,25 +440,33 @@ if (typeof window !== "undefined") {
1103
440
  globalThisObject = Function("return this")();
1104
441
  }
1105
442
  globalThisObject.globalThis = globalThisObject;
443
+ function BlockDisplay({ children }) {
444
+ return /* @__PURE__ */ jsx9("div", { className: "cac-absolute cac-left-0 cac-top-0 cac-z-50 cac-flex cac-h-screen cac-w-screen cac-items-center cac-justify-center cac-bg-white", children: /* @__PURE__ */ jsx9("div", { className: "cac-absolute cac-inset-0 cac-flex cac-items-center cac-justify-center cac-bg-white", children }) });
445
+ }
1106
446
  var AuthProvider = ({
1107
447
  children,
1108
448
  clientId,
1109
449
  redirectUrl: inputRedirectUrl,
1110
450
  config = authConfig,
1111
- nonce,
1112
451
  onSignIn,
1113
452
  onSignOut,
1114
- authServiceImpl,
1115
- serverSideTokenExchange
453
+ pkceConsumer,
454
+ nonce,
455
+ modalIframe = true
1116
456
  }) => {
1117
- const [iframeUrl, setIframeUrl] = useState3(null);
1118
- const [currentUrl, setCurrentUrl] = useState3(null);
1119
- const [isInIframe, setIsInIframe] = useState3(false);
1120
- const [authResponseUrl, setAuthResponseUrl] = useState3(null);
1121
- const [tokenExchangeError, setTokenExchangeError] = useState3();
457
+ const [iframeUrl, setIframeUrl] = useState2(null);
458
+ const [currentUrl, setCurrentUrl] = useState2(null);
459
+ const [isInIframe, setIsInIframe] = useState2(false);
460
+ const [authResponseUrl, setAuthResponseUrl] = useState2(null);
461
+ const [tokenExchangeError, setTokenExchangeError] = useState2();
462
+ const [displayMode, setDisplayMode] = useState2("iframe");
463
+ const [browserAuthenticationInitiator, setBrowserAuthenticationInitiator] = useState2();
464
+ const [showIFrame, setShowIFrame] = useState2(false);
465
+ const [isRedirecting, setIsRedirecting] = useState2(false);
1122
466
  const queryClient3 = useQueryClient2();
1123
467
  const iframeRef = useRef2(null);
1124
- useEffect3(() => {
468
+ const serverTokenExchange = pkceConsumer instanceof ConfidentialClientPKCEConsumer;
469
+ useEffect2(() => {
1125
470
  if (typeof globalThis.window !== "undefined") {
1126
471
  setCurrentUrl(globalThis.window.location.href);
1127
472
  const isInIframeVal = isWindowInIframe(globalThis.window);
@@ -1132,26 +477,30 @@ var AuthProvider = ({
1132
477
  () => (inputRedirectUrl || currentUrl || "").split("?")[0],
1133
478
  [currentUrl, inputRedirectUrl]
1134
479
  );
1135
- const authService = useMemo2(
1136
- () => currentUrl ? authServiceImpl || new AuthSessionServiceImpl(
480
+ const [authService, setAuthService] = useState2();
481
+ useEffect2(() => {
482
+ if (!currentUrl) return;
483
+ BrowserAuthenticationService.build({
1137
484
  clientId,
1138
485
  redirectUrl,
1139
- config == null ? void 0 : config.oauthServer,
1140
- config == null ? void 0 : config.endpoints
1141
- ) : null,
1142
- [currentUrl, clientId, redirectUrl, config, authServiceImpl]
1143
- );
1144
- const [userInfoService, setUserInfoService] = useState3();
1145
- useEffect3(() => {
1146
- if (!authService) return;
1147
- authService.getUserInfoService().then(setUserInfoService);
1148
- }, [authService]);
486
+ oauthServer: config.oauthServer,
487
+ scopes: DEFAULT_SCOPES,
488
+ displayMode
489
+ }).then(setAuthService);
490
+ }, [currentUrl, clientId, redirectUrl, config, displayMode]);
1149
491
  const {
1150
492
  data: session,
1151
493
  isLoading,
1152
494
  error
1153
495
  } = useQuery2({
1154
- queryKey: ["session", authResponseUrl, iframeUrl, currentUrl, isInIframe],
496
+ queryKey: [
497
+ "session",
498
+ authResponseUrl,
499
+ iframeUrl,
500
+ currentUrl,
501
+ isInIframe,
502
+ authService
503
+ ],
1155
504
  queryFn: () => __async(void 0, null, function* () {
1156
505
  if (!authService) {
1157
506
  return { authenticated: false };
@@ -1160,12 +509,24 @@ var AuthProvider = ({
1160
509
  authResponseUrl ? authResponseUrl : globalThis.window.location.href || ""
1161
510
  );
1162
511
  const code = url.searchParams.get("code");
1163
- if (code && !isInIframe && !serverSideTokenExchange) {
512
+ const state = url.searchParams.get("state");
513
+ if (!serverTokenExchange && code && state && !isInIframe) {
1164
514
  try {
1165
- console.log("AuthProvider useQuery code", { isInIframe, code });
1166
- const newSession = yield authService.tokenExchange(url.toString());
515
+ console.log("AuthProvider useQuery code", {
516
+ isInIframe,
517
+ code,
518
+ state
519
+ });
520
+ yield authService.tokenExchange(code, state);
521
+ const clientStorage = new LocalStorageAdapter();
522
+ const user = yield getUser(clientStorage);
523
+ if (!user) {
524
+ throw new Error("Failed to get user info");
525
+ }
526
+ const userSession = new GenericUserSession(clientStorage);
527
+ userSession.set(user);
1167
528
  onSignIn == null ? void 0 : onSignIn();
1168
- return newSession;
529
+ return authService.getSessionData();
1169
530
  } catch (error2) {
1170
531
  setTokenExchangeError(error2);
1171
532
  onSignIn == null ? void 0 : onSignIn(
@@ -1183,39 +544,90 @@ var AuthProvider = ({
1183
544
  });
1184
545
  const signOutMutation = useMutation2({
1185
546
  mutationFn: () => __async(void 0, null, function* () {
1186
- authService == null ? void 0 : authService.updateSessionData({});
547
+ const authInitiator = getAuthInitiator();
548
+ authInitiator == null ? void 0 : authInitiator.signOut();
1187
549
  setIframeUrl(null);
550
+ setShowIFrame(false);
1188
551
  setAuthResponseUrl(null);
1189
552
  onSignOut == null ? void 0 : onSignOut();
1190
553
  }),
1191
554
  onSuccess: () => {
1192
555
  queryClient3.setQueryData(
1193
- ["session", authResponseUrl, iframeUrl, currentUrl, isInIframe],
556
+ [
557
+ "session",
558
+ authResponseUrl,
559
+ iframeUrl,
560
+ currentUrl,
561
+ isInIframe,
562
+ authService
563
+ ],
1194
564
  null
1195
565
  );
1196
566
  }
1197
567
  });
1198
- const signIn = useCallback3(
1199
- (overrideDisplayMode = "iframe") => __async(void 0, null, function* () {
1200
- if (!authService) return;
1201
- const url = yield authService.getAuthorizationUrl(
1202
- // This is the default scope. We will eventually pull this from the partner dashboard
1203
- DEFAULT_SCOPES,
1204
- overrideDisplayMode,
568
+ const getAuthInitiator = useCallback2(
569
+ (overrideDisplayMode) => {
570
+ const useDisplayMode = overrideDisplayMode || displayMode;
571
+ if (!pkceConsumer) {
572
+ return null;
573
+ }
574
+ return browserAuthenticationInitiator || new BrowserAuthenticationInitiator({
575
+ pkceConsumer,
576
+ // generate and retrieve the challenge client-side
577
+ clientId,
578
+ redirectUrl,
579
+ state: generateState(useDisplayMode),
580
+ scopes: DEFAULT_SCOPES,
581
+ displayMode: useDisplayMode,
582
+ oauthServer: config.oauthServer,
583
+ // the endpoints to use for the login (if not obtained from the auth server
584
+ endpointOverrides: config.endpoints,
1205
585
  nonce
1206
- );
586
+ });
587
+ },
588
+ [
589
+ displayMode,
590
+ browserAuthenticationInitiator,
591
+ clientId,
592
+ redirectUrl,
593
+ config.oauthServer,
594
+ config.endpoints,
595
+ pkceConsumer,
596
+ nonce
597
+ ]
598
+ );
599
+ const signIn = useCallback2(
600
+ (overrideDisplayMode = "iframe") => __async(void 0, null, function* () {
601
+ setDisplayMode(overrideDisplayMode);
602
+ const authInitiator = getAuthInitiator(overrideDisplayMode);
603
+ setBrowserAuthenticationInitiator(authInitiator);
1207
604
  if (overrideDisplayMode === "iframe") {
1208
- setIframeUrl(url);
1209
- return;
605
+ setShowIFrame(true);
606
+ } else if (overrideDisplayMode === "redirect") {
607
+ setIsRedirecting(true);
1210
608
  }
1211
- authService.loadAuthorizationUrl(url, overrideDisplayMode);
609
+ authInitiator == null ? void 0 : authInitiator.signIn(iframeRef.current);
1212
610
  }),
1213
- [authService, nonce]
611
+ [getAuthInitiator]
1214
612
  );
1215
613
  const isAuthenticated = useMemo2(
1216
614
  () => session ? session.authenticated : false,
1217
615
  [session]
1218
616
  );
617
+ const {
618
+ data: autoSignIn,
619
+ isLoading: autoSignInLoading,
620
+ error: autoSignInError
621
+ } = useQuery2({
622
+ queryKey: ["autoSignIn", modalIframe, redirectUrl, isAuthenticated],
623
+ queryFn: () => __async(void 0, null, function* () {
624
+ if (!modalIframe && redirectUrl && !isAuthenticated && iframeRef.current) {
625
+ signIn("iframe");
626
+ }
627
+ return true;
628
+ }),
629
+ refetchOnWindowFocus: false
630
+ });
1219
631
  const value = useMemo2(
1220
632
  () => ({
1221
633
  isLoading,
@@ -1228,41 +640,66 @@ var AuthProvider = ({
1228
640
  }),
1229
641
  [isLoading, error, signOutMutation, isAuthenticated, signIn]
1230
642
  );
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
- ] }) }) }) });
643
+ return /* @__PURE__ */ jsx9(AuthContext.Provider, { value, children: /* @__PURE__ */ jsx9(
644
+ ConfigProvider,
645
+ {
646
+ config,
647
+ redirectUrl,
648
+ modalIframe,
649
+ serverTokenExchange,
650
+ children: /* @__PURE__ */ jsx9(
651
+ SessionProvider,
652
+ {
653
+ session,
654
+ setAuthResponseUrl,
655
+ iframeRef,
656
+ children: /* @__PURE__ */ jsx9(TokenProvider, { children: /* @__PURE__ */ jsxs4(UserProvider, { storage: new LocalStorageAdapter(), children: [
657
+ modalIframe && !isInIframe && !(session == null ? void 0 : session.authenticated) && /* @__PURE__ */ jsx9(
658
+ "div",
659
+ {
660
+ style: showIFrame ? { display: "block" } : { display: "none" },
661
+ children: /* @__PURE__ */ jsx9(
662
+ CivicAuthIframeContainer,
663
+ {
664
+ onClose: () => setShowIFrame(false)
665
+ }
666
+ )
667
+ }
668
+ ),
669
+ modalIframe && (isInIframe || isRedirecting || isLoading) && /* @__PURE__ */ jsx9(BlockDisplay, { children: /* @__PURE__ */ jsx9(LoadingIcon, {}) }),
670
+ (tokenExchangeError || error) && /* @__PURE__ */ jsx9(BlockDisplay, { children: /* @__PURE__ */ jsxs4("div", { children: [
671
+ "Error: ",
672
+ (tokenExchangeError || error).message
673
+ ] }) }),
674
+ children
675
+ ] }) })
676
+ }
677
+ )
678
+ }
679
+ ) });
1249
680
  };
1250
681
 
1251
682
  // src/shared/CivicAuthProvider.tsx
1252
683
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
1253
684
  import "@civic/auth/styles.css";
1254
- import { jsx as jsx13 } from "react/jsx-runtime";
685
+ import { jsx as jsx10 } from "react/jsx-runtime";
1255
686
  var queryClient = new QueryClient();
1256
687
  var CivicAuthProvider = (_a) => {
1257
688
  var _b = _a, { children } = _b, props = __objRest(_b, ["children"]);
1258
- return /* @__PURE__ */ jsx13(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx13(AuthProvider, __spreadProps(__spreadValues({}, props), { children })) });
689
+ return /* @__PURE__ */ jsx10(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx10(
690
+ AuthProvider,
691
+ __spreadProps(__spreadValues({}, props), {
692
+ pkceConsumer: new BrowserPublicClientPKCEProducer(),
693
+ children
694
+ })
695
+ ) });
1259
696
  };
1260
697
 
1261
698
  // src/react/providers/NextAuthProvider.tsx
1262
- import { createContext as createContext5, useContext as useContext3, useEffect as useEffect5, useMemo as useMemo3, useState as useState4 } from "react";
699
+ import { createContext as createContext6, useContext as useContext4, useEffect as useEffect4, useState as useState3 } from "react";
1263
700
 
1264
701
  // src/react/hooks/useUserCookie.ts
1265
- import { useEffect as useEffect4, useRef as useRef3 } from "react";
702
+ import { useEffect as useEffect3, useRef as useRef3 } from "react";
1266
703
  import { useRouter } from "next/navigation.js";
1267
704
  import { useQuery as useQuery3 } from "@tanstack/react-query";
1268
705
 
@@ -1273,7 +710,7 @@ var getCookieValue = (key, window2) => {
1273
710
  const cookies = cookie.split(";");
1274
711
  for (const c of cookies) {
1275
712
  const [name, value] = c.trim().split("=");
1276
- if (name === key) {
713
+ if (value && name === key) {
1277
714
  try {
1278
715
  return JSON.parse(decodeURIComponent(value));
1279
716
  } catch (e) {
@@ -1301,7 +738,7 @@ var useUserCookie = () => {
1301
738
  enabled: !hasRunRef.current,
1302
739
  refetchOnWindowFocus: true
1303
740
  });
1304
- useEffect4(() => {
741
+ useEffect3(() => {
1305
742
  if (user) {
1306
743
  if (!hasRunRef.current) {
1307
744
  hasRunRef.current = true;
@@ -1316,10 +753,11 @@ var useUserCookie = () => {
1316
753
 
1317
754
  // src/react/providers/NextAuthProvider.tsx
1318
755
  import { QueryClient as QueryClient2, QueryClientProvider as QueryClientProvider2 } from "@tanstack/react-query";
1319
- import { jsx as jsx14 } from "react/jsx-runtime";
756
+ import "@civic/auth/styles.css";
757
+ import { jsx as jsx11 } from "react/jsx-runtime";
1320
758
  var queryClient2 = new QueryClient2();
1321
759
  var defaultUserContext = { user: null };
1322
- var UserContext2 = createContext5(defaultUserContext);
760
+ var UserContext2 = createContext6(defaultUserContext);
1323
761
  var CivicNextAuthProvider = (_a) => {
1324
762
  var _b = _a, {
1325
763
  children
@@ -1327,54 +765,218 @@ var CivicNextAuthProvider = (_a) => {
1327
765
  "children"
1328
766
  ]);
1329
767
  const user = useUserCookie();
1330
- const [redirectUrl, setRedirectUrl] = useState4("");
768
+ const [redirectUrl, setRedirectUrl] = useState3("");
1331
769
  const { clientId, oauthServer, callbackUrl, challengeUrl } = resolveAuthConfig();
1332
- useEffect5(() => {
770
+ useEffect4(() => {
1333
771
  if (typeof globalThis.window !== "undefined") {
1334
772
  const currentUrl = globalThis.window.location.href;
1335
- setRedirectUrl(new URL(callbackUrl, currentUrl).toString());
773
+ setRedirectUrl(resolveCallbackUrl(resolveAuthConfig(), currentUrl));
1336
774
  }
1337
775
  }, [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(
776
+ return /* @__PURE__ */ jsx11(QueryClientProvider2, { client: queryClient2, children: /* @__PURE__ */ jsx11(
1347
777
  AuthProvider,
1348
778
  __spreadProps(__spreadValues({}, props), {
779
+ redirectUrl,
1349
780
  config: { oauthServer },
1350
781
  clientId,
1351
- authServiceImpl: authService,
1352
- serverSideTokenExchange: true,
1353
- children: /* @__PURE__ */ jsx14(UserContext2.Provider, { value: user, children })
782
+ pkceConsumer: new ConfidentialClientPKCEConsumer(challengeUrl),
783
+ children: /* @__PURE__ */ jsx11(UserContext2.Provider, { value: user, children })
1354
784
  })
1355
785
  ) });
1356
786
  };
1357
- var useNextUser = () => useContext3(UserContext2);
787
+ var useNextUser = () => useContext4(UserContext2);
1358
788
 
1359
789
  // src/react/hooks/useUser.tsx
1360
790
  var useUser = () => {
1361
- const context = useContext4(UserContext);
791
+ const context = useContext5(UserContext);
1362
792
  if (!context) {
1363
793
  throw new Error("useUser must be used within a UserProvider");
1364
794
  }
1365
795
  return context;
1366
796
  };
1367
797
 
1368
- // src/react/hooks/useSession.tsx
1369
- import { useContext as useContext5 } from "react";
1370
- var useSession = () => {
1371
- const context = useContext5(SessionContext);
798
+ // src/react/hooks/useConfig.tsx
799
+ import { useContext as useContext6 } from "react";
800
+ var useConfig = () => {
801
+ const context = useContext6(ConfigContext);
1372
802
  if (!context) {
1373
- throw new Error("useSession must be used within an SessionProvider");
803
+ throw new Error("useConfig must be used within an ConfigProvider");
1374
804
  }
1375
805
  return context;
1376
806
  };
807
+
808
+ // src/react/components/UserButton.tsx
809
+ import { useCallback as useCallback3, useEffect as useEffect5, useState as useState4 } from "react";
810
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
811
+ var ChevronDown = () => /* @__PURE__ */ jsx12(
812
+ "svg",
813
+ {
814
+ xmlns: "http://www.w3.org/2000/svg",
815
+ width: "24",
816
+ height: "24",
817
+ viewBox: "0 0 24 24",
818
+ fill: "none",
819
+ stroke: "currentColor",
820
+ strokeWidth: "2",
821
+ strokeLinecap: "round",
822
+ strokeLinejoin: "round",
823
+ className: "lucide lucide-chevron-down",
824
+ children: /* @__PURE__ */ jsx12("path", { d: "m6 9 6 6 6-6" })
825
+ }
826
+ );
827
+ var ChevronUp = () => /* @__PURE__ */ jsx12(
828
+ "svg",
829
+ {
830
+ xmlns: "http://www.w3.org/2000/svg",
831
+ width: "24",
832
+ height: "24",
833
+ viewBox: "0 0 24 24",
834
+ fill: "none",
835
+ stroke: "currentColor",
836
+ strokeWidth: "2",
837
+ strokeLinecap: "round",
838
+ strokeLinejoin: "round",
839
+ className: "lucide lucide-chevron-up",
840
+ children: /* @__PURE__ */ jsx12("path", { d: "m18 15-6-6-6 6" })
841
+ }
842
+ );
843
+ var UserButton = ({
844
+ displayMode,
845
+ className
846
+ }) => {
847
+ const [isOpen, setIsOpen] = useState4(false);
848
+ const { signIn, isAuthenticated, signOut } = useAuth();
849
+ const { user } = useUser();
850
+ const handleClickOutside = useCallback3((event) => {
851
+ const target = event.target;
852
+ if (!target.closest("#civic-dropdown-container")) {
853
+ setIsOpen(false);
854
+ }
855
+ }, []);
856
+ const handleSignOut = useCallback3(() => __async(void 0, null, function* () {
857
+ yield signOut();
858
+ setIsOpen(false);
859
+ }), [signOut]);
860
+ const handleSignIn = useCallback3(() => __async(void 0, null, function* () {
861
+ yield signIn(displayMode);
862
+ setIsOpen(false);
863
+ }), [signIn, displayMode]);
864
+ const handleEscape = useCallback3((event) => {
865
+ if (event.key === "Escape") {
866
+ setIsOpen(false);
867
+ }
868
+ }, []);
869
+ useEffect5(() => {
870
+ if (isOpen) {
871
+ window.addEventListener("click", handleClickOutside);
872
+ window.addEventListener("keydown", handleEscape);
873
+ }
874
+ return () => {
875
+ window.removeEventListener("click", handleClickOutside);
876
+ window.removeEventListener("keydown", handleEscape);
877
+ };
878
+ }, [handleClickOutside, handleEscape, isOpen]);
879
+ if (isAuthenticated) {
880
+ return /* @__PURE__ */ jsxs5("div", { className: "cac-relative", id: "civic-dropdown-container", children: [
881
+ /* @__PURE__ */ jsxs5(
882
+ "button",
883
+ {
884
+ className: cn(
885
+ "cac-flex cac-w-full cac-items-center cac-justify-between cac-gap-2 cac-rounded-full cac-border cac-border-neutral-500 cac-px-3 cac-py-2 cac-text-neutral-500 cac-transition-colors hover:cac-bg-neutral-200 hover:cac-bg-opacity-50",
886
+ className
887
+ ),
888
+ onClick: () => setIsOpen((isOpen2) => !isOpen2),
889
+ children: [
890
+ (user == null ? void 0 : user.picture) ? /* @__PURE__ */ jsx12("span", { className: "cac-relative cac-flex cac-h-10 cac-w-10 cac-shrink-0 cac-gap-2 cac-overflow-hidden cac-rounded-full", children: /* @__PURE__ */ jsx12(
891
+ "img",
892
+ {
893
+ className: "cac-h-full cac-w-full cac-object-cover",
894
+ src: user.picture,
895
+ alt: (user == null ? void 0 : user.name) || (user == null ? void 0 : user.email)
896
+ }
897
+ ) }) : /* @__PURE__ */ jsx12("div", {}),
898
+ /* @__PURE__ */ jsx12("span", { children: (user == null ? void 0 : user.name) || (user == null ? void 0 : user.email) }),
899
+ isOpen ? /* @__PURE__ */ jsx12(ChevronUp, {}) : /* @__PURE__ */ jsx12(ChevronDown, {})
900
+ ]
901
+ }
902
+ ),
903
+ /* @__PURE__ */ jsx12(
904
+ "div",
905
+ {
906
+ className: isOpen ? "cac-absolute cac-right-0 cac-mt-2 cac-w-full cac-rounded-lg cac-bg-white cac-py-2 cac-text-neutral-500 cac-shadow-xl" : "cac-hidden",
907
+ children: /* @__PURE__ */ jsx12("ul", { children: /* @__PURE__ */ jsx12("li", { children: /* @__PURE__ */ jsx12(
908
+ "button",
909
+ {
910
+ className: "cac-block cac-w-full cac-px-4 cac-py-2 cac-transition-colors hover:cac-bg-neutral-200 hover:cac-bg-opacity-50",
911
+ onClick: handleSignOut,
912
+ children: "Logout"
913
+ }
914
+ ) }) })
915
+ }
916
+ )
917
+ ] });
918
+ }
919
+ return /* @__PURE__ */ jsx12(
920
+ "button",
921
+ {
922
+ "data-testid": "sign-in-button",
923
+ className: cn(
924
+ "cac-rounded-full cac-border cac-border-neutral-500 cac-px-3 cac-py-2 cac-transition-colors hover:cac-bg-neutral-200 hover:cac-bg-opacity-50",
925
+ className
926
+ ),
927
+ onClick: handleSignIn,
928
+ children: "Sign in"
929
+ }
930
+ );
931
+ };
932
+
933
+ // src/react/components/SignInButton.tsx
934
+ import { jsx as jsx13 } from "react/jsx-runtime";
935
+ var SignInButton = ({
936
+ displayMode,
937
+ className
938
+ }) => {
939
+ const { signIn } = useAuth();
940
+ return /* @__PURE__ */ jsx13(
941
+ "button",
942
+ {
943
+ "data-testid": "sign-in-button",
944
+ className: cn(
945
+ "cac-rounded-full cac-border cac-border-neutral-500 cac-px-3 cac-py-2 cac-transition-colors hover:cac-bg-neutral-200 hover:cac-bg-opacity-50",
946
+ className
947
+ ),
948
+ onClick: () => signIn(displayMode),
949
+ children: "Sign In"
950
+ }
951
+ );
952
+ };
953
+
954
+ // src/react/components/SignOutButton.tsx
955
+ import { jsx as jsx14 } from "react/jsx-runtime";
956
+ var SignOutButton = ({ className }) => {
957
+ const { signOut } = useAuth();
958
+ return /* @__PURE__ */ jsx14(
959
+ "button",
960
+ {
961
+ className: cn(
962
+ "cac-rounded-full cac-border cac-border-neutral-500 cac-px-3 cac-py-2 cac-transition-colors hover:cac-bg-neutral-200 hover:cac-bg-opacity-50",
963
+ className
964
+ ),
965
+ onClick: () => signOut(),
966
+ children: "Sign Out"
967
+ }
968
+ );
969
+ };
970
+
971
+ // src/react/components/NextLogOut.tsx
972
+ import { jsx as jsx15 } from "react/jsx-runtime";
973
+ var NextLogOut = ({ children }) => {
974
+ const config = resolveAuthConfig();
975
+ const logoutUrl = `${config.logoutUrl}`;
976
+ return /* @__PURE__ */ jsx15("a", { href: logoutUrl, children });
977
+ };
1377
978
  export {
979
+ CivicAuthIframeContainer,
1378
980
  CivicAuthProvider,
1379
981
  CivicNextAuthProvider,
1380
982
  NextLogOut,
@@ -1382,6 +984,7 @@ export {
1382
984
  SignOutButton,
1383
985
  UserButton,
1384
986
  useAuth,
987
+ useConfig,
1385
988
  useNextUser,
1386
989
  useSession,
1387
990
  useToken,