@netlify/identity 0.1.1-alpha.1 → 0.1.1-alpha.11

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
 
@@ -23,6 +30,7 @@ var MissingIdentityError = class extends Error {
23
30
  };
24
31
 
25
32
  // src/environment.ts
33
+ var IDENTITY_PATH = "/.netlify/identity";
26
34
  var goTrueClient = null;
27
35
  var cachedApiUrl;
28
36
  var warnedMissingUrl = false;
@@ -30,13 +38,15 @@ var isBrowser = () => typeof window !== "undefined" && typeof window.location !=
30
38
  var discoverApiUrl = () => {
31
39
  if (cachedApiUrl !== void 0) return cachedApiUrl;
32
40
  if (isBrowser()) {
33
- cachedApiUrl = `${window.location.origin}/.netlify/identity`;
41
+ cachedApiUrl = `${window.location.origin}${IDENTITY_PATH}`;
34
42
  } else {
35
43
  const identityContext = getIdentityContext();
36
44
  if (identityContext?.url) {
37
45
  cachedApiUrl = identityContext.url;
38
46
  } else if (globalThis.Netlify?.context?.url) {
39
- cachedApiUrl = new URL("/.netlify/identity", globalThis.Netlify.context.url).href;
47
+ cachedApiUrl = new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href;
48
+ } else if (process.env.URL) {
49
+ cachedApiUrl = new URL(IDENTITY_PATH, process.env.URL).href;
40
50
  }
41
51
  }
42
52
  return cachedApiUrl ?? null;
@@ -63,18 +73,92 @@ var getClient = () => {
63
73
  };
64
74
  var getIdentityContext = () => {
65
75
  const identityContext = globalThis.netlifyIdentityContext;
66
- if (identityContext?.url && typeof identityContext.url === "string") {
76
+ if (identityContext?.url) {
67
77
  return {
68
78
  url: identityContext.url,
69
- token: typeof identityContext.token === "string" ? identityContext.token : void 0
79
+ token: identityContext.token
70
80
  };
71
81
  }
72
82
  if (globalThis.Netlify?.context?.url) {
73
- return { url: new URL("/.netlify/identity", globalThis.Netlify.context.url).href };
83
+ return { url: new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href };
84
+ }
85
+ const siteUrl = process.env.URL;
86
+ if (siteUrl) {
87
+ return { url: new URL(IDENTITY_PATH, siteUrl).href };
74
88
  }
75
89
  return null;
76
90
  };
77
91
 
92
+ // src/cookies.ts
93
+ var NF_JWT_COOKIE = "nf_jwt";
94
+ var NF_REFRESH_COOKIE = "nf_refresh";
95
+ var getCookie = (name) => {
96
+ const match = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=([^;]*)`));
97
+ return match ? decodeURIComponent(match[1]) : null;
98
+ };
99
+ var setAuthCookies = (cookies, accessToken, refreshToken) => {
100
+ cookies.set({
101
+ name: NF_JWT_COOKIE,
102
+ value: accessToken,
103
+ httpOnly: false,
104
+ secure: true,
105
+ path: "/",
106
+ sameSite: "Lax"
107
+ });
108
+ if (refreshToken) {
109
+ cookies.set({
110
+ name: NF_REFRESH_COOKIE,
111
+ value: refreshToken,
112
+ httpOnly: false,
113
+ secure: true,
114
+ path: "/",
115
+ sameSite: "Lax"
116
+ });
117
+ }
118
+ };
119
+ var deleteAuthCookies = (cookies) => {
120
+ cookies.delete(NF_JWT_COOKIE);
121
+ cookies.delete(NF_REFRESH_COOKIE);
122
+ };
123
+ var setBrowserAuthCookies = (accessToken, refreshToken) => {
124
+ document.cookie = `${NF_JWT_COOKIE}=${encodeURIComponent(accessToken)}; path=/; secure; samesite=lax`;
125
+ if (refreshToken) {
126
+ document.cookie = `${NF_REFRESH_COOKIE}=${encodeURIComponent(refreshToken)}; path=/; secure; samesite=lax`;
127
+ }
128
+ };
129
+ var deleteBrowserAuthCookies = () => {
130
+ document.cookie = `${NF_JWT_COOKIE}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
131
+ document.cookie = `${NF_REFRESH_COOKIE}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
132
+ };
133
+
134
+ // src/nextjs.ts
135
+ var nextHeadersFn;
136
+ var triggerNextjsDynamic = () => {
137
+ if (nextHeadersFn === null) return;
138
+ if (nextHeadersFn === void 0) {
139
+ try {
140
+ if (typeof __require === "undefined") {
141
+ nextHeadersFn = null;
142
+ return;
143
+ }
144
+ const mod = __require("next/headers");
145
+ nextHeadersFn = mod.headers;
146
+ } catch {
147
+ nextHeadersFn = null;
148
+ return;
149
+ }
150
+ }
151
+ const fn = nextHeadersFn;
152
+ if (!fn) return;
153
+ try {
154
+ fn();
155
+ } catch (e) {
156
+ if (e instanceof Error && ("digest" in e || /bail\s*out.*prerende/i.test(e.message))) {
157
+ throw e;
158
+ }
159
+ }
160
+ };
161
+
78
162
  // src/user.ts
79
163
  var toAuthProvider = (value) => typeof value === "string" && AUTH_PROVIDERS.includes(value) ? value : void 0;
80
164
  var toUser = (userData) => {
@@ -100,32 +184,57 @@ var claimsToUser = (claims) => {
100
184
  const userMeta = claims.user_metadata ?? {};
101
185
  const name = userMeta.full_name || userMeta.name;
102
186
  return {
103
- id: typeof claims.sub === "string" ? claims.sub : "",
104
- email: typeof claims.email === "string" ? claims.email : void 0,
187
+ id: claims.sub ?? "",
188
+ email: claims.email,
105
189
  provider: toAuthProvider(appMeta.provider),
106
190
  name: typeof name === "string" ? name : void 0,
107
191
  metadata: userMeta
108
192
  };
109
193
  };
194
+ var decodeJwtPayload = (token) => {
195
+ try {
196
+ const parts = token.split(".");
197
+ if (parts.length !== 3) return null;
198
+ const payload = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
199
+ return JSON.parse(payload);
200
+ } catch {
201
+ return null;
202
+ }
203
+ };
110
204
  var getUser = () => {
111
205
  if (isBrowser()) {
112
206
  const client = getGoTrueClient();
113
207
  const currentUser = client?.currentUser() ?? null;
114
- if (!currentUser) return null;
115
- return toUser(currentUser);
208
+ if (currentUser) {
209
+ const jwt2 = getCookie(NF_JWT_COOKIE);
210
+ if (!jwt2) {
211
+ try {
212
+ currentUser.logout();
213
+ } catch {
214
+ }
215
+ return null;
216
+ }
217
+ return toUser(currentUser);
218
+ }
219
+ const jwt = getCookie(NF_JWT_COOKIE);
220
+ if (!jwt) return null;
221
+ const claims = decodeJwtPayload(jwt);
222
+ if (!claims) return null;
223
+ return claimsToUser(claims);
116
224
  }
225
+ triggerNextjsDynamic();
117
226
  const identityContext = globalThis.netlifyIdentityContext;
118
- if (!identityContext) return null;
119
- const claims = identityContext.user ?? (identityContext.sub ? identityContext : null);
120
- if (!claims) return null;
121
- return claimsToUser(claims);
227
+ if (identityContext?.user) {
228
+ return claimsToUser(identityContext.user);
229
+ }
230
+ return null;
122
231
  };
123
232
  var isAuthenticated = () => getUser() !== null;
124
233
 
125
234
  // src/config.ts
126
235
  var getIdentityConfig = () => {
127
236
  if (isBrowser()) {
128
- return { url: `${window.location.origin}/.netlify/identity` };
237
+ return { url: `${window.location.origin}${IDENTITY_PATH}` };
129
238
  }
130
239
  return getIdentityContext();
131
240
  };
@@ -153,6 +262,20 @@ var getSettings = async () => {
153
262
  };
154
263
 
155
264
  // src/auth.ts
265
+ var getCookies = () => {
266
+ const cookies = globalThis.Netlify?.context?.cookies;
267
+ if (!cookies) {
268
+ throw new AuthError("Server-side auth requires Netlify Functions runtime");
269
+ }
270
+ return cookies;
271
+ };
272
+ var getServerIdentityUrl = () => {
273
+ const ctx = getIdentityContext();
274
+ if (!ctx?.url) {
275
+ throw new AuthError("Could not determine the Identity endpoint URL on the server");
276
+ }
277
+ return ctx.url;
278
+ };
156
279
  var persistSession = true;
157
280
  var listeners = /* @__PURE__ */ new Set();
158
281
  var emitAuthEvent = (event, user) => {
@@ -187,9 +310,58 @@ var onAuthChange = (callback) => {
187
310
  };
188
311
  };
189
312
  var login = async (email, password) => {
313
+ if (!isBrowser()) {
314
+ const identityUrl = getServerIdentityUrl();
315
+ const cookies = getCookies();
316
+ const body = new URLSearchParams({
317
+ grant_type: "password",
318
+ username: email,
319
+ password
320
+ });
321
+ let res;
322
+ try {
323
+ res = await fetch(`${identityUrl}/token`, {
324
+ method: "POST",
325
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
326
+ body: body.toString()
327
+ });
328
+ } catch (error) {
329
+ throw new AuthError(error.message, void 0, { cause: error });
330
+ }
331
+ if (!res.ok) {
332
+ const errorBody = await res.json().catch(() => ({}));
333
+ throw new AuthError(
334
+ errorBody.msg || errorBody.error_description || `Login failed (${res.status})`,
335
+ res.status
336
+ );
337
+ }
338
+ const data = await res.json();
339
+ const accessToken = data.access_token;
340
+ let userRes;
341
+ try {
342
+ userRes = await fetch(`${identityUrl}/user`, {
343
+ headers: { Authorization: `Bearer ${accessToken}` }
344
+ });
345
+ } catch (error) {
346
+ throw new AuthError(error.message, void 0, { cause: error });
347
+ }
348
+ if (!userRes.ok) {
349
+ const errorBody = await userRes.json().catch(() => ({}));
350
+ throw new AuthError(
351
+ errorBody.msg || `Failed to fetch user data (${userRes.status})`,
352
+ userRes.status
353
+ );
354
+ }
355
+ const userData = await userRes.json();
356
+ const user = toUser(userData);
357
+ setAuthCookies(cookies, accessToken, data.refresh_token);
358
+ return user;
359
+ }
190
360
  const client = getClient();
191
361
  try {
192
362
  const gotrueUser = await client.login(email, password, persistSession);
363
+ const jwt = await gotrueUser.jwt();
364
+ setBrowserAuthCookies(jwt);
193
365
  const user = toUser(gotrueUser);
194
366
  emitAuthEvent("login", user);
195
367
  return user;
@@ -198,6 +370,34 @@ var login = async (email, password) => {
198
370
  }
199
371
  };
200
372
  var signup = async (email, password, data) => {
373
+ if (!isBrowser()) {
374
+ const identityUrl = getServerIdentityUrl();
375
+ const cookies = getCookies();
376
+ let res;
377
+ try {
378
+ res = await fetch(`${identityUrl}/signup`, {
379
+ method: "POST",
380
+ headers: { "Content-Type": "application/json" },
381
+ body: JSON.stringify({ email, password, data })
382
+ });
383
+ } catch (error) {
384
+ throw new AuthError(error.message, void 0, { cause: error });
385
+ }
386
+ if (!res.ok) {
387
+ const errorBody = await res.json().catch(() => ({}));
388
+ throw new AuthError(errorBody.msg || `Signup failed (${res.status})`, res.status);
389
+ }
390
+ const responseData = await res.json();
391
+ const user = toUser(responseData);
392
+ if (responseData.confirmed_at) {
393
+ const responseRecord = responseData;
394
+ const accessToken = responseRecord.access_token;
395
+ if (accessToken) {
396
+ setAuthCookies(cookies, accessToken, responseRecord.refresh_token);
397
+ }
398
+ }
399
+ return user;
400
+ }
201
401
  const client = getClient();
202
402
  try {
203
403
  const response = await client.signup(email, password, data);
@@ -211,12 +411,30 @@ var signup = async (email, password, data) => {
211
411
  }
212
412
  };
213
413
  var logout = async () => {
414
+ if (!isBrowser()) {
415
+ const identityUrl = getServerIdentityUrl();
416
+ const cookies = getCookies();
417
+ const jwt = cookies.get(NF_JWT_COOKIE);
418
+ if (jwt) {
419
+ try {
420
+ await fetch(`${identityUrl}/logout`, {
421
+ method: "POST",
422
+ headers: { Authorization: `Bearer ${jwt}` }
423
+ });
424
+ } catch (error) {
425
+ throw new AuthError(error.message, void 0, { cause: error });
426
+ }
427
+ }
428
+ deleteAuthCookies(cookies);
429
+ return;
430
+ }
214
431
  const client = getClient();
215
432
  try {
216
433
  const currentUser = client.currentUser();
217
434
  if (currentUser) {
218
435
  await currentUser.logout();
219
436
  }
437
+ deleteBrowserAuthCookies();
220
438
  emitAuthEvent("logout", null);
221
439
  } catch (error) {
222
440
  throw new AuthError(error.message, void 0, { cause: error });
@@ -239,16 +457,18 @@ var handleAuthCallback = async () => {
239
457
  const params = new URLSearchParams(hash);
240
458
  const accessToken = params.get("access_token");
241
459
  if (accessToken) {
460
+ const refreshToken = params.get("refresh_token") ?? "";
242
461
  const gotrueUser = await client.createUser(
243
462
  {
244
463
  access_token: accessToken,
245
464
  token_type: params.get("token_type") ?? "bearer",
246
465
  expires_in: Number(params.get("expires_in")),
247
466
  expires_at: Number(params.get("expires_at")),
248
- refresh_token: params.get("refresh_token") ?? ""
467
+ refresh_token: refreshToken
249
468
  },
250
469
  persistSession
251
470
  );
471
+ setBrowserAuthCookies(accessToken, refreshToken || void 0);
252
472
  const user = toUser(gotrueUser);
253
473
  clearHash();
254
474
  emitAuthEvent("login", user);
@@ -257,6 +477,8 @@ var handleAuthCallback = async () => {
257
477
  const confirmationToken = params.get("confirmation_token");
258
478
  if (confirmationToken) {
259
479
  const gotrueUser = await client.confirm(confirmationToken, persistSession);
480
+ const jwt = await gotrueUser.jwt();
481
+ setBrowserAuthCookies(jwt);
260
482
  const user = toUser(gotrueUser);
261
483
  clearHash();
262
484
  emitAuthEvent("login", user);
@@ -265,6 +487,8 @@ var handleAuthCallback = async () => {
265
487
  const recoveryToken = params.get("recovery_token");
266
488
  if (recoveryToken) {
267
489
  const gotrueUser = await client.recover(recoveryToken, persistSession);
490
+ const jwt = await gotrueUser.jwt();
491
+ setBrowserAuthCookies(jwt);
268
492
  const user = toUser(gotrueUser);
269
493
  clearHash();
270
494
  emitAuthEvent("login", user);
@@ -277,8 +501,29 @@ var handleAuthCallback = async () => {
277
501
  }
278
502
  const emailChangeToken = params.get("email_change_token");
279
503
  if (emailChangeToken) {
280
- const gotrueUser = await client.verify("email_change", emailChangeToken, persistSession);
281
- const user = toUser(gotrueUser);
504
+ const currentUser = client.currentUser();
505
+ if (!currentUser) {
506
+ throw new AuthError("Email change verification requires an active browser session");
507
+ }
508
+ const jwt = await currentUser.jwt();
509
+ const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
510
+ const emailChangeRes = await fetch(`${identityUrl}/user`, {
511
+ method: "PUT",
512
+ headers: {
513
+ "Content-Type": "application/json",
514
+ Authorization: `Bearer ${jwt}`
515
+ },
516
+ body: JSON.stringify({ email_change_token: emailChangeToken })
517
+ });
518
+ if (!emailChangeRes.ok) {
519
+ const errorBody = await emailChangeRes.json().catch(() => ({}));
520
+ throw new AuthError(
521
+ errorBody.msg || `Email change verification failed (${emailChangeRes.status})`,
522
+ emailChangeRes.status
523
+ );
524
+ }
525
+ const emailChangeData = await emailChangeRes.json();
526
+ const user = toUser(emailChangeData);
282
527
  clearHash();
283
528
  emitAuthEvent("user_updated", user);
284
529
  return { type: "email_change", user };
@@ -291,8 +536,43 @@ var handleAuthCallback = async () => {
291
536
  var clearHash = () => {
292
537
  history.replaceState(null, "", window.location.pathname + window.location.search);
293
538
  };
539
+ var hydrateSession = async () => {
540
+ if (!isBrowser()) return null;
541
+ const client = getClient();
542
+ const currentUser = client.currentUser();
543
+ if (currentUser) return toUser(currentUser);
544
+ const accessToken = getCookie(NF_JWT_COOKIE);
545
+ if (!accessToken) return null;
546
+ const refreshToken = getCookie(NF_REFRESH_COOKIE) ?? "";
547
+ const gotrueUser = await client.createUser(
548
+ {
549
+ access_token: accessToken,
550
+ token_type: "bearer",
551
+ expires_in: 3600,
552
+ expires_at: Math.floor(Date.now() / 1e3) + 3600,
553
+ refresh_token: refreshToken
554
+ },
555
+ persistSession
556
+ );
557
+ const user = toUser(gotrueUser);
558
+ emitAuthEvent("login", user);
559
+ return user;
560
+ };
294
561
 
295
562
  // src/account.ts
563
+ var ensureCurrentUser = async () => {
564
+ const client = getClient();
565
+ let currentUser = client.currentUser();
566
+ if (!currentUser && isBrowser()) {
567
+ try {
568
+ await hydrateSession();
569
+ } catch {
570
+ }
571
+ currentUser = client.currentUser();
572
+ }
573
+ if (!currentUser) throw new AuthError("No user is currently logged in");
574
+ return currentUser;
575
+ };
296
576
  var requestPasswordRecovery = async (email) => {
297
577
  const client = getClient();
298
578
  try {
@@ -301,6 +581,18 @@ var requestPasswordRecovery = async (email) => {
301
581
  throw new AuthError(error.message, void 0, { cause: error });
302
582
  }
303
583
  };
584
+ var recoverPassword = async (token, newPassword) => {
585
+ const client = getClient();
586
+ try {
587
+ const gotrueUser = await client.recover(token, persistSession);
588
+ const updatedUser = await gotrueUser.update({ password: newPassword });
589
+ const user = toUser(updatedUser);
590
+ emitAuthEvent("login", user);
591
+ return user;
592
+ } catch (error) {
593
+ throw new AuthError(error.message, void 0, { cause: error });
594
+ }
595
+ };
304
596
  var confirmEmail = async (token) => {
305
597
  const client = getClient();
306
598
  try {
@@ -324,20 +616,37 @@ var acceptInvite = async (token, password) => {
324
616
  }
325
617
  };
326
618
  var verifyEmailChange = async (token) => {
327
- const client = getClient();
619
+ if (!isBrowser()) throw new AuthError("verifyEmailChange() is only available in the browser");
620
+ const currentUser = await ensureCurrentUser();
621
+ const jwt = await currentUser.jwt();
622
+ const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
328
623
  try {
329
- const gotrueUser = await client.verify("email_change", token, persistSession);
330
- const user = toUser(gotrueUser);
624
+ const res = await fetch(`${identityUrl}/user`, {
625
+ method: "PUT",
626
+ headers: {
627
+ "Content-Type": "application/json",
628
+ Authorization: `Bearer ${jwt}`
629
+ },
630
+ body: JSON.stringify({ email_change_token: token })
631
+ });
632
+ if (!res.ok) {
633
+ const errorBody = await res.json().catch(() => ({}));
634
+ throw new AuthError(
635
+ errorBody.msg || `Email change verification failed (${res.status})`,
636
+ res.status
637
+ );
638
+ }
639
+ const userData = await res.json();
640
+ const user = toUser(userData);
331
641
  emitAuthEvent("user_updated", user);
332
642
  return user;
333
643
  } catch (error) {
644
+ if (error instanceof AuthError) throw error;
334
645
  throw new AuthError(error.message, void 0, { cause: error });
335
646
  }
336
647
  };
337
648
  var updateUser = async (updates) => {
338
- const client = getClient();
339
- const currentUser = client.currentUser();
340
- if (!currentUser) throw new AuthError("No user is currently logged in");
649
+ const currentUser = await ensureCurrentUser();
341
650
  try {
342
651
  const updatedUser = await currentUser.update(updates);
343
652
  const user = toUser(updatedUser);
@@ -361,6 +670,7 @@ export {
361
670
  logout,
362
671
  oauthLogin,
363
672
  onAuthChange,
673
+ recoverPassword,
364
674
  requestPasswordRecovery,
365
675
  signup,
366
676
  updateUser,