@netlify/identity 0.3.0-alpha.3 → 0.3.0-alpha.5

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/README.md CHANGED
@@ -9,7 +9,7 @@ A lightweight, no-config headless authentication library for projects using Netl
9
9
  - [Netlify Identity](https://docs.netlify.com/security/secure-access-to-sites/identity/) must be enabled on your Netlify project
10
10
  - For local development, use [`netlify dev`](https://docs.netlify.com/cli/local-development/) so the Identity endpoint is available
11
11
 
12
- ### How this library relates to other Netlify auth packages
12
+ ## How this library relates to other Netlify auth packages
13
13
 
14
14
  | Package | What it is | When to use it |
15
15
  | ------------------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------ |
@@ -54,8 +54,8 @@ import { login, getUser } from '@netlify/identity'
54
54
  const user = await login('jane@example.com', 'password123')
55
55
  console.log(`Hello, ${user.name}`)
56
56
 
57
- // Later, check auth state synchronously
58
- const currentUser = getUser()
57
+ // Later, check auth state
58
+ const currentUser = await getUser()
59
59
  ```
60
60
 
61
61
  ### Protect a Netlify Function
@@ -65,7 +65,7 @@ import { getUser } from '@netlify/identity'
65
65
  import type { Context } from '@netlify/functions'
66
66
 
67
67
  export default async (req: Request, context: Context) => {
68
- const user = getUser()
68
+ const user = await getUser()
69
69
  if (!user) return new Response('Unauthorized', { status: 401 })
70
70
  return Response.json({ id: user.id, email: user.email })
71
71
  }
@@ -78,7 +78,7 @@ import { getUser } from '@netlify/identity'
78
78
  import type { Context } from '@netlify/edge-functions'
79
79
 
80
80
  export default async (req: Request, context: Context) => {
81
- const user = getUser()
81
+ const user = await getUser()
82
82
  if (!user) return new Response('Unauthorized', { status: 401 })
83
83
  return Response.json({ id: user.id, email: user.email })
84
84
  }
@@ -91,20 +91,20 @@ export default async (req: Request, context: Context) => {
91
91
  #### `getUser`
92
92
 
93
93
  ```ts
94
- getUser(): User | null
94
+ getUser(): Promise<User | null>
95
95
  ```
96
96
 
97
- Returns the current authenticated user, or `null` if not logged in. Synchronous. Never throws.
97
+ Returns the current authenticated user, or `null` if not logged in. Always returns a full `User` object with all available fields (email, roles, timestamps, metadata) regardless of whether the call happens in the browser or on the server. Never throws.
98
98
 
99
99
  > **Next.js note:** Calling `getUser()` in a Server Component opts the page into [dynamic rendering](https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering) because it reads cookies. This is expected and correct for authenticated pages. Next.js handles the internal dynamic rendering signal automatically.
100
100
 
101
101
  #### `isAuthenticated`
102
102
 
103
103
  ```ts
104
- isAuthenticated(): boolean
104
+ isAuthenticated(): Promise<boolean>
105
105
  ```
106
106
 
107
- Returns `true` if a user is currently authenticated. Equivalent to `getUser() !== null`. Never throws.
107
+ Returns `true` if a user is currently authenticated. Equivalent to `(await getUser()) !== null`. Never throws.
108
108
 
109
109
  #### `getIdentityConfig`
110
110
 
@@ -200,7 +200,7 @@ hydrateSession(): Promise<User | null>
200
200
 
201
201
  Bootstraps the browser-side gotrue-js session from server-set auth cookies (`nf_jwt`, `nf_refresh`). Returns the hydrated `User`, or `null` if no auth cookies are present. No-op on the server.
202
202
 
203
- **When to use:** After a server-side login (e.g., via a Netlify Function or Server Action), the `nf_jwt` cookie is set but gotrue-js has no browser session yet. `getUser()` works immediately (it decodes the cookie), but account operations like `updateUser()` or `verifyEmailChange()` require a live gotrue-js session. Call `hydrateSession()` once on page load to bridge this gap.
203
+ **When to use:** After a server-side login (e.g., via a Netlify Function or Server Action), the `nf_jwt` cookie is set but gotrue-js has no browser session yet. `getUser()` calls `hydrateSession()` automatically, but account operations like `updateUser()` or `verifyEmailChange()` require a live gotrue-js session. Call `hydrateSession()` explicitly if you need the session ready before calling those operations.
204
204
 
205
205
  If a gotrue-js session already exists (e.g., from a browser-side login), this is a no-op and returns the existing user.
206
206
 
@@ -373,6 +373,7 @@ interface User {
373
373
  provider?: AuthProvider
374
374
  name?: string
375
375
  pictureUrl?: string
376
+ roles?: string[]
376
377
  metadata?: Record<string, unknown>
377
378
  rawGoTrueData?: Record<string, unknown>
378
379
  }
@@ -501,6 +502,14 @@ const AUTH_EVENTS: {
501
502
 
502
503
  Constants for auth event names. Use these instead of string literals for type safety and autocomplete.
503
504
 
505
+ | Event | When it fires |
506
+ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
507
+ | `LOGIN` | `login()`, `signup()` (with autoconfirm), `recoverPassword()`, `handleAuthCallback()` (OAuth/confirmation), `hydrateSession()` |
508
+ | `LOGOUT` | `logout()` |
509
+ | `TOKEN_REFRESH` | gotrue-js refreshes an expiring access token in the background |
510
+ | `USER_UPDATED` | `updateUser()`, `verifyEmailChange()`, `handleAuthCallback()` (email change) |
511
+ | `RECOVERY` | `handleAuthCallback()` (recovery token only). The user is authenticated but has **not** set a new password yet. Listen for this to redirect to a password reset form. `recoverPassword()` emits `LOGIN` instead because it completes both steps (token redemption + password change). |
512
+
504
513
  #### `AuthEvent`
505
514
 
506
515
  ```ts
@@ -603,8 +612,8 @@ export default function LoginPage() {
603
612
  import { getUser } from '@netlify/identity'
604
613
  import { redirect } from 'next/navigation'
605
614
 
606
- export default function Dashboard() {
607
- const user = getUser()
615
+ export default async function Dashboard() {
616
+ const user = await getUser()
608
617
  if (!user) redirect('/login')
609
618
 
610
619
  return <h1>Hello, {user.email}</h1>
@@ -643,7 +652,7 @@ import { getUser } from '@netlify/identity'
643
652
  import { redirect } from '@remix-run/node'
644
653
 
645
654
  export async function loader() {
646
- const user = getUser()
655
+ const user = await getUser()
647
656
  if (!user) return redirect('/login')
648
657
  return { user }
649
658
  }
@@ -661,7 +670,7 @@ import { createServerFn } from '@tanstack/react-start'
661
670
  import { getUser } from '@netlify/identity'
662
671
 
663
672
  export const getServerUser = createServerFn({ method: 'GET' }).handler(async () => {
664
- const user = getUser()
673
+ const user = await getUser()
665
674
  return user ?? null
666
675
  })
667
676
  ```
@@ -747,7 +756,7 @@ export const POST: APIRoute = async ({ request }) => {
747
756
  // src/pages/dashboard.astro
748
757
  import { getUser } from '@netlify/identity'
749
758
 
750
- const user = getUser()
759
+ const user = await getUser()
751
760
  if (!user) return Astro.redirect('/login')
752
761
  ---
753
762
  <h1>Hello, {user.email}</h1>
@@ -789,8 +798,8 @@ if (!user) return Astro.redirect('/login')
789
798
  import { getUser } from '@netlify/identity'
790
799
  import { redirect } from '@sveltejs/kit'
791
800
 
792
- export function load() {
793
- const user = getUser()
801
+ export async function load() {
802
+ const user = await getUser()
794
803
  if (!user) redirect(302, '/login')
795
804
  return { user }
796
805
  }
@@ -865,9 +874,10 @@ import { getUser, onAuthChange } from '@netlify/identity'
865
874
  import type { User } from '@netlify/identity'
866
875
 
867
876
  export function useAuth() {
868
- const [user, setUser] = useState<User | null>(getUser())
877
+ const [user, setUser] = useState<User | null>(null)
869
878
 
870
879
  useEffect(() => {
880
+ getUser().then(setUser)
871
881
  return onAuthChange((_event, user) => setUser(user))
872
882
  }, [])
873
883
 
package/dist/index.cjs CHANGED
@@ -61,7 +61,7 @@ var AUTH_PROVIDERS = ["google", "github", "gitlab", "bitbucket", "facebook", "sa
61
61
  var import_gotrue_js = __toESM(require("gotrue-js"), 1);
62
62
 
63
63
  // src/errors.ts
64
- var AuthError = class extends Error {
64
+ var AuthError = class _AuthError extends Error {
65
65
  constructor(message, status, options) {
66
66
  super(message);
67
67
  this.name = "AuthError";
@@ -70,6 +70,10 @@ var AuthError = class extends Error {
70
70
  this.cause = options.cause;
71
71
  }
72
72
  }
73
+ static from(error) {
74
+ const message = error instanceof Error ? error.message : String(error);
75
+ return new _AuthError(message, void 0, { cause: error });
76
+ }
73
77
  };
74
78
  var MissingIdentityError = class extends Error {
75
79
  constructor(message = "Netlify Identity is not available.") {
@@ -94,7 +98,7 @@ var discoverApiUrl = () => {
94
98
  cachedApiUrl = identityContext.url;
95
99
  } else if (globalThis.Netlify?.context?.url) {
96
100
  cachedApiUrl = new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href;
97
- } else if (process.env.URL) {
101
+ } else if (typeof process !== "undefined" && process.env?.URL) {
98
102
  cachedApiUrl = new URL(IDENTITY_PATH, process.env.URL).href;
99
103
  }
100
104
  }
@@ -131,7 +135,7 @@ var getIdentityContext = () => {
131
135
  if (globalThis.Netlify?.context?.url) {
132
136
  return { url: new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href };
133
137
  }
134
- const siteUrl = process.env.URL;
138
+ const siteUrl = typeof process !== "undefined" ? process.env?.URL : void 0;
135
139
  if (siteUrl) {
136
140
  return { url: new URL(IDENTITY_PATH, siteUrl).href };
137
141
  }
@@ -143,7 +147,12 @@ var NF_JWT_COOKIE = "nf_jwt";
143
147
  var NF_REFRESH_COOKIE = "nf_refresh";
144
148
  var getCookie = (name) => {
145
149
  const match = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=([^;]*)`));
146
- return match ? decodeURIComponent(match[1]) : null;
150
+ if (!match) return null;
151
+ try {
152
+ return decodeURIComponent(match[1]);
153
+ } catch {
154
+ return match[1];
155
+ }
147
156
  };
148
157
  var setAuthCookies = (cookies, accessToken, refreshToken) => {
149
158
  cookies.set({
@@ -213,143 +222,6 @@ var triggerNextjsDynamic = () => {
213
222
  }
214
223
  };
215
224
 
216
- // src/user.ts
217
- var toAuthProvider = (value) => typeof value === "string" && AUTH_PROVIDERS.includes(value) ? value : void 0;
218
- var toUser = (userData) => {
219
- const userMeta = userData.user_metadata ?? {};
220
- const appMeta = userData.app_metadata ?? {};
221
- const name = userMeta.full_name || userMeta.name;
222
- const pictureUrl = userMeta.avatar_url;
223
- return {
224
- id: userData.id,
225
- email: userData.email,
226
- emailVerified: !!userData.confirmed_at,
227
- createdAt: userData.created_at,
228
- updatedAt: userData.updated_at,
229
- provider: toAuthProvider(appMeta.provider),
230
- name: typeof name === "string" ? name : void 0,
231
- pictureUrl: typeof pictureUrl === "string" ? pictureUrl : void 0,
232
- metadata: userMeta,
233
- rawGoTrueData: { ...userData }
234
- };
235
- };
236
- var claimsToUser = (claims) => {
237
- const appMeta = claims.app_metadata ?? {};
238
- const userMeta = claims.user_metadata ?? {};
239
- const name = userMeta.full_name || userMeta.name;
240
- return {
241
- id: claims.sub ?? "",
242
- email: claims.email,
243
- provider: toAuthProvider(appMeta.provider),
244
- name: typeof name === "string" ? name : void 0,
245
- metadata: userMeta
246
- };
247
- };
248
- var hydrating = false;
249
- var backgroundHydrate = (accessToken) => {
250
- if (hydrating) return;
251
- hydrating = true;
252
- const refreshToken = getCookie(NF_REFRESH_COOKIE) ?? "";
253
- const decoded = decodeJwtPayload(accessToken);
254
- const expiresAt = decoded?.exp ?? Math.floor(Date.now() / 1e3) + 3600;
255
- const expiresIn = Math.max(0, expiresAt - Math.floor(Date.now() / 1e3));
256
- setTimeout(() => {
257
- try {
258
- const client = getClient();
259
- client.createUser(
260
- {
261
- access_token: accessToken,
262
- token_type: "bearer",
263
- expires_in: expiresIn,
264
- expires_at: expiresAt,
265
- refresh_token: refreshToken
266
- },
267
- true
268
- ).catch(() => {
269
- }).finally(() => {
270
- hydrating = false;
271
- });
272
- } catch {
273
- hydrating = false;
274
- }
275
- }, 0);
276
- };
277
- var decodeJwtPayload = (token) => {
278
- try {
279
- const parts = token.split(".");
280
- if (parts.length !== 3) return null;
281
- const payload = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
282
- return JSON.parse(payload);
283
- } catch {
284
- return null;
285
- }
286
- };
287
- var getUser = () => {
288
- if (isBrowser()) {
289
- const client = getGoTrueClient();
290
- const currentUser = client?.currentUser() ?? null;
291
- if (currentUser) {
292
- const jwt2 = getCookie(NF_JWT_COOKIE);
293
- if (!jwt2) {
294
- try {
295
- currentUser.clearSession();
296
- } catch {
297
- }
298
- return null;
299
- }
300
- return toUser(currentUser);
301
- }
302
- const jwt = getCookie(NF_JWT_COOKIE);
303
- if (!jwt) return null;
304
- const claims = decodeJwtPayload(jwt);
305
- if (!claims) return null;
306
- backgroundHydrate(jwt);
307
- return claimsToUser(claims);
308
- }
309
- triggerNextjsDynamic();
310
- const identityContext = globalThis.netlifyIdentityContext;
311
- if (identityContext?.user) {
312
- return claimsToUser(identityContext.user);
313
- }
314
- const serverJwt = getServerCookie(NF_JWT_COOKIE);
315
- if (serverJwt) {
316
- const claims = decodeJwtPayload(serverJwt);
317
- if (claims) return claimsToUser(claims);
318
- }
319
- return null;
320
- };
321
- var isAuthenticated = () => getUser() !== null;
322
-
323
- // src/config.ts
324
- var getIdentityConfig = () => {
325
- if (isBrowser()) {
326
- return { url: `${window.location.origin}${IDENTITY_PATH}` };
327
- }
328
- return getIdentityContext();
329
- };
330
- var getSettings = async () => {
331
- const client = getClient();
332
- try {
333
- const raw = await client.settings();
334
- const external = raw.external ?? {};
335
- return {
336
- autoconfirm: raw.autoconfirm,
337
- disableSignup: raw.disable_signup,
338
- providers: {
339
- google: external.google ?? false,
340
- github: external.github ?? false,
341
- gitlab: external.gitlab ?? false,
342
- bitbucket: external.bitbucket ?? false,
343
- facebook: external.facebook ?? false,
344
- email: external.email ?? false,
345
- saml: external.saml ?? false
346
- }
347
- };
348
- } catch (err) {
349
- throw new AuthError(err instanceof Error ? err.message : "Failed to fetch identity settings", 502, { cause: err });
350
- }
351
- };
352
-
353
225
  // src/events.ts
354
226
  var AUTH_EVENTS = {
355
227
  LOGIN: "login",
@@ -428,7 +300,7 @@ var login = async (email, password) => {
428
300
  body: body.toString()
429
301
  });
430
302
  } catch (error) {
431
- throw new AuthError(error.message, void 0, { cause: error });
303
+ throw AuthError.from(error);
432
304
  }
433
305
  if (!res.ok) {
434
306
  const errorBody = await res.json().catch(() => ({}));
@@ -442,7 +314,7 @@ var login = async (email, password) => {
442
314
  headers: { Authorization: `Bearer ${accessToken}` }
443
315
  });
444
316
  } catch (error) {
445
- throw new AuthError(error.message, void 0, { cause: error });
317
+ throw AuthError.from(error);
446
318
  }
447
319
  if (!userRes.ok) {
448
320
  const errorBody = await userRes.json().catch(() => ({}));
@@ -462,7 +334,7 @@ var login = async (email, password) => {
462
334
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
463
335
  return user;
464
336
  } catch (error) {
465
- throw new AuthError(error.message, void 0, { cause: error });
337
+ throw AuthError.from(error);
466
338
  }
467
339
  };
468
340
  var signup = async (email, password, data) => {
@@ -477,7 +349,7 @@ var signup = async (email, password, data) => {
477
349
  body: JSON.stringify({ email, password, data })
478
350
  });
479
351
  } catch (error) {
480
- throw new AuthError(error.message, void 0, { cause: error });
352
+ throw AuthError.from(error);
481
353
  }
482
354
  if (!res.ok) {
483
355
  const errorBody = await res.json().catch(() => ({}));
@@ -506,7 +378,7 @@ var signup = async (email, password, data) => {
506
378
  }
507
379
  return user;
508
380
  } catch (error) {
509
- throw new AuthError(error.message, void 0, { cause: error });
381
+ throw AuthError.from(error);
510
382
  }
511
383
  };
