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