@netlify/identity 0.1.1-alpha.9 → 0.2.0

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
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ AUTH_EVENTS: () => AUTH_EVENTS,
33
34
  AuthError: () => AuthError,
34
35
  MissingIdentityError: () => MissingIdentityError,
35
36
  acceptInvite: () => acceptInvite,
@@ -38,6 +39,7 @@ __export(index_exports, {
38
39
  getSettings: () => getSettings,
39
40
  getUser: () => getUser,
40
41
  handleAuthCallback: () => handleAuthCallback,
42
+ hydrateSession: () => hydrateSession,
41
43
  isAuthenticated: () => isAuthenticated,
42
44
  login: () => login,
43
45
  logout: () => logout,
@@ -58,7 +60,7 @@ var AUTH_PROVIDERS = ["google", "github", "gitlab", "bitbucket", "facebook", "sa
58
60
  var import_gotrue_js = __toESM(require("gotrue-js"), 1);
59
61
 
60
62
  // src/errors.ts
61
- var AuthError = class extends Error {
63
+ var AuthError = class _AuthError extends Error {
62
64
  constructor(message, status, options) {
63
65
  super(message);
64
66
  this.name = "AuthError";
@@ -67,9 +69,14 @@ var AuthError = class extends Error {
67
69
  this.cause = options.cause;
68
70
  }
69
71
  }
72
+ static from(error) {
73
+ if (error instanceof _AuthError) return error;
74
+ const message = error instanceof Error ? error.message : String(error);
75
+ return new _AuthError(message, void 0, { cause: error });
76
+ }
70
77
  };
71
78
  var MissingIdentityError = class extends Error {
72
- constructor(message = "Identity is not available in this environment") {
79
+ constructor(message = "Netlify Identity is not available.") {
73
80
  super(message);
74
81
  this.name = "MissingIdentityError";
75
82
  }
@@ -91,6 +98,8 @@ var discoverApiUrl = () => {
91
98
  cachedApiUrl = identityContext.url;
92
99
  } else if (globalThis.Netlify?.context?.url) {
93
100
  cachedApiUrl = new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href;
101
+ } else if (typeof process !== "undefined" && process.env?.URL) {
102
+ cachedApiUrl = new URL(IDENTITY_PATH, process.env.URL).href;
94
103
  }
95
104
  }
96
105
  return cachedApiUrl ?? null;
@@ -107,7 +116,7 @@ var getGoTrueClient = () => {
107
116
  }
108
117
  return null;
109
118
  }
110
- goTrueClient = new import_gotrue_js.default({ APIUrl: apiUrl, setCookie: isBrowser() });
119
+ goTrueClient = new import_gotrue_js.default({ APIUrl: apiUrl, setCookie: false });
111
120
  return goTrueClient;
112
121
  };
113
122
  var getClient = () => {
@@ -126,6 +135,10 @@ var getIdentityContext = () => {
126
135
  if (globalThis.Netlify?.context?.url) {
127
136
  return { url: new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href };
128
137
  }
138
+ const siteUrl = typeof process !== "undefined" ? process.env?.URL : void 0;
139
+ if (siteUrl) {
140
+ return { url: new URL(IDENTITY_PATH, siteUrl).href };
141
+ }
129
142
  return null;
130
143
  };
131
144
 
@@ -133,8 +146,14 @@ var getIdentityContext = () => {
133
146
  var NF_JWT_COOKIE = "nf_jwt";
134
147
  var NF_REFRESH_COOKIE = "nf_refresh";
135
148
  var getCookie = (name) => {
149
+ if (typeof document === "undefined") return null;
136
150
  const match = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=([^;]*)`));
137
- return match ? decodeURIComponent(match[1]) : null;
151
+ if (!match) return null;
152
+ try {
153
+ return decodeURIComponent(match[1]);
154
+ } catch {
155
+ return match[1];
156
+ }
138
157
  };
139
158
  var setAuthCookies = (cookies, accessToken, refreshToken) => {
140
159
  cookies.set({
@@ -161,14 +180,16 @@ var deleteAuthCookies = (cookies) => {
161
180
  cookies.delete(NF_REFRESH_COOKIE);
162
181
  };
163
182
  var setBrowserAuthCookies = (accessToken, refreshToken) => {
183
+ if (typeof document === "undefined") return;
164
184
  document.cookie = `${NF_JWT_COOKIE}=${encodeURIComponent(accessToken)}; path=/; secure; samesite=lax`;
165
185
  if (refreshToken) {
166
186
  document.cookie = `${NF_REFRESH_COOKIE}=${encodeURIComponent(refreshToken)}; path=/; secure; samesite=lax`;
167
187
  }
168
188
  };
169
189
  var deleteBrowserAuthCookies = () => {
170
- document.cookie = `${NF_JWT_COOKIE}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
171
- document.cookie = `${NF_REFRESH_COOKIE}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
190
+ if (typeof document === "undefined") return;
191
+ document.cookie = `${NF_JWT_COOKIE}=; path=/; secure; samesite=lax; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
192
+ document.cookie = `${NF_REFRESH_COOKIE}=; path=/; secure; samesite=lax; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
172
193
  };
173
194
  var getServerCookie = (name) => {
174
195
  const cookies = globalThis.Netlify?.context?.cookies;
@@ -176,6 +197,34 @@ var getServerCookie = (name) => {
176
197
  return cookies.get(name) ?? null;
177
198
  };
178
199
 
200
+ // src/nextjs.ts
201
+ var nextHeadersFn;
202
+ var triggerNextjsDynamic = () => {
203
+ if (nextHeadersFn === null) return;
204
+ if (nextHeadersFn === void 0) {
205
+ try {
206
+ if (typeof require === "undefined") {
207
+ nextHeadersFn = null;
208
+ return;
209
+ }
210
+ const mod = require("next/headers");
211
+ nextHeadersFn = mod.headers;
212
+ } catch {
213
+ nextHeadersFn = null;
214
+ return;
215
+ }
216
+ }
217
+ const fn = nextHeadersFn;
218
+ if (!fn) return;
219
+ try {
220
+ fn();
221
+ } catch (e) {
222
+ if (e instanceof Error && ("digest" in e || /bail\s*out.*prerende/i.test(e.message))) {
223
+ throw e;
224
+ }
225
+ }
226
+ };
227
+
179
228
  // src/user.ts
180
229
  var toAuthProvider = (value) => typeof value === "string" && AUTH_PROVIDERS.includes(value) ? value : void 0;
181
230
  var toUser = (userData) => {
@@ -208,6 +257,35 @@ var claimsToUser = (claims) => {
208
257
  metadata: userMeta
209
258
  };
210
259
  };
260
+ var hydrating = false;
261
+ var backgroundHydrate = (accessToken) => {
262
+ if (hydrating) return;
263
+ hydrating = true;
264
+ const refreshToken = getCookie(NF_REFRESH_COOKIE) ?? "";
265
+ const decoded = decodeJwtPayload(accessToken);
266
+ const expiresAt = decoded?.exp ?? Math.floor(Date.now() / 1e3) + 3600;
267
+ const expiresIn = Math.max(0, expiresAt - Math.floor(Date.now() / 1e3));
268
+ setTimeout(() => {
269
+ try {
270
+ const client = getClient();
271
+ client.createUser(
272
+ {
273
+ access_token: accessToken,
274
+ token_type: "bearer",
275
+ expires_in: expiresIn,
276
+ expires_at: expiresAt,
277
+ refresh_token: refreshToken
278
+ },
279
+ true
280
+ ).catch(() => {
281
+ }).finally(() => {
282
+ hydrating = false;
283
+ });
284
+ } catch {
285
+ hydrating = false;
286
+ }
287
+ }, 0);
288
+ };
211
289
  var decodeJwtPayload = (token) => {
212
290
  try {
213
291
  const parts = token.split(".");
@@ -223,32 +301,31 @@ var getUser = () => {
223
301
  const client = getGoTrueClient();
224
302
  const currentUser = client?.currentUser() ?? null;
225
303
  if (currentUser) {
226
- const jwt3 = getCookie(NF_JWT_COOKIE);
227
- if (!jwt3) {
304
+ const jwt2 = getCookie(NF_JWT_COOKIE);
305
+ if (!jwt2) {
228
306
  try {
229
- currentUser.logout();
307
+ currentUser.clearSession();
230
308
  } catch {
231
309
  }
232
310
  return null;
233
311
  }
234
312
  return toUser(currentUser);
235
313
  }
236
- const jwt2 = getCookie(NF_JWT_COOKIE);
237
- if (!jwt2) return null;
238
- const claims = decodeJwtPayload(jwt2);
314
+ const jwt = getCookie(NF_JWT_COOKIE);
315
+ if (!jwt) return null;
316
+ const claims = decodeJwtPayload(jwt);
239
317
  if (!claims) return null;
318
+ backgroundHydrate(jwt);
240
319
  return claimsToUser(claims);
241
320
  }
321
+ triggerNextjsDynamic();
242
322
  const identityContext = globalThis.netlifyIdentityContext;
243
323
  if (identityContext?.user) {
244
324
  return claimsToUser(identityContext.user);
245
325
  }
246
- const jwt = getServerCookie(NF_JWT_COOKIE);
247
- if (jwt) {
248
- console.debug(
249
- `[@netlify/identity] getUser: no identityContext.user, but nf_jwt cookie found on request. Decoding JWT from cookie. (identityContext: ${identityContext ? `{ url: ${!!identityContext.url}, token: ${!!identityContext.token}, user: ${!!identityContext.user} }` : "null"}, Netlify.context: ${!!globalThis.Netlify?.context})`
250
- );
251
- const claims = decodeJwtPayload(jwt);
326
+ const serverJwt = getServerCookie(NF_JWT_COOKIE);
327
+ if (serverJwt) {
328
+ const claims = decodeJwtPayload(serverJwt);
252
329
  if (claims) return claimsToUser(claims);
253
330
  }
254
331
  return null;
@@ -285,40 +362,36 @@ var getSettings = async () => {
285
362
  }
286
363
  };
287
364
 
288
- // src/auth.ts
289
- var getCookies = () => {
290
- const cookies = globalThis.Netlify?.context?.cookies;
291
- if (!cookies) {
292
- throw new AuthError("Server-side auth requires Netlify Functions runtime");
293
- }
294
- return cookies;
295
- };
296
- var getServerIdentityUrl = () => {
297
- const ctx = getIdentityContext();
298
- if (!ctx?.url) {
299
- throw new AuthError("Could not determine the Identity endpoint URL on the server");
300
- }
301
- return ctx.url;
302
- };
303
- var persistSession = true;
365
+ // src/events.ts
366
+ var AUTH_EVENTS = {
367
+ LOGIN: "login",
368
+ LOGOUT: "logout",
369
+ TOKEN_REFRESH: "token_refresh",
370
+ USER_UPDATED: "user_updated",
371
+ RECOVERY: "recovery"
372
+ };
373
+ var GOTRUE_STORAGE_KEY = "gotrue.user";
304
374
  var listeners = /* @__PURE__ */ new Set();
305
375
  var emitAuthEvent = (event, user) => {
306
376
  for (const listener of listeners) {
307
- listener(event, user);
377
+ try {
378
+ listener(event, user);
379
+ } catch {
380
+ }
308
381
  }
309
382
  };
310
383
  var storageListenerAttached = false;
311
384
  var attachStorageListener = () => {
312
- if (storageListenerAttached) return;
385
+ if (storageListenerAttached || !isBrowser()) return;
313
386
  storageListenerAttached = true;
314
387
  window.addEventListener("storage", (event) => {
315
- if (event.key !== "gotrue.user") return;
388
+ if (event.key !== GOTRUE_STORAGE_KEY) return;
316
389
  if (event.newValue) {
317
390
  const client = getGoTrueClient();
318
391
  const currentUser = client?.currentUser();
319
- emitAuthEvent("login", currentUser ? toUser(currentUser) : null);
392
+ emitAuthEvent(AUTH_EVENTS.LOGIN, currentUser ? toUser(currentUser) : null);
320
393
  } else {
321
- emitAuthEvent("logout", null);
394
+ emitAuthEvent(AUTH_EVENTS.LOGOUT, null);
322
395
  }
323
396
  });
324
397
  };
@@ -333,6 +406,23 @@ var onAuthChange = (callback) => {
333
406
  listeners.delete(callback);
334
407
  };
335
408
  };
409
+
410
+ // src/auth.ts
411
+ var getCookies = () => {
412
+ const cookies = globalThis.Netlify?.context?.cookies;
413
+ if (!cookies) {
414
+ throw new AuthError("Server-side auth requires Netlify Functions runtime");
415
+ }
416
+ return cookies;
417
+ };
418
+ var getServerIdentityUrl = () => {
419
+ const ctx = getIdentityContext();
420
+ if (!ctx?.url) {
421
+ throw new AuthError("Could not determine the Identity endpoint URL on the server");
422
+ }
423
+ return ctx.url;
424
+ };
425
+ var persistSession = true;
336
426
  var login = async (email, password) => {
337
427
  if (!isBrowser()) {
338
428
  const identityUrl = getServerIdentityUrl();
@@ -350,14 +440,11 @@ var login = async (email, password) => {
350
440
  body: body.toString()
351
441
  });
352
442
  } catch (error) {
353
- throw new AuthError(error.message, void 0, { cause: error });
443
+ throw AuthError.from(error);
354
444
  }
355
445
  if (!res.ok) {
356
446
  const errorBody = await res.json().catch(() => ({}));
357
- throw new AuthError(
358
- errorBody.msg || errorBody.error_description || `Login failed (${res.status})`,
359
- res.status
360
- );
447
+ throw new AuthError(errorBody.msg || errorBody.error_description || `Login failed (${res.status})`, res.status);
361
448
  }
362
449
  const data = await res.json();
363
450
  const accessToken = data.access_token;
@@ -367,14 +454,11 @@ var login = async (email, password) => {
367
454
  headers: { Authorization: `Bearer ${accessToken}` }
368
455
  });
369
456
  } catch (error) {
370
- throw new AuthError(error.message, void 0, { cause: error });
457
+ throw AuthError.from(error);
371
458
  }
372
459
  if (!userRes.ok) {
373
460
  const errorBody = await userRes.json().catch(() => ({}));
374
- throw new AuthError(
375
- errorBody.msg || `Failed to fetch user data (${userRes.status})`,
376
- userRes.status
377
- );
461
+ throw new AuthError(errorBody.msg || `Failed to fetch user data (${userRes.status})`, userRes.status);
378
462
  }
379
463
  const userData = await userRes.json();
380
464
  const user = toUser(userData);
@@ -385,12 +469,12 @@ var login = async (email, password) => {
385
469
  try {
386
470
  const gotrueUser = await client.login(email, password, persistSession);
387
471
  const jwt = await gotrueUser.jwt();
388
- setBrowserAuthCookies(jwt);
472
+ setBrowserAuthCookies(jwt, gotrueUser.tokenDetails()?.refresh_token);
389
473
  const user = toUser(gotrueUser);
390
- emitAuthEvent("login", user);
474
+ emitAuthEvent(AUTH_EVENTS.LOGIN, user);
391
475
  return user;
392
476
  } catch (error) {
393
- throw new AuthError(error.message, void 0, { cause: error });
477
+ throw AuthError.from(error);
394
478
  }
395
479
  };
396
480
  var signup = async (email, password, data) => {
@@ -405,7 +489,7 @@ var signup = async (email, password, data) => {
405
489
  body: JSON.stringify({ email, password, data })
406
490
  });
407
491
  } catch (error) {
408
- throw new AuthError(error.message, void 0, { cause: error });
492
+ throw AuthError.from(error);
409
493
  }
410
494
  if (!res.ok) {
411
495
  const errorBody = await res.json().catch(() => ({}));
@@ -414,10 +498,9 @@ var signup = async (email, password, data) => {
414
498
  const responseData = await res.json();
415
499
  const user = toUser(responseData);
416
500
  if (responseData.confirmed_at) {
417
- const responseRecord = responseData;
418
- const accessToken = responseRecord.access_token;
501
+ const accessToken = responseData.access_token;
419
502
  if (accessToken) {
420
- setAuthCookies(cookies, accessToken, responseRecord.refresh_token);
503
+ setAuthCookies(cookies, accessToken, responseData.refresh_token);
421
504
  }
422
505
  }
423
506
  return user;
@@ -427,11 +510,16 @@ var signup = async (email, password, data) => {
427
510
  const response = await client.signup(email, password, data);
428
511
  const user = toUser(response);
429
512
  if (response.confirmed_at) {
430
- emitAuthEvent("login", user);
513
+ const jwt = await response.jwt?.();
514
+ if (jwt) {
515
+ const refreshToken = response.tokenDetails?.()?.refresh_token;
516
+ setBrowserAuthCookies(jwt, refreshToken);
517
+ }
518
+ emitAuthEvent(AUTH_EVENTS.LOGIN, user);
431
519
  }
432
520
  return user;
433
521
  } catch (error) {
434
- throw new AuthError(error.message, void 0, { cause: error });
522
+ throw AuthError.from(error);
435
523
  }
436
524
  };
437
525
  var logout = async () => {
@@ -445,8 +533,7 @@ var logout = async () => {
445
533
  method: "POST",
446
534
  headers: { Authorization: `Bearer ${jwt}` }
447
535
  });
448
- } catch (error) {
449
- throw new AuthError(error.message, void 0, { cause: error });
536
+ } catch {
450
537
  }
451
538
  }
452
539
  deleteAuthCookies(cookies);
@@ -459,103 +546,111 @@ var logout = async () => {
459
546
  await currentUser.logout();
460
547
  }
461
548
  deleteBrowserAuthCookies();
462
- emitAuthEvent("logout", null);
549
+ emitAuthEvent(AUTH_EVENTS.LOGOUT, null);
463
550
  } catch (error) {
464
- throw new AuthError(error.message, void 0, { cause: error });
551
+ throw AuthError.from(error);
465
552
  }
466
553
  };
467
554
  var oauthLogin = (provider) => {
468
555
  if (!isBrowser()) {
469
- throw new Error("oauthLogin() is only available in the browser");
556
+ throw new AuthError("oauthLogin() is only available in the browser");
470
557
  }
471
558
  const client = getClient();
472
559
  window.location.href = client.loginExternalUrl(provider);
473
- throw new Error("Redirecting to OAuth provider");
560
+ throw new AuthError("Redirecting to OAuth provider");
474
561
  };
475
562
  var handleAuthCallback = async () => {
476
563
  if (!isBrowser()) return null;
477
564
  const hash = window.location.hash.substring(1);
478
565
  if (!hash) return null;
479
566
  const client = getClient();
567
+ const params = new URLSearchParams(hash);
480
568
  try {
481
- const params = new URLSearchParams(hash);
482
569
  const accessToken = params.get("access_token");
483
- if (accessToken) {
484
- const refreshToken = params.get("refresh_token") ?? "";
485
- const gotrueUser = await client.createUser(
486
- {
487
- access_token: accessToken,
488
- token_type: params.get("token_type") ?? "bearer",
489
- expires_in: Number(params.get("expires_in")),
490
- expires_at: Number(params.get("expires_at")),
491
- refresh_token: refreshToken
492
- },
493
- persistSession
494
- );
495
- setBrowserAuthCookies(accessToken, refreshToken || void 0);
496
- const user = toUser(gotrueUser);
497
- clearHash();
498
- emitAuthEvent("login", user);
499
- return { type: "oauth", user };
500
- }
570
+ if (accessToken) return await handleOAuthCallback(client, params, accessToken);
501
571
  const confirmationToken = params.get("confirmation_token");
502
- if (confirmationToken) {
503
- const gotrueUser = await client.confirm(confirmationToken, persistSession);
504
- const jwt = await gotrueUser.jwt();
505
- setBrowserAuthCookies(jwt);
506
- const user = toUser(gotrueUser);
507
- clearHash();
508
- emitAuthEvent("login", user);
509
- return { type: "confirmation", user };
510
- }
572
+ if (confirmationToken) return await handleConfirmationCallback(client, confirmationToken);
511
573
  const recoveryToken = params.get("recovery_token");
512
- if (recoveryToken) {
513
- const gotrueUser = await client.recover(recoveryToken, persistSession);
514
- const jwt = await gotrueUser.jwt();
515
- setBrowserAuthCookies(jwt);
516
- const user = toUser(gotrueUser);
517
- clearHash();
518
- emitAuthEvent("login", user);
519
- return { type: "recovery", user };
520
- }
574
+ if (recoveryToken) return await handleRecoveryCallback(client, recoveryToken);
521
575
  const inviteToken = params.get("invite_token");
522
- if (inviteToken) {
523
- clearHash();
524
- return { type: "invite", user: null, token: inviteToken };
525
- }
576
+ if (inviteToken) return handleInviteCallback(inviteToken);
526
577
  const emailChangeToken = params.get("email_change_token");
527
- if (emailChangeToken) {
528
- const currentUser = client.currentUser();
529
- if (!currentUser) {
530
- throw new AuthError("Email change verification requires an active browser session");
531
- }
532
- const jwt = await currentUser.jwt();
533
- const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
534
- const emailChangeRes = await fetch(`${identityUrl}/user`, {
535
- method: "PUT",
536
- headers: {
537
- "Content-Type": "application/json",
538
- Authorization: `Bearer ${jwt}`
539
- },
540
- body: JSON.stringify({ email_change_token: emailChangeToken })
541
- });
542
- if (!emailChangeRes.ok) {
543
- const errorBody = await emailChangeRes.json().catch(() => ({}));
544
- throw new AuthError(
545
- errorBody.msg || `Email change verification failed (${emailChangeRes.status})`,
546
- emailChangeRes.status
547
- );
548
- }
549
- const emailChangeData = await emailChangeRes.json();
550
- const user = toUser(emailChangeData);
551
- clearHash();
552
- emitAuthEvent("user_updated", user);
553
- return { type: "email_change", user };
554
- }
578
+ if (emailChangeToken) return await handleEmailChangeCallback(client, emailChangeToken);
555
579
  return null;
556
580
  } catch (error) {
557
- throw new AuthError(error.message, void 0, { cause: error });
581
+ if (error instanceof AuthError) throw error;
582
+ throw AuthError.from(error);
583
+ }
584
+ };
585
+ var handleOAuthCallback = async (client, params, accessToken) => {
586
+ const refreshToken = params.get("refresh_token") ?? "";
587
+ const expiresIn = parseInt(params.get("expires_in") ?? "", 10);
588
+ const expiresAt = parseInt(params.get("expires_at") ?? "", 10);
589
+ const gotrueUser = await client.createUser(
590
+ {
591
+ access_token: accessToken,
592
+ token_type: params.get("token_type") ?? "bearer",
593
+ expires_in: isFinite(expiresIn) ? expiresIn : 3600,
594
+ expires_at: isFinite(expiresAt) ? expiresAt : Math.floor(Date.now() / 1e3) + 3600,
595
+ refresh_token: refreshToken
596
+ },
597
+ persistSession
598
+ );
599
+ setBrowserAuthCookies(accessToken, refreshToken || void 0);
600
+ const user = toUser(gotrueUser);
601
+ clearHash();
602
+ emitAuthEvent(AUTH_EVENTS.LOGIN, user);
603
+ return { type: "oauth", user };
604
+ };
605
+ var handleConfirmationCallback = async (client, token) => {
606
+ const gotrueUser = await client.confirm(token, persistSession);
607
+ const jwt = await gotrueUser.jwt();
608
+ setBrowserAuthCookies(jwt, gotrueUser.tokenDetails()?.refresh_token);
609
+ const user = toUser(gotrueUser);
610
+ clearHash();
611
+ emitAuthEvent(AUTH_EVENTS.LOGIN, user);
612
+ return { type: "confirmation", user };
613
+ };
614
+ var handleRecoveryCallback = async (client, token) => {
615
+ const gotrueUser = await client.recover(token, persistSession);
616
+ const jwt = await gotrueUser.jwt();
617
+ setBrowserAuthCookies(jwt, gotrueUser.tokenDetails()?.refresh_token);
618
+ const user = toUser(gotrueUser);
619
+ clearHash();
620
+ emitAuthEvent(AUTH_EVENTS.RECOVERY, user);
621
+ return { type: "recovery", user };
622
+ };
623
+ var handleInviteCallback = (token) => {
624
+ clearHash();
625
+ return { type: "invite", user: null, token };
626
+ };
627
+ var handleEmailChangeCallback = async (client, emailChangeToken) => {
628
+ const currentUser = client.currentUser();
629
+ if (!currentUser) {
630
+ throw new AuthError("Email change verification requires an active browser session");
558
631
  }
632
+ const jwt = await currentUser.jwt();
633
+ const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
634
+ const emailChangeRes = await fetch(`${identityUrl}/user`, {
635
+ method: "PUT",
636
+ headers: {
637
+ "Content-Type": "application/json",
638
+ Authorization: `Bearer ${jwt}`
639
+ },
640
+ body: JSON.stringify({ email_change_token: emailChangeToken })
641
+ });
642
+ if (!emailChangeRes.ok) {
643
+ const errorBody = await emailChangeRes.json().catch(() => ({}));
644
+ throw new AuthError(
645
+ errorBody.msg || `Email change verification failed (${emailChangeRes.status})`,
646
+ emailChangeRes.status
647
+ );
648
+ }
649
+ const emailChangeData = await emailChangeRes.json();
650
+ const user = toUser(emailChangeData);
651
+ clearHash();
652
+ emitAuthEvent(AUTH_EVENTS.USER_UPDATED, user);
653
+ return { type: "email_change", user };
559
654
  };
560
655
  var clearHash = () => {
561
656
  history.replaceState(null, "", window.location.pathname + window.location.search);
@@ -568,23 +663,32 @@ var hydrateSession = async () => {
568
663
  const accessToken = getCookie(NF_JWT_COOKIE);
569
664
  if (!accessToken) return null;
570
665
  const refreshToken = getCookie(NF_REFRESH_COOKIE) ?? "";
571
- const gotrueUser = await client.createUser(
572
- {
573
- access_token: accessToken,
574
- token_type: "bearer",
575
- expires_in: 3600,
576
- expires_at: Math.floor(Date.now() / 1e3) + 3600,
577
- refresh_token: refreshToken
578
- },
579
- persistSession
580
- );
666
+ const decoded = decodeJwtPayload(accessToken);
667
+ const expiresAt = decoded?.exp ?? Math.floor(Date.now() / 1e3) + 3600;
668
+ const expiresIn = Math.max(0, expiresAt - Math.floor(Date.now() / 1e3));
669
+ let gotrueUser;
670
+ try {
671
+ gotrueUser = await client.createUser(
672
+ {
673
+ access_token: accessToken,
674
+ token_type: "bearer",
675
+ expires_in: expiresIn,
676
+ expires_at: expiresAt,
677
+ refresh_token: refreshToken
678
+ },
679
+ persistSession
680
+ );
681
+ } catch {
682
+ deleteBrowserAuthCookies();
683
+ return null;
684
+ }
581
685
  const user = toUser(gotrueUser);
582
- emitAuthEvent("login", user);
686
+ emitAuthEvent(AUTH_EVENTS.LOGIN, user);
583
687
  return user;
584
688
  };
585
689
 
586
690
  // src/account.ts
587
- var ensureCurrentUser = async () => {
691
+ var resolveCurrentUser = async () => {
588
692
  const client = getClient();
589
693
  let currentUser = client.currentUser();
590
694
  if (!currentUser && isBrowser()) {
@@ -602,7 +706,7 @@ var requestPasswordRecovery = async (email) => {
602
706
  try {
603
707
  await client.requestPasswordRecovery(email);
604
708
  } catch (error) {
605
- throw new AuthError(error.message, void 0, { cause: error });
709
+ throw AuthError.from(error);
606
710
  }
607
711
  };
608
712
  var recoverPassword = async (token, newPassword) => {
@@ -611,10 +715,10 @@ var recoverPassword = async (token, newPassword) => {
611
715
  const gotrueUser = await client.recover(token, persistSession);
612
716
  const updatedUser = await gotrueUser.update({ password: newPassword });
613
717
  const user = toUser(updatedUser);
614
- emitAuthEvent("login", user);
718
+ emitAuthEvent(AUTH_EVENTS.LOGIN, user);
615
719
  return user;
616
720
  } catch (error) {
617
- throw new AuthError(error.message, void 0, { cause: error });
721
+ throw AuthError.from(error);
618
722
  }
619
723
  };
620
724
  var confirmEmail = async (token) => {
@@ -622,10 +726,10 @@ var confirmEmail = async (token) => {
622
726
  try {
623
727
  const gotrueUser = await client.confirm(token, persistSession);
624
728
  const user = toUser(gotrueUser);
625
- emitAuthEvent("login", user);
729
+ emitAuthEvent(AUTH_EVENTS.LOGIN, user);
626
730
  return user;
627
731
  } catch (error) {
628
- throw new AuthError(error.message, void 0, { cause: error });
732
+ throw AuthError.from(error);
629
733
  }
630
734
  };
631
735
  var acceptInvite = async (token, password) => {
@@ -633,18 +737,18 @@ var acceptInvite = async (token, password) => {
633
737
  try {
634
738
  const gotrueUser = await client.acceptInvite(token, password, persistSession);
635
739
  const user = toUser(gotrueUser);
636
- emitAuthEvent("login", user);
740
+ emitAuthEvent(AUTH_EVENTS.LOGIN, user);
637
741
  return user;
638
742
  } catch (error) {
639
- throw new AuthError(error.message, void 0, { cause: error });
743
+ throw AuthError.from(error);
640
744
  }
641
745
  };
642
746
  var verifyEmailChange = async (token) => {
643
747
  if (!isBrowser()) throw new AuthError("verifyEmailChange() is only available in the browser");
644
- const currentUser = await ensureCurrentUser();
645
- const jwt = await currentUser.jwt();
646
- const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
748
+ const currentUser = await resolveCurrentUser();
647
749
  try {
750
+ const jwt = await currentUser.jwt();
751
+ const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
648
752
  const res = await fetch(`${identityUrl}/user`, {
649
753
  method: "PUT",
650
754
  headers: {
@@ -655,33 +759,31 @@ var verifyEmailChange = async (token) => {
655
759
  });
656
760
  if (!res.ok) {
657
761
  const errorBody = await res.json().catch(() => ({}));
658
- throw new AuthError(
659
- errorBody.msg || `Email change verification failed (${res.status})`,
660
- res.status
661
- );
762
+ throw new AuthError(errorBody.msg || `Email change verification failed (${res.status})`, res.status);
662
763
  }
663
764
  const userData = await res.json();
664
765
  const user = toUser(userData);
665
- emitAuthEvent("user_updated", user);
766
+ emitAuthEvent(AUTH_EVENTS.USER_UPDATED, user);
666
767
  return user;
667
768
  } catch (error) {
668
769
  if (error instanceof AuthError) throw error;
669
- throw new AuthError(error.message, void 0, { cause: error });
770
+ throw AuthError.from(error);
670
771
  }
671
772
  };
672
773
  var updateUser = async (updates) => {
673
- const currentUser = await ensureCurrentUser();
774
+ const currentUser = await resolveCurrentUser();
674
775
  try {
675
776
  const updatedUser = await currentUser.update(updates);
676
777
  const user = toUser(updatedUser);
677
- emitAuthEvent("user_updated", user);
778
+ emitAuthEvent(AUTH_EVENTS.USER_UPDATED, user);
678
779
  return user;
679
780
  } catch (error) {
680
- throw new AuthError(error.message, void 0, { cause: error });
781
+ throw AuthError.from(error);
681
782
  }
682
783
  };
683
784
  // Annotate the CommonJS export names for ESM import in node:
684
785
  0 && (module.exports = {
786
+ AUTH_EVENTS,
685
787
  AuthError,
686
788
  MissingIdentityError,
687
789
  acceptInvite,
@@ -690,6 +792,7 @@ var updateUser = async (updates) => {
690
792
  getSettings,
691
793
  getUser,
692
794
  handleAuthCallback,
795
+ hydrateSession,
693
796
  isAuthenticated,
694
797
  login,
695
798
  logout,