512
384
  var logout = async () => {
@@ -535,7 +407,7 @@ var logout = async () => {
535
407
  deleteBrowserAuthCookies();
536
408
  emitAuthEvent(AUTH_EVENTS.LOGOUT, null);
537
409
  } catch (error) {
538
- throw new AuthError(error.message, void 0, { cause: error });
410
+ throw AuthError.from(error);
539
411
  }
540
412
  };
541
413
  var oauthLogin = (provider) => {
@@ -566,7 +438,7 @@ var handleAuthCallback = async () => {
566
438
  return null;
567
439
  } catch (error) {
568
440
  if (error instanceof AuthError) throw error;
569
- throw new AuthError(error.message, void 0, { cause: error });
441
+ throw AuthError.from(error);
570
442
  }
571
443
  };
572
444
  var handleOAuthCallback = async (client, params, accessToken) => {
@@ -651,21 +523,172 @@ var hydrateSession = async () => {
651
523
  const decoded = decodeJwtPayload(accessToken);
652
524
  const expiresAt = decoded?.exp ?? Math.floor(Date.now() / 1e3) + 3600;
653
525
  const expiresIn = Math.max(0, expiresAt - Math.floor(Date.now() / 1e3));
654
- const gotrueUser = await client.createUser(
655
- {
656
- access_token: accessToken,
657
- token_type: "bearer",
658
- expires_in: expiresIn,
659
- expires_at: expiresAt,
660
- refresh_token: refreshToken
661
- },
662
- persistSession
663
- );
526
+ let gotrueUser;
527
+ try {
528
+ gotrueUser = await client.createUser(
529
+ {
530
+ access_token: accessToken,
531
+ token_type: "bearer",
532
+ expires_in: expiresIn,
533
+ expires_at: expiresAt,
534
+ refresh_token: refreshToken
535
+ },
536
+ persistSession
537
+ );
538
+ } catch {
539
+ deleteBrowserAuthCookies();
540
+ return null;
541
+ }
664
542
  const user = toUser(gotrueUser);
665
543
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
666
544
  return user;
667
545
  };
668
546
 
547
+ // src/user.ts
548
+ var toAuthProvider = (value) => typeof value === "string" && AUTH_PROVIDERS.includes(value) ? value : void 0;
549
+ var toRoles = (appMeta) => {
550
+ const roles = appMeta.roles;
551
+ if (Array.isArray(roles) && roles.every((r) => typeof r === "string")) {
552
+ return roles;
553
+ }
554
+ return void 0;
555
+ };
556
+ var toUser = (userData) => {
557
+ const userMeta = userData.user_metadata ?? {};
558
+ const appMeta = userData.app_metadata ?? {};
559
+ const name = userMeta.full_name || userMeta.name;
560
+ const pictureUrl = userMeta.avatar_url;
561
+ return {
562
+ id: userData.id,
563
+ email: userData.email,
564
+ emailVerified: !!userData.confirmed_at,
565
+ createdAt: userData.created_at,
566
+ updatedAt: userData.updated_at,
567
+ provider: toAuthProvider(appMeta.provider),
568
+ name: typeof name === "string" ? name : void 0,
569
+ pictureUrl: typeof pictureUrl === "string" ? pictureUrl : void 0,
570
+ roles: toRoles(appMeta),
571
+ metadata: userMeta,
572
+ rawGoTrueData: { ...userData }
573
+ };
574
+ };
575
+ var claimsToUser = (claims) => {
576
+ const appMeta = claims.app_metadata ?? {};
577
+ const userMeta = claims.user_metadata ?? {};
578
+ const name = userMeta.full_name || userMeta.name;
579
+ const pictureUrl = userMeta.avatar_url;
580
+ return {
581
+ id: claims.sub ?? "",
582
+ email: claims.email,
583
+ provider: toAuthProvider(appMeta.provider),
584
+ name: typeof name === "string" ? name : void 0,
585
+ pictureUrl: typeof pictureUrl === "string" ? pictureUrl : void 0,
586
+ roles: toRoles(appMeta),
587
+ metadata: userMeta
588
+ };
589
+ };
590
+ var decodeJwtPayload = (token) => {
591
+ try {
592
+ const parts = token.split(".");
593
+ if (parts.length !== 3) return null;
594
+ const payload = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
595
+ return JSON.parse(payload);
596
+ } catch {
597
+ return null;
598
+ }
599
+ };
600
+ var fetchFullUser = async (identityUrl, jwt) => {
601
+ try {
602
+ const res = await fetch(`${identityUrl}/user`, {
603
+ headers: { Authorization: `Bearer ${jwt}` }
604
+ });
605
+ if (!res.ok) return null;
606
+ const userData = await res.json();
607
+ return toUser(userData);
608
+ } catch {
609
+ return null;
610
+ }
611
+ };
612
+ var resolveIdentityUrl = () => {
613
+ const identityContext = getIdentityContext();
614
+ if (identityContext?.url) return identityContext.url;
615
+ if (globalThis.Netlify?.context?.url) {
616
+ return new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href;
617
+ }
618
+ const siteUrl = typeof process !== "undefined" ? process.env?.URL : void 0;
619
+ if (siteUrl) {
620
+ return new URL(IDENTITY_PATH, siteUrl).href;
621
+ }
622
+ return null;
623
+ };
624
+ var getUser = async () => {
625
+ if (isBrowser()) {
626
+ const client = getGoTrueClient();
627
+ const currentUser = client?.currentUser() ?? null;
628
+ if (currentUser) {
629
+ const jwt2 = getCookie(NF_JWT_COOKIE);
630
+ if (!jwt2) {
631
+ try {
632
+ currentUser.clearSession();
633
+ } catch {
634
+ }
635
+ return null;
636
+ }
637
+ return toUser(currentUser);
638
+ }
639
+ const jwt = getCookie(NF_JWT_COOKIE);
640
+ if (!jwt) return null;
641
+ const claims2 = decodeJwtPayload(jwt);
642
+ if (!claims2) return null;
643
+ const hydrated = await hydrateSession();
644
+ if (hydrated) return hydrated;
645
+ return claimsToUser(claims2);
646
+ }
647
+ triggerNextjsDynamic();
648
+ const identityContext = globalThis.netlifyIdentityContext;
649
+ const serverJwt = identityContext?.token || getServerCookie(NF_JWT_COOKIE);
650
+ if (serverJwt) {
651
+ const identityUrl = resolveIdentityUrl();
652
+ if (identityUrl) {
653
+ const fullUser = await fetchFullUser(identityUrl, serverJwt);
654
+ if (fullUser) return fullUser;
655
+ }
656
+ }
657
+ const claims = identityContext?.user ?? (serverJwt ? decodeJwtPayload(serverJwt) : null);
658
+ return claims ? claimsToUser(claims) : null;
659
+ };
660
+ var isAuthenticated = async () => await getUser() !== null;
661
+
662
+ // src/config.ts
663
+ var getIdentityConfig = () => {
664
+ if (isBrowser()) {
665
+ return { url: `${window.location.origin}${IDENTITY_PATH}` };
666
+ }
667
+ return getIdentityContext();
668
+ };
669
+ var getSettings = async () => {
670
+ const client = getClient();
671
+ try {
672
+ const raw = await client.settings();
673
+ const external = raw.external ?? {};
674
+ return {
675
+ autoconfirm: raw.autoconfirm,
676
+ disableSignup: raw.disable_signup,
677
+ providers: {
678
+ google: external.google ?? false,
679
+ github: external.github ?? false,
680
+ gitlab: external.gitlab ?? false,
681
+ bitbucket: external.bitbucket ?? false,
682
+ facebook: external.facebook ?? false,
683
+ email: external.email ?? false,
684
+ saml: external.saml ?? false
685
+ }
686
+ };
687
+ } catch (err) {
688
+ throw new AuthError(err instanceof Error ? err.message : "Failed to fetch identity settings", 502, { cause: err });
689
+ }
690
+ };
691
+
669
692
  // src/account.ts
670
693
  var resolveCurrentUser = async () => {
671
694
  const client = getClient();
@@ -685,7 +708,7 @@ var requestPasswordRecovery = async (email) => {
685
708
  try {
686
709
  await client.requestPasswordRecovery(email);
687
710
  } catch (error) {
688
- throw new AuthError(error.message, void 0, { cause: error });
711
+ throw AuthError.from(error);
689
712
  }
690
713
  };
691
714
  var recoverPassword = async (token, newPassword) => {
@@ -697,7 +720,7 @@ var recoverPassword = async (token, newPassword) => {
697
720
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
698
721
  return user;
699
722
  } catch (error) {
700
- throw new AuthError(error.message, void 0, { cause: error });
723
+ throw AuthError.from(error);
701
724
  }
702
725
  };
703
726
  var confirmEmail = async (token) => {
@@ -708,7 +731,7 @@ var confirmEmail = async (token) => {
708
731
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
709
732
  return user;
710
733
  } catch (error) {
711
- throw new AuthError(error.message, void 0, { cause: error });
734
+ throw AuthError.from(error);
712
735
  }
713
736
  };
714
737
  var acceptInvite = async (token, password) => {
@@ -719,15 +742,15 @@ var acceptInvite = async (token, password) => {
719
742
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
720
743
  return user;
721
744
  } catch (error) {
722
- throw new AuthError(error.message, void 0, { cause: error });
745
+ throw AuthError.from(error);
723
746
  }
724
747
  };
725
748
  var verifyEmailChange = async (token) => {
726
749
  if (!isBrowser()) throw new AuthError("verifyEmailChange() is only available in the browser");
727
750
  const currentUser = await resolveCurrentUser();
728
- const jwt = await currentUser.jwt();
729
- const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
730
751
  try {
752
+ const jwt = await currentUser.jwt();
753
+ const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
731
754
  const res = await fetch(`${identityUrl}/user`, {
732
755
  method: "PUT",
733
756
  headers: {
@@ -746,7 +769,7 @@ var verifyEmailChange = async (token) => {
746
769
  return user;
747
770
  } catch (error) {
748
771
  if (error instanceof AuthError) throw error;
749
- throw new AuthError(error.message, void 0, { cause: error });
772
+ throw AuthError.from(error);
750
773
  }
751
774
  };
752
775
  var updateUser = async (updates) => {
@@ -757,7 +780,7 @@ var updateUser = async (updates) => {
757
780
  emitAuthEvent(AUTH_EVENTS.USER_UPDATED, user);
758
781
  return user;
759
782
  } catch (error) {
760
- throw new AuthError(error.message, void 0, { cause: error });
783
+ throw AuthError.from(error);
761
784
  }
762
785
  };
763
786