@netlify/identity 0.1.1-alpha.2 → 0.1.1-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -38,11 +38,13 @@ __export(index_exports, {
38
38
  getSettings: () => getSettings,
39
39
  getUser: () => getUser,
40
40
  handleAuthCallback: () => handleAuthCallback,
41
+ hydrateSession: () => hydrateSession,
41
42
  isAuthenticated: () => isAuthenticated,
42
43
  login: () => login,
43
44
  logout: () => logout,
44
45
  oauthLogin: () => oauthLogin,
45
46
  onAuthChange: () => onAuthChange,
47
+ recoverPassword: () => recoverPassword,
46
48
  requestPasswordRecovery: () => requestPasswordRecovery,
47
49
  signup: () => signup,
48
50
  updateUser: () => updateUser,
@@ -68,13 +70,14 @@ var AuthError = class extends Error {
68
70
  }
69
71
  };
70
72
  var MissingIdentityError = class extends Error {
71
- constructor(message = "Identity is not available in this environment") {
73
+ constructor(message = "Netlify Identity is not available. Enable Identity in your site dashboard and use `netlify dev` for local development.") {
72
74
  super(message);
73
75
  this.name = "MissingIdentityError";
74
76
  }
75
77
  };
76
78
 
77
79
  // src/environment.ts
80
+ var IDENTITY_PATH = "/.netlify/identity";
78
81
  var goTrueClient = null;
79
82
  var cachedApiUrl;
80
83
  var warnedMissingUrl = false;
@@ -82,13 +85,15 @@ var isBrowser = () => typeof window !== "undefined" && typeof window.location !=
82
85
  var discoverApiUrl = () => {
83
86
  if (cachedApiUrl !== void 0) return cachedApiUrl;
84
87
  if (isBrowser()) {
85
- cachedApiUrl = `${window.location.origin}/.netlify/identity`;
88
+ cachedApiUrl = `${window.location.origin}${IDENTITY_PATH}`;
86
89
  } else {
87
90
  const identityContext = getIdentityContext();
88
91
  if (identityContext?.url) {
89
92
  cachedApiUrl = identityContext.url;
90
93
  } else if (globalThis.Netlify?.context?.url) {
91
- cachedApiUrl = new URL("/.netlify/identity", globalThis.Netlify.context.url).href;
94
+ cachedApiUrl = new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href;
95
+ } else if (process.env.URL) {
96
+ cachedApiUrl = new URL(IDENTITY_PATH, process.env.URL).href;
92
97
  }
93
98
  }
94
99
  return cachedApiUrl ?? null;
@@ -105,7 +110,7 @@ var getGoTrueClient = () => {
105
110
  }
106
111
  return null;
107
112
  }
108
- goTrueClient = new import_gotrue_js.default({ APIUrl: apiUrl, setCookie: isBrowser() });
113
+ goTrueClient = new import_gotrue_js.default({ APIUrl: apiUrl, setCookie: false });
109
114
  return goTrueClient;
110
115
  };
111
116
  var getClient = () => {
@@ -115,18 +120,97 @@ var getClient = () => {
115
120
  };
116
121
  var getIdentityContext = () => {
117
122
  const identityContext = globalThis.netlifyIdentityContext;
118
- if (identityContext?.url && typeof identityContext.url === "string") {
123
+ if (identityContext?.url) {
119
124
  return {
120
125
  url: identityContext.url,
121
- token: typeof identityContext.token === "string" ? identityContext.token : void 0
126
+ token: identityContext.token
122
127
  };
123
128
  }
124
129
  if (globalThis.Netlify?.context?.url) {
125
- return { url: new URL("/.netlify/identity", globalThis.Netlify.context.url).href };
130
+ return { url: new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href };
131
+ }
132
+ const siteUrl = process.env.URL;
133
+ if (siteUrl) {
134
+ return { url: new URL(IDENTITY_PATH, siteUrl).href };
126
135
  }
127
136
  return null;
128
137
  };
129
138
 
139
+ // src/cookies.ts
140
+ var NF_JWT_COOKIE = "nf_jwt";
141
+ var NF_REFRESH_COOKIE = "nf_refresh";
142
+ var getCookie = (name) => {
143
+ const match = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=([^;]*)`));
144
+ return match ? decodeURIComponent(match[1]) : null;
145
+ };
146
+ var setAuthCookies = (cookies, accessToken, refreshToken) => {
147
+ cookies.set({
148
+ name: NF_JWT_COOKIE,
149
+ value: accessToken,
150
+ httpOnly: false,
151
+ secure: true,
152
+ path: "/",
153
+ sameSite: "Lax"
154
+ });
155
+ if (refreshToken) {
156
+ cookies.set({
157
+ name: NF_REFRESH_COOKIE,
158
+ value: refreshToken,
159
+ httpOnly: false,
160
+ secure: true,
161
+ path: "/",
162
+ sameSite: "Lax"
163
+ });
164
+ }
165
+ };
166
+ var deleteAuthCookies = (cookies) => {
167
+ cookies.delete(NF_JWT_COOKIE);
168
+ cookies.delete(NF_REFRESH_COOKIE);
169
+ };
170
+ var setBrowserAuthCookies = (accessToken, refreshToken) => {
171
+ document.cookie = `${NF_JWT_COOKIE}=${encodeURIComponent(accessToken)}; path=/; secure; samesite=lax`;
172
+ if (refreshToken) {
173
+ document.cookie = `${NF_REFRESH_COOKIE}=${encodeURIComponent(refreshToken)}; path=/; secure; samesite=lax`;
174
+ }
175
+ };
176
+ var deleteBrowserAuthCookies = () => {
177
+ document.cookie = `${NF_JWT_COOKIE}=; path=/; secure; samesite=lax; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
178
+ document.cookie = `${NF_REFRESH_COOKIE}=; path=/; secure; samesite=lax; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
179
+ };
180
+ var getServerCookie = (name) => {
181
+ const cookies = globalThis.Netlify?.context?.cookies;
182
+ if (!cookies || typeof cookies.get !== "function") return null;
183
+ return cookies.get(name) ?? null;
184
+ };
185
+
186
+ // src/nextjs.ts
187
+ var nextHeadersFn;
188
+ var triggerNextjsDynamic = () => {
189
+ if (nextHeadersFn === null) return;
190
+ if (nextHeadersFn === void 0) {
191
+ try {
192
+ if (typeof require === "undefined") {
193
+ nextHeadersFn = null;
194
+ return;
195
+ }
196
+ const mod = require("next/headers");
197
+ nextHeadersFn = mod.headers;
198
+ } catch {
199
+ nextHeadersFn = null;
200
+ return;
201
+ }
202
+ }
203
+ const fn = nextHeadersFn;
204
+ if (!fn) return;
205
+ try {
206
+ fn();
207
+ } catch (e) {
208
+ if (e instanceof Error && ("digest" in e || /bail\s*out.*prerende/i.test(e.message))) {
209
+ throw e;
210
+ }
211
+ }
212
+ };
213
+
130
214
  // src/user.ts
131
215
  var toAuthProvider = (value) => typeof value === "string" && AUTH_PROVIDERS.includes(value) ? value : void 0;
132
216
  var toUser = (userData) => {
@@ -152,32 +236,92 @@ var claimsToUser = (claims) => {
152
236
  const userMeta = claims.user_metadata ?? {};
153
237
  const name = userMeta.full_name || userMeta.name;
154
238
  return {
155
- id: typeof claims.sub === "string" ? claims.sub : "",
156
- email: typeof claims.email === "string" ? claims.email : void 0,
239
+ id: claims.sub ?? "",
240
+ email: claims.email,
157
241
  provider: toAuthProvider(appMeta.provider),
158
242
  name: typeof name === "string" ? name : void 0,
159
243
  metadata: userMeta
160
244
  };
161
245
  };
246
+ var hydrating = false;
247
+ var backgroundHydrate = (accessToken) => {
248
+ if (hydrating) return;
249
+ hydrating = true;
250
+ const refreshToken = getCookie(NF_REFRESH_COOKIE) ?? "";
251
+ const decoded = decodeJwtPayload(accessToken);
252
+ const expiresAt = decoded?.exp ?? Math.floor(Date.now() / 1e3) + 3600;
253
+ const expiresIn = Math.max(0, expiresAt - Math.floor(Date.now() / 1e3));
254
+ setTimeout(() => {
255
+ try {
256
+ const client = getClient();
257
+ client.createUser(
258
+ {
259
+ access_token: accessToken,
260
+ token_type: "bearer",
261
+ expires_in: expiresIn,
262
+ expires_at: expiresAt,
263
+ refresh_token: refreshToken
264
+ },
265
+ true
266
+ ).catch(() => {
267
+ }).finally(() => {
268
+ hydrating = false;
269
+ });
270
+ } catch {
271
+ hydrating = false;
272
+ }
273
+ }, 0);
274
+ };
275
+ var decodeJwtPayload = (token) => {
276
+ try {
277
+ const parts = token.split(".");
278
+ if (parts.length !== 3) return null;
279
+ const payload = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
280
+ return JSON.parse(payload);
281
+ } catch {
282
+ return null;
283
+ }
284
+ };
162
285
  var getUser = () => {
163
286
  if (isBrowser()) {
164
287
  const client = getGoTrueClient();
165
288
  const currentUser = client?.currentUser() ?? null;
166
- if (!currentUser) return null;
167
- return toUser(currentUser);
289
+ if (currentUser) {
290
+ const jwt2 = getCookie(NF_JWT_COOKIE);
291
+ if (!jwt2) {
292
+ try {
293
+ currentUser.clearSession();
294
+ } catch {
295
+ }
296
+ return null;
297
+ }
298
+ return toUser(currentUser);
299
+ }
300
+ const jwt = getCookie(NF_JWT_COOKIE);
301
+ if (!jwt) return null;
302
+ const claims = decodeJwtPayload(jwt);
303
+ if (!claims) return null;
304
+ backgroundHydrate(jwt);
305
+ return claimsToUser(claims);
168
306
  }
307
+ triggerNextjsDynamic();
169
308
  const identityContext = globalThis.netlifyIdentityContext;
170
- if (!identityContext) return null;
171
- const claims = identityContext.user ?? (identityContext.sub ? identityContext : null);
172
- if (!claims) return null;
173
- return claimsToUser(claims);
309
+ if (identityContext?.user) {
310
+ return claimsToUser(identityContext.user);
311
+ }
312
+ const serverJwt = getServerCookie(NF_JWT_COOKIE);
313
+ if (serverJwt) {
314
+ const claims = decodeJwtPayload(serverJwt);
315
+ if (claims) return claimsToUser(claims);
316
+ }
317
+ return null;
174
318
  };
175
319
  var isAuthenticated = () => getUser() !== null;
176
320
 
177
321
  // src/config.ts
178
322
  var getIdentityConfig = () => {
179
323
  if (isBrowser()) {
180
- return { url: `${window.location.origin}/.netlify/identity` };
324
+ return { url: `${window.location.origin}${IDENTITY_PATH}` };
181
325
  }
182
326
  return getIdentityContext();
183
327
  };
@@ -205,19 +349,37 @@ var getSettings = async () => {
205
349
  };
206
350
 
207
351
  // src/auth.ts
352
+ var getCookies = () => {
353
+ const cookies = globalThis.Netlify?.context?.cookies;
354
+ if (!cookies) {
355
+ throw new AuthError("Server-side auth requires Netlify Functions runtime");
356
+ }
357
+ return cookies;
358
+ };
359
+ var getServerIdentityUrl = () => {
360
+ const ctx = getIdentityContext();
361
+ if (!ctx?.url) {
362
+ throw new AuthError("Could not determine the Identity endpoint URL on the server");
363
+ }
364
+ return ctx.url;
365
+ };
208
366
  var persistSession = true;
209
367
  var listeners = /* @__PURE__ */ new Set();
210
368
  var emitAuthEvent = (event, user) => {
211
369
  for (const listener of listeners) {
212
- listener(event, user);
370
+ try {
371
+ listener(event, user);
372
+ } catch {
373
+ }
213
374
  }
214
375
  };
376
+ var GOTRUE_STORAGE_KEY = "gotrue.user";
215
377
  var storageListenerAttached = false;
216
378
  var attachStorageListener = () => {
217
379
  if (storageListenerAttached) return;
218
380
  storageListenerAttached = true;
219
381
  window.addEventListener("storage", (event) => {
220
- if (event.key !== "gotrue.user") return;
382
+ if (event.key !== GOTRUE_STORAGE_KEY) return;
221
383
  if (event.newValue) {
222
384
  const client = getGoTrueClient();
223
385
  const currentUser = client?.currentUser();
@@ -239,9 +401,58 @@ var onAuthChange = (callback) => {
239
401
  };
240
402
  };
241
403
  var login = async (email, password) => {
404
+ if (!isBrowser()) {
405
+ const identityUrl = getServerIdentityUrl();
406
+ const cookies = getCookies();
407
+ const body = new URLSearchParams({
408
+ grant_type: "password",
409
+ username: email,
410
+ password
411
+ });
412
+ let res;
413
+ try {
414
+ res = await fetch(`${identityUrl}/token`, {
415
+ method: "POST",
416
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
417
+ body: body.toString()
418
+ });
419
+ } catch (error) {
420
+ throw new AuthError(error.message, void 0, { cause: error });
421
+ }
422
+ if (!res.ok) {
423
+ const errorBody = await res.json().catch(() => ({}));
424
+ throw new AuthError(
425
+ errorBody.msg || errorBody.error_description || `Login failed (${res.status})`,
426
+ res.status
427
+ );
428
+ }
429
+ const data = await res.json();
430
+ const accessToken = data.access_token;
431
+ let userRes;
432
+ try {
433
+ userRes = await fetch(`${identityUrl}/user`, {
434
+ headers: { Authorization: `Bearer ${accessToken}` }
435
+ });
436
+ } catch (error) {
437
+ throw new AuthError(error.message, void 0, { cause: error });
438
+ }
439
+ if (!userRes.ok) {
440
+ const errorBody = await userRes.json().catch(() => ({}));
441
+ throw new AuthError(
442
+ errorBody.msg || `Failed to fetch user data (${userRes.status})`,
443
+ userRes.status
444
+ );
445
+ }
446
+ const userData = await userRes.json();
447
+ const user = toUser(userData);
448
+ setAuthCookies(cookies, accessToken, data.refresh_token);
449
+ return user;
450
+ }
242
451
  const client = getClient();
243
452
  try {
244
453
  const gotrueUser = await client.login(email, password, persistSession);
454
+ const jwt = await gotrueUser.jwt();
455
+ setBrowserAuthCookies(jwt);
245
456
  const user = toUser(gotrueUser);
246
457
  emitAuthEvent("login", user);
247
458
  return user;
@@ -250,11 +461,43 @@ var login = async (email, password) => {
250
461
  }
251
462
  };
252
463
  var signup = async (email, password, data) => {
464
+ if (!isBrowser()) {
465
+ const identityUrl = getServerIdentityUrl();
466
+ const cookies = getCookies();
467
+ let res;
468
+ try {
469
+ res = await fetch(`${identityUrl}/signup`, {
470
+ method: "POST",
471
+ headers: { "Content-Type": "application/json" },
472
+ body: JSON.stringify({ email, password, data })
473
+ });
474
+ } catch (error) {
475
+ throw new AuthError(error.message, void 0, { cause: error });
476
+ }
477
+ if (!res.ok) {
478
+ const errorBody = await res.json().catch(() => ({}));
479
+ throw new AuthError(errorBody.msg || `Signup failed (${res.status})`, res.status);
480
+ }
481
+ const responseData = await res.json();
482
+ const user = toUser(responseData);
483
+ if (responseData.confirmed_at) {
484
+ const responseRecord = responseData;
485
+ const accessToken = responseRecord.access_token;
486
+ if (accessToken) {
487
+ setAuthCookies(cookies, accessToken, responseRecord.refresh_token);
488
+ }
489
+ }
490
+ return user;
491
+ }
253
492
  const client = getClient();
254
493
  try {
255
494
  const response = await client.signup(email, password, data);
256
495
  const user = toUser(response);
257
496
  if (response.confirmed_at) {
497
+ const jwt = await response.jwt?.();
498
+ if (jwt) {
499
+ setBrowserAuthCookies(jwt);
500
+ }
258
501
  emitAuthEvent("login", user);
259
502
  }
260
503
  return user;
@@ -263,12 +506,29 @@ var signup = async (email, password, data) => {
263
506
  }
264
507
  };
265
508
  var logout = async () => {
509
+ if (!isBrowser()) {
510
+ const identityUrl = getServerIdentityUrl();
511
+ const cookies = getCookies();
512
+ const jwt = cookies.get(NF_JWT_COOKIE);
513
+ if (jwt) {
514
+ try {
515
+ await fetch(`${identityUrl}/logout`, {
516
+ method: "POST",
517
+ headers: { Authorization: `Bearer ${jwt}` }
518
+ });
519
+ } catch {
520
+ }
521
+ }
522
+ deleteAuthCookies(cookies);
523
+ return;
524
+ }
266
525
  const client = getClient();
267
526
  try {
268
527
  const currentUser = client.currentUser();
269
528
  if (currentUser) {
270
529
  await currentUser.logout();
271
530
  }
531
+ deleteBrowserAuthCookies();
272
532
  emitAuthEvent("logout", null);
273
533
  } catch (error) {
274
534
  throw new AuthError(error.message, void 0, { cause: error });
@@ -276,11 +536,11 @@ var logout = async () => {
276
536
  };
277
537
  var oauthLogin = (provider) => {
278
538
  if (!isBrowser()) {
279
- throw new Error("oauthLogin() is only available in the browser");
539
+ throw new AuthError("oauthLogin() is only available in the browser");
280
540
  }
281
541
  const client = getClient();
282
542
  window.location.href = client.loginExternalUrl(provider);
283
- throw new Error("Redirecting to OAuth provider");
543
+ throw new AuthError("Redirecting to OAuth provider");
284
544
  };
285
545
  var handleAuthCallback = async () => {
286
546
  if (!isBrowser()) return null;
@@ -291,16 +551,18 @@ var handleAuthCallback = async () => {
291
551
  const params = new URLSearchParams(hash);
292
552
  const accessToken = params.get("access_token");
293
553
  if (accessToken) {
554
+ const refreshToken = params.get("refresh_token") ?? "";
294
555
  const gotrueUser = await client.createUser(
295
556
  {
296
557
  access_token: accessToken,
297
558
  token_type: params.get("token_type") ?? "bearer",
298
559
  expires_in: Number(params.get("expires_in")),
299
560
  expires_at: Number(params.get("expires_at")),
300
- refresh_token: params.get("refresh_token") ?? ""
561
+ refresh_token: refreshToken
301
562
  },
302
563
  persistSession
303
564
  );
565
+ setBrowserAuthCookies(accessToken, refreshToken || void 0);
304
566
  const user = toUser(gotrueUser);
305
567
  clearHash();
306
568
  emitAuthEvent("login", user);
@@ -309,6 +571,8 @@ var handleAuthCallback = async () => {
309
571
  const confirmationToken = params.get("confirmation_token");
310
572
  if (confirmationToken) {
311
573
  const gotrueUser = await client.confirm(confirmationToken, persistSession);
574
+ const jwt = await gotrueUser.jwt();
575
+ setBrowserAuthCookies(jwt);
312
576
  const user = toUser(gotrueUser);
313
577
  clearHash();
314
578
  emitAuthEvent("login", user);
@@ -317,6 +581,8 @@ var handleAuthCallback = async () => {
317
581
  const recoveryToken = params.get("recovery_token");
318
582
  if (recoveryToken) {
319
583
  const gotrueUser = await client.recover(recoveryToken, persistSession);
584
+ const jwt = await gotrueUser.jwt();
585
+ setBrowserAuthCookies(jwt);
320
586
  const user = toUser(gotrueUser);
321
587
  clearHash();
322
588
  emitAuthEvent("login", user);
@@ -331,25 +597,80 @@ var handleAuthCallback = async () => {
331
597
  if (emailChangeToken) {
332
598
  const currentUser = client.currentUser();
333
599
  if (!currentUser) {
334
- clearHash();
335
- return { type: "email_change", user: null, token: emailChangeToken };
600
+ throw new AuthError("Email change verification requires an active browser session");
336
601
  }
337
- const gotrueUser = await currentUser.update({ email_change_token: emailChangeToken });
338
- const user = toUser(gotrueUser);
602
+ const jwt = await currentUser.jwt();
603
+ const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
604
+ const emailChangeRes = await fetch(`${identityUrl}/user`, {
605
+ method: "PUT",
606
+ headers: {
607
+ "Content-Type": "application/json",
608
+ Authorization: `Bearer ${jwt}`
609
+ },
610
+ body: JSON.stringify({ email_change_token: emailChangeToken })
611
+ });
612
+ if (!emailChangeRes.ok) {
613
+ const errorBody = await emailChangeRes.json().catch(() => ({}));
614
+ throw new AuthError(
615
+ errorBody.msg || `Email change verification failed (${emailChangeRes.status})`,
616
+ emailChangeRes.status
617
+ );
618
+ }
619
+ const emailChangeData = await emailChangeRes.json();
620
+ const user = toUser(emailChangeData);
339
621
  clearHash();
340
622
  emitAuthEvent("user_updated", user);
341
623
  return { type: "email_change", user };
342
624
  }
343
625
  return null;
344
626
  } catch (error) {
627
+ if (error instanceof AuthError) throw error;
345
628
  throw new AuthError(error.message, void 0, { cause: error });
346
629
  }
347
630
  };
348
631
  var clearHash = () => {
349
632
  history.replaceState(null, "", window.location.pathname + window.location.search);
350
633
  };
634
+ var hydrateSession = async () => {
635
+ if (!isBrowser()) return null;
636
+ const client = getClient();
637
+ const currentUser = client.currentUser();
638
+ if (currentUser) return toUser(currentUser);
639
+ const accessToken = getCookie(NF_JWT_COOKIE);
640
+ if (!accessToken) return null;
641
+ const refreshToken = getCookie(NF_REFRESH_COOKIE) ?? "";
642
+ const decoded = decodeJwtPayload(accessToken);
643
+ const expiresAt = decoded?.exp ?? Math.floor(Date.now() / 1e3) + 3600;
644
+ const expiresIn = Math.max(0, expiresAt - Math.floor(Date.now() / 1e3));
645
+ const gotrueUser = await client.createUser(
646
+ {
647
+ access_token: accessToken,
648
+ token_type: "bearer",
649
+ expires_in: expiresIn,
650
+ expires_at: expiresAt,
651
+ refresh_token: refreshToken
652
+ },
653
+ persistSession
654
+ );
655
+ const user = toUser(gotrueUser);
656
+ emitAuthEvent("login", user);
657
+ return user;
658
+ };
351
659
 
352
660
  // src/account.ts
661
+ var ensureCurrentUser = async () => {
662
+ const client = getClient();
663
+ let currentUser = client.currentUser();
664
+ if (!currentUser && isBrowser()) {
665
+ try {
666
+ await hydrateSession();
667
+ } catch {
668
+ }
669
+ currentUser = client.currentUser();
670
+ }
671
+ if (!currentUser) throw new AuthError("No user is currently logged in");
672
+ return currentUser;
673
+ };
353
674
  var requestPasswordRecovery = async (email) => {
354
675
  const client = getClient();
355
676
  try {
@@ -358,6 +679,18 @@ var requestPasswordRecovery = async (email) => {
358
679
  throw new AuthError(error.message, void 0, { cause: error });
359
680
  }
360
681
  };
682
+ var recoverPassword = async (token, newPassword) => {
683
+ const client = getClient();
684
+ try {
685
+ const gotrueUser = await client.recover(token, persistSession);
686
+ const updatedUser = await gotrueUser.update({ password: newPassword });
687
+ const user = toUser(updatedUser);
688
+ emitAuthEvent("login", user);
689
+ return user;
690
+ } catch (error) {
691
+ throw new AuthError(error.message, void 0, { cause: error });
692
+ }
693
+ };
361
694
  var confirmEmail = async (token) => {
362
695
  const client = getClient();
363
696
  try {
@@ -381,22 +714,37 @@ var acceptInvite = async (token, password) => {
381
714
  }
382
715
  };
383
716
  var verifyEmailChange = async (token) => {
384
- const client = getClient();
385
- const currentUser = client.currentUser();
386
- if (!currentUser) throw new AuthError("No user is currently logged in");
717
+ if (!isBrowser()) throw new AuthError("verifyEmailChange() is only available in the browser");
718
+ const currentUser = await ensureCurrentUser();
719
+ const jwt = await currentUser.jwt();
720
+ const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
387
721
  try {
388
- const gotrueUser = await currentUser.update({ email_change_token: token });
389
- const user = toUser(gotrueUser);
722
+ const res = await fetch(`${identityUrl}/user`, {
723
+ method: "PUT",
724
+ headers: {
725
+ "Content-Type": "application/json",
726
+ Authorization: `Bearer ${jwt}`
727
+ },
728
+ body: JSON.stringify({ email_change_token: token })
729
+ });
730
+ if (!res.ok) {
731
+ const errorBody = await res.json().catch(() => ({}));
732
+ throw new AuthError(
733
+ errorBody.msg || `Email change verification failed (${res.status})`,
734
+ res.status
735
+ );
736
+ }
737
+ const userData = await res.json();
738
+ const user = toUser(userData);
390
739
  emitAuthEvent("user_updated", user);
391
740
  return user;
392
741
  } catch (error) {
742
+ if (error instanceof AuthError) throw error;
393
743
  throw new AuthError(error.message, void 0, { cause: error });
394
744
  }
395
745
  };
396
746
  var updateUser = async (updates) => {
397
- const client = getClient();
398
- const currentUser = client.currentUser();
399
- if (!currentUser) throw new AuthError("No user is currently logged in");
747
+ const currentUser = await ensureCurrentUser();
400
748
  try {
401
749
  const updatedUser = await currentUser.update(updates);
402
750
  const user = toUser(updatedUser);
@@ -416,11 +764,13 @@ var updateUser = async (updates) => {
416
764
  getSettings,
417
765
  getUser,
418
766
  handleAuthCallback,
767
+ hydrateSession,
419
768
  isAuthenticated,
420
769
  login,
421
770
  logout,
422
771
  oauthLogin,
423
772
  onAuthChange,
773
+ recoverPassword,
424
774
  requestPasswordRecovery,
425
775
  signup,
426
776
  updateUser,