@netlify/identity 0.3.0-alpha.2 → 0.3.0-alpha.4

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
  | ------------------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------ |
@@ -26,7 +26,7 @@ This library wraps `gotrue-js` in the browser and calls the GoTrue HTTP API dire
26
26
  - [API](#api)
27
27
  - [Functions](#functions) -- `getUser`, `login`, `signup`, `logout`, `oauthLogin`, `handleAuthCallback`, `onAuthChange`, `hydrateSession`, and more
28
28
  - [Admin Operations](#admin-operations) -- `admin.listUsers`, `admin.getUser`, `admin.createUser`, `admin.updateUser`, `admin.deleteUser`
29
- - [Types](#types) -- `User`, `AuthEvent`, `CallbackResult`, `Settings`, etc.
29
+ - [Types](#types) -- `User`, `AuthEvent`, `CallbackResult`, `Settings`, `Admin`, `ListUsersOptions`, `CreateUserParams`, etc.
30
30
  - [Errors](#errors) -- `AuthError`, `MissingIdentityError`
31
31
  - [Framework integration](#framework-integration) -- Next.js, Remix, TanStack Start, Astro, SvelteKit
32
32
  - [Guides](#guides)
@@ -312,7 +312,7 @@ export default async (req: Request, context: Context) => {
312
312
  #### `admin.listUsers`
313
313
 
314
314
  ```ts
315
- admin.listUsers(options?: { page?: number; perPage?: number }): Promise<User[]>
315
+ admin.listUsers(options?: ListUsersOptions): Promise<User[]>
316
316
  ```
317
317
 
318
318
  Lists all users. Pagination options are supported on the server; they are ignored in the browser (gotrue-js does not support pagination for this method).
@@ -332,7 +332,7 @@ Gets a single user by ID.
332
332
  #### `admin.createUser`
333
333
 
334
334
  ```ts
335
- admin.createUser(params: { email: string; password: string; data?: Record<string, unknown> }): Promise<User>
335
+ admin.createUser(params: CreateUserParams): Promise<User>
336
336
  ```
337
337
 
338
338
  Creates a new user. The user is auto-confirmed. Optional `data` is spread into the request body as additional attributes.
@@ -450,6 +450,43 @@ interface AppMetadata {
450
450
  }
451
451
  ```
452
452
 
453
+ #### `ListUsersOptions`
454
+
455
+ ```ts
456
+ interface ListUsersOptions {
457
+ page?: number
458
+ perPage?: number
459
+ }
460
+ ```
461
+
462
+ Pagination options for `admin.listUsers()`. Only used on the server; pagination is ignored in the browser (gotrue-js limitation).
463
+
464
+ #### `CreateUserParams`
465
+
466
+ ```ts
467
+ interface CreateUserParams {
468
+ email: string
469
+ password: string
470
+ data?: Record<string, unknown>
471
+ }
472
+ ```
473
+
474
+ Parameters for `admin.createUser()`. Optional `data` is spread into the GoTrue request body as top-level attributes (use it to set `app_metadata`, `user_metadata`, `role`, etc.).
475
+
476
+ #### `Admin`
477
+
478
+ ```ts
479
+ interface Admin {
480
+ listUsers: (options?: ListUsersOptions) => Promise<User[]>
481
+ getUser: (userId: string) => Promise<User>
482
+ createUser: (params: CreateUserParams) => Promise<User>
483
+ updateUser: (userId: string, attributes: AdminUserUpdates) => Promise<User>
484
+ deleteUser: (userId: string) => Promise<void>
485
+ }
486
+ ```
487
+
488
+ The type of the `admin` export. Useful for passing the admin namespace as a dependency.
489
+
453
490
  #### `AUTH_EVENTS`
454
491
 
455
492
  ```ts
@@ -464,6 +501,14 @@ const AUTH_EVENTS: {
464
501
 
465
502
  Constants for auth event names. Use these instead of string literals for type safety and autocomplete.
466
503
 
504
+ | Event | When it fires |
505
+ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
506
+ | `LOGIN` | `login()`, `signup()` (with autoconfirm), `recoverPassword()`, `handleAuthCallback()` (OAuth/confirmation), `hydrateSession()` |
507
+ | `LOGOUT` | `logout()` |
508
+ | `TOKEN_REFRESH` | gotrue-js refreshes an expiring access token in the background |
509
+ | `USER_UPDATED` | `updateUser()`, `verifyEmailChange()`, `handleAuthCallback()` (email change) |
510
+ | `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). |
511
+
467
512
  #### `AuthEvent`
468
513
 
469
514
  ```ts
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({
@@ -428,7 +437,7 @@ var login = async (email, password) => {
428
437
  body: body.toString()
429
438
  });
430
439
  } catch (error) {
431
- throw new AuthError(error.message, void 0, { cause: error });
440
+ throw AuthError.from(error);
432
441
  }
433
442
  if (!res.ok) {
434
443
  const errorBody = await res.json().catch(() => ({}));
@@ -442,7 +451,7 @@ var login = async (email, password) => {
442
451
  headers: { Authorization: `Bearer ${accessToken}` }
443
452
  });
444
453
  } catch (error) {
445
- throw new AuthError(error.message, void 0, { cause: error });
454
+ throw AuthError.from(error);
446
455
  }
447
456
  if (!userRes.ok) {
448
457
  const errorBody = await userRes.json().catch(() => ({}));
@@ -462,7 +471,7 @@ var login = async (email, password) => {
462
471
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
463
472
  return user;
464
473
  } catch (error) {
465
- throw new AuthError(error.message, void 0, { cause: error });
474
+ throw AuthError.from(error);
466
475
  }
467
476
  };
468
477
  var signup = async (email, password, data) => {
@@ -477,7 +486,7 @@ var signup = async (email, password, data) => {
477
486
  body: JSON.stringify({ email, password, data })
478
487
  });
479
488
  } catch (error) {
480
- throw new AuthError(error.message, void 0, { cause: error });
489
+ throw AuthError.from(error);
481
490
  }
482
491
  if (!res.ok) {
483
492
  const errorBody = await res.json().catch(() => ({}));
@@ -506,7 +515,7 @@ var signup = async (email, password, data) => {
506
515
  }
507
516
  return user;
508
517
  } catch (error) {
509
- throw new AuthError(error.message, void 0, { cause: error });
518
+ throw AuthError.from(error);
510
519
  }
511
520
  };
512
521
  var logout = async () => {
@@ -535,7 +544,7 @@ var logout = async () => {
535
544
  deleteBrowserAuthCookies();
536
545
  emitAuthEvent(AUTH_EVENTS.LOGOUT, null);
537
546
  } catch (error) {
538
- throw new AuthError(error.message, void 0, { cause: error });
547
+ throw AuthError.from(error);
539
548
  }
540
549
  };
541
550
  var oauthLogin = (provider) => {
@@ -566,7 +575,7 @@ var handleAuthCallback = async () => {
566
575
  return null;
567
576
  } catch (error) {
568
577
  if (error instanceof AuthError) throw error;
569
- throw new AuthError(error.message, void 0, { cause: error });
578
+ throw AuthError.from(error);
570
579
  }
571
580
  };
572
581
  var handleOAuthCallback = async (client, params, accessToken) => {
@@ -651,16 +660,22 @@ var hydrateSession = async () => {
651
660
  const decoded = decodeJwtPayload(accessToken);
652
661
  const expiresAt = decoded?.exp ?? Math.floor(Date.now() / 1e3) + 3600;
653
662
  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
- );
663
+ let gotrueUser;
664
+ try {
665
+ gotrueUser = await client.createUser(
666
+ {
667
+ access_token: accessToken,
668
+ token_type: "bearer",
669
+ expires_in: expiresIn,
670
+ expires_at: expiresAt,
671
+ refresh_token: refreshToken
672
+ },
673
+ persistSession
674
+ );
675
+ } catch {
676
+ deleteBrowserAuthCookies();
677
+ return null;
678
+ }
664
679
  const user = toUser(gotrueUser);
665
680
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
666
681
  return user;
@@ -685,7 +700,7 @@ var requestPasswordRecovery = async (email) => {
685
700
  try {
686
701
  await client.requestPasswordRecovery(email);
687
702
  } catch (error) {
688
- throw new AuthError(error.message, void 0, { cause: error });
703
+ throw AuthError.from(error);
689
704
  }
690
705
  };
691
706
  var recoverPassword = async (token, newPassword) => {
@@ -697,7 +712,7 @@ var recoverPassword = async (token, newPassword) => {
697
712
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
698
713
  return user;
699
714
  } catch (error) {
700
- throw new AuthError(error.message, void 0, { cause: error });
715
+ throw AuthError.from(error);
701
716
  }
702
717
  };
703
718
  var confirmEmail = async (token) => {
@@ -708,7 +723,7 @@ var confirmEmail = async (token) => {
708
723
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
709
724
  return user;
710
725
  } catch (error) {
711
- throw new AuthError(error.message, void 0, { cause: error });
726
+ throw AuthError.from(error);
712
727
  }
713
728
  };
714
729
  var acceptInvite = async (token, password) => {
@@ -719,15 +734,15 @@ var acceptInvite = async (token, password) => {
719
734
  emitAuthEvent(AUTH_EVENTS.LOGIN, user);
720
735
  return user;
721
736
  } catch (error) {
722
- throw new AuthError(error.message, void 0, { cause: error });
737
+ throw AuthError.from(error);
723
738
  }
724
739
  };
725
740
  var verifyEmailChange = async (token) => {
726
741
  if (!isBrowser()) throw new AuthError("verifyEmailChange() is only available in the browser");
727
742
  const currentUser = await resolveCurrentUser();
728
- const jwt = await currentUser.jwt();
729
- const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
730
743
  try {
744
+ const jwt = await currentUser.jwt();
745
+ const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
731
746
  const res = await fetch(`${identityUrl}/user`, {
732
747
  method: "PUT",
733
748
  headers: {
@@ -746,7 +761,7 @@ var verifyEmailChange = async (token) => {
746
761
  return user;
747
762
  } catch (error) {
748
763
  if (error instanceof AuthError) throw error;
749
- throw new AuthError(error.message, void 0, { cause: error });
764
+ throw AuthError.from(error);
750
765
  }
751
766
  };
752
767
  var updateUser = async (updates) => {
@@ -757,7 +772,7 @@ var updateUser = async (updates) => {
757
772
  emitAuthEvent(AUTH_EVENTS.USER_UPDATED, user);
758
773
  return user;
759
774
  } catch (error) {
760
- throw new AuthError(error.message, void 0, { cause: error });
775
+ throw AuthError.from(error);
761
776
  }
762
777
  };
763
778