@netlify/identity 0.1.1-alpha.0 → 0.1.1-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -13
- package/dist/index.cjs +284 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -5
- package/dist/index.d.ts +16 -5
- package/dist/index.js +284 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -30,6 +30,11 @@ interface User {
|
|
|
30
30
|
/**
|
|
31
31
|
* Returns the currently authenticated user, or `null` if not logged in.
|
|
32
32
|
* Synchronous. Never throws.
|
|
33
|
+
*
|
|
34
|
+
* In the browser, checks gotrue-js localStorage first. If no localStorage
|
|
35
|
+
* session exists, falls back to decoding the `nf_jwt` cookie (set by
|
|
36
|
+
* server-side login). This gives immediate synchronous read access without
|
|
37
|
+
* waiting for async hydration via `hydrateSession()`.
|
|
33
38
|
*/
|
|
34
39
|
declare const getUser: () => User | null;
|
|
35
40
|
/**
|
|
@@ -59,11 +64,11 @@ type AuthCallback = (event: AuthEvent, user: User | null) => void;
|
|
|
59
64
|
* Returns an unsubscribe function. No-op on the server.
|
|
60
65
|
*/
|
|
61
66
|
declare const onAuthChange: (callback: AuthCallback) => (() => void);
|
|
62
|
-
/** Logs in with email and password.
|
|
67
|
+
/** Logs in with email and password. Works in both browser and server contexts. */
|
|
63
68
|
declare const login: (email: string, password: string) => Promise<User>;
|
|
64
|
-
/** Creates a new account. Emits 'login' if autoconfirm is enabled.
|
|
69
|
+
/** Creates a new account. Emits 'login' if autoconfirm is enabled. Works in both browser and server contexts. */
|
|
65
70
|
declare const signup: (email: string, password: string, data?: Record<string, unknown>) => Promise<User>;
|
|
66
|
-
/** Logs out the current user and clears the session.
|
|
71
|
+
/** Logs out the current user and clears the session. Works in both browser and server contexts. */
|
|
67
72
|
declare const logout: () => Promise<void>;
|
|
68
73
|
/** Redirects to an OAuth provider. Always throws (the page navigates away). Browser only. */
|
|
69
74
|
declare const oauthLogin: (provider: string) => never;
|
|
@@ -100,9 +105,15 @@ declare const recoverPassword: (token: string, newPassword: string) => Promise<U
|
|
|
100
105
|
declare const confirmEmail: (token: string) => Promise<User>;
|
|
101
106
|
/** Accepts an invite token and sets a password for the new account. Logs the user in on success. */
|
|
102
107
|
declare const acceptInvite: (token: string, password: string) => Promise<User>;
|
|
103
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Verifies an email change using the token from a verification email.
|
|
110
|
+
* Auto-hydrates from auth cookies if no browser session exists.
|
|
111
|
+
*/
|
|
104
112
|
declare const verifyEmailChange: (token: string) => Promise<User>;
|
|
105
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Updates the current user's metadata or credentials.
|
|
115
|
+
* Auto-hydrates from auth cookies if no browser session exists.
|
|
116
|
+
*/
|
|
106
117
|
declare const updateUser: (updates: Record<string, unknown>) => Promise<User>;
|
|
107
118
|
|
|
108
119
|
export { type AppMetadata, type AuthCallback, AuthError, type AuthEvent, type AuthProvider, type CallbackResult, type IdentityConfig, MissingIdentityError, type Settings, type User, acceptInvite, confirmEmail, getIdentityConfig, getSettings, getUser, handleAuthCallback, isAuthenticated, login, logout, oauthLogin, onAuthChange, recoverPassword, requestPasswordRecovery, signup, updateUser, verifyEmailChange };
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,11 @@ interface User {
|
|
|
30
30
|
/**
|
|
31
31
|
* Returns the currently authenticated user, or `null` if not logged in.
|
|
32
32
|
* Synchronous. Never throws.
|
|
33
|
+
*
|
|
34
|
+
* In the browser, checks gotrue-js localStorage first. If no localStorage
|
|
35
|
+
* session exists, falls back to decoding the `nf_jwt` cookie (set by
|
|
36
|
+
* server-side login). This gives immediate synchronous read access without
|
|
37
|
+
* waiting for async hydration via `hydrateSession()`.
|
|
33
38
|
*/
|
|
34
39
|
declare const getUser: () => User | null;
|
|
35
40
|
/**
|
|
@@ -59,11 +64,11 @@ type AuthCallback = (event: AuthEvent, user: User | null) => void;
|
|
|
59
64
|
* Returns an unsubscribe function. No-op on the server.
|
|
60
65
|
*/
|
|
61
66
|
declare const onAuthChange: (callback: AuthCallback) => (() => void);
|
|
62
|
-
/** Logs in with email and password.
|
|
67
|
+
/** Logs in with email and password. Works in both browser and server contexts. */
|
|
63
68
|
declare const login: (email: string, password: string) => Promise<User>;
|
|
64
|
-
/** Creates a new account. Emits 'login' if autoconfirm is enabled.
|
|
69
|
+
/** Creates a new account. Emits 'login' if autoconfirm is enabled. Works in both browser and server contexts. */
|
|
65
70
|
declare const signup: (email: string, password: string, data?: Record<string, unknown>) => Promise<User>;
|
|
66
|
-
/** Logs out the current user and clears the session.
|
|
71
|
+
/** Logs out the current user and clears the session. Works in both browser and server contexts. */
|
|
67
72
|
declare const logout: () => Promise<void>;
|
|
68
73
|
/** Redirects to an OAuth provider. Always throws (the page navigates away). Browser only. */
|
|
69
74
|
declare const oauthLogin: (provider: string) => never;
|
|
@@ -100,9 +105,15 @@ declare const recoverPassword: (token: string, newPassword: string) => Promise<U
|
|
|
100
105
|
declare const confirmEmail: (token: string) => Promise<User>;
|
|
101
106
|
/** Accepts an invite token and sets a password for the new account. Logs the user in on success. */
|
|
102
107
|
declare const acceptInvite: (token: string, password: string) => Promise<User>;
|
|
103
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Verifies an email change using the token from a verification email.
|
|
110
|
+
* Auto-hydrates from auth cookies if no browser session exists.
|
|
111
|
+
*/
|
|
104
112
|
declare const verifyEmailChange: (token: string) => Promise<User>;
|
|
105
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Updates the current user's metadata or credentials.
|
|
115
|
+
* Auto-hydrates from auth cookies if no browser session exists.
|
|
116
|
+
*/
|
|
106
117
|
declare const updateUser: (updates: Record<string, unknown>) => Promise<User>;
|
|
107
118
|
|
|
108
119
|
export { type AppMetadata, type AuthCallback, AuthError, type AuthEvent, type AuthProvider, type CallbackResult, type IdentityConfig, MissingIdentityError, type Settings, type User, acceptInvite, confirmEmail, getIdentityConfig, getSettings, getUser, handleAuthCallback, isAuthenticated, login, logout, oauthLogin, onAuthChange, recoverPassword, requestPasswordRecovery, signup, updateUser, verifyEmailChange };
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ var MissingIdentityError = class extends Error {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
// src/environment.ts
|
|
26
|
+
var IDENTITY_PATH = "/.netlify/identity";
|
|
26
27
|
var goTrueClient = null;
|
|
27
28
|
var cachedApiUrl;
|
|
28
29
|
var warnedMissingUrl = false;
|
|
@@ -30,13 +31,15 @@ var isBrowser = () => typeof window !== "undefined" && typeof window.location !=
|
|
|
30
31
|
var discoverApiUrl = () => {
|
|
31
32
|
if (cachedApiUrl !== void 0) return cachedApiUrl;
|
|
32
33
|
if (isBrowser()) {
|
|
33
|
-
cachedApiUrl = `${window.location.origin}
|
|
34
|
+
cachedApiUrl = `${window.location.origin}${IDENTITY_PATH}`;
|
|
34
35
|
} else {
|
|
35
36
|
const identityContext = getIdentityContext();
|
|
36
37
|
if (identityContext?.url) {
|
|
37
38
|
cachedApiUrl = identityContext.url;
|
|
38
39
|
} else if (globalThis.Netlify?.context?.url) {
|
|
39
|
-
cachedApiUrl = new URL(
|
|
40
|
+
cachedApiUrl = new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href;
|
|
41
|
+
} else if (process.env.URL) {
|
|
42
|
+
cachedApiUrl = new URL(IDENTITY_PATH, process.env.URL).href;
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
return cachedApiUrl ?? null;
|
|
@@ -63,18 +66,64 @@ var getClient = () => {
|
|
|
63
66
|
};
|
|
64
67
|
var getIdentityContext = () => {
|
|
65
68
|
const identityContext = globalThis.netlifyIdentityContext;
|
|
66
|
-
if (identityContext?.url
|
|
69
|
+
if (identityContext?.url) {
|
|
67
70
|
return {
|
|
68
71
|
url: identityContext.url,
|
|
69
|
-
token:
|
|
72
|
+
token: identityContext.token
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
75
|
if (globalThis.Netlify?.context?.url) {
|
|
73
|
-
return { url: new URL(
|
|
76
|
+
return { url: new URL(IDENTITY_PATH, globalThis.Netlify.context.url).href };
|
|
77
|
+
}
|
|
78
|
+
const siteUrl = process.env.URL;
|
|
79
|
+
if (siteUrl) {
|
|
80
|
+
return { url: new URL(IDENTITY_PATH, siteUrl).href };
|
|
74
81
|
}
|
|
75
82
|
return null;
|
|
76
83
|
};
|
|
77
84
|
|
|
85
|
+
// src/cookies.ts
|
|
86
|
+
var NF_JWT_COOKIE = "nf_jwt";
|
|
87
|
+
var NF_REFRESH_COOKIE = "nf_refresh";
|
|
88
|
+
var getCookie = (name) => {
|
|
89
|
+
const match = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=([^;]*)`));
|
|
90
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
91
|
+
};
|
|
92
|
+
var setAuthCookies = (cookies, accessToken, refreshToken) => {
|
|
93
|
+
cookies.set({
|
|
94
|
+
name: NF_JWT_COOKIE,
|
|
95
|
+
value: accessToken,
|
|
96
|
+
httpOnly: false,
|
|
97
|
+
secure: true,
|
|
98
|
+
path: "/",
|
|
99
|
+
sameSite: "Lax"
|
|
100
|
+
});
|
|
101
|
+
if (refreshToken) {
|
|
102
|
+
cookies.set({
|
|
103
|
+
name: NF_REFRESH_COOKIE,
|
|
104
|
+
value: refreshToken,
|
|
105
|
+
httpOnly: false,
|
|
106
|
+
secure: true,
|
|
107
|
+
path: "/",
|
|
108
|
+
sameSite: "Lax"
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var deleteAuthCookies = (cookies) => {
|
|
113
|
+
cookies.delete(NF_JWT_COOKIE);
|
|
114
|
+
cookies.delete(NF_REFRESH_COOKIE);
|
|
115
|
+
};
|
|
116
|
+
var setBrowserAuthCookies = (accessToken, refreshToken) => {
|
|
117
|
+
document.cookie = `${NF_JWT_COOKIE}=${encodeURIComponent(accessToken)}; path=/; secure; samesite=lax`;
|
|
118
|
+
if (refreshToken) {
|
|
119
|
+
document.cookie = `${NF_REFRESH_COOKIE}=${encodeURIComponent(refreshToken)}; path=/; secure; samesite=lax`;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var deleteBrowserAuthCookies = () => {
|
|
123
|
+
document.cookie = `${NF_JWT_COOKIE}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
124
|
+
document.cookie = `${NF_REFRESH_COOKIE}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
125
|
+
};
|
|
126
|
+
|
|
78
127
|
// src/user.ts
|
|
79
128
|
var toAuthProvider = (value) => typeof value === "string" && AUTH_PROVIDERS.includes(value) ? value : void 0;
|
|
80
129
|
var toUser = (userData) => {
|
|
@@ -100,32 +149,56 @@ var claimsToUser = (claims) => {
|
|
|
100
149
|
const userMeta = claims.user_metadata ?? {};
|
|
101
150
|
const name = userMeta.full_name || userMeta.name;
|
|
102
151
|
return {
|
|
103
|
-
id:
|
|
104
|
-
email:
|
|
152
|
+
id: claims.sub ?? "",
|
|
153
|
+
email: claims.email,
|
|
105
154
|
provider: toAuthProvider(appMeta.provider),
|
|
106
155
|
name: typeof name === "string" ? name : void 0,
|
|
107
156
|
metadata: userMeta
|
|
108
157
|
};
|
|
109
158
|
};
|
|
159
|
+
var decodeJwtPayload = (token) => {
|
|
160
|
+
try {
|
|
161
|
+
const parts = token.split(".");
|
|
162
|
+
if (parts.length !== 3) return null;
|
|
163
|
+
const payload = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
|
|
164
|
+
return JSON.parse(payload);
|
|
165
|
+
} catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
110
169
|
var getUser = () => {
|
|
111
170
|
if (isBrowser()) {
|
|
112
171
|
const client = getGoTrueClient();
|
|
113
172
|
const currentUser = client?.currentUser() ?? null;
|
|
114
|
-
if (
|
|
115
|
-
|
|
173
|
+
if (currentUser) {
|
|
174
|
+
const jwt2 = getCookie(NF_JWT_COOKIE);
|
|
175
|
+
if (!jwt2) {
|
|
176
|
+
try {
|
|
177
|
+
currentUser.logout();
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return toUser(currentUser);
|
|
183
|
+
}
|
|
184
|
+
const jwt = getCookie(NF_JWT_COOKIE);
|
|
185
|
+
if (!jwt) return null;
|
|
186
|
+
const claims = decodeJwtPayload(jwt);
|
|
187
|
+
if (!claims) return null;
|
|
188
|
+
return claimsToUser(claims);
|
|
116
189
|
}
|
|
117
190
|
const identityContext = globalThis.netlifyIdentityContext;
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return
|
|
191
|
+
if (identityContext?.user) {
|
|
192
|
+
return claimsToUser(identityContext.user);
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
122
195
|
};
|
|
123
196
|
var isAuthenticated = () => getUser() !== null;
|
|
124
197
|
|
|
125
198
|
// src/config.ts
|
|
126
199
|
var getIdentityConfig = () => {
|
|
127
200
|
if (isBrowser()) {
|
|
128
|
-
return { url: `${window.location.origin}
|
|
201
|
+
return { url: `${window.location.origin}${IDENTITY_PATH}` };
|
|
129
202
|
}
|
|
130
203
|
return getIdentityContext();
|
|
131
204
|
};
|
|
@@ -153,6 +226,20 @@ var getSettings = async () => {
|
|
|
153
226
|
};
|
|
154
227
|
|
|
155
228
|
// src/auth.ts
|
|
229
|
+
var getCookies = () => {
|
|
230
|
+
const cookies = globalThis.Netlify?.context?.cookies;
|
|
231
|
+
if (!cookies) {
|
|
232
|
+
throw new AuthError("Server-side auth requires Netlify Functions runtime");
|
|
233
|
+
}
|
|
234
|
+
return cookies;
|
|
235
|
+
};
|
|
236
|
+
var getServerIdentityUrl = () => {
|
|
237
|
+
const ctx = getIdentityContext();
|
|
238
|
+
if (!ctx?.url) {
|
|
239
|
+
throw new AuthError("Could not determine the Identity endpoint URL on the server");
|
|
240
|
+
}
|
|
241
|
+
return ctx.url;
|
|
242
|
+
};
|
|
156
243
|
var persistSession = true;
|
|
157
244
|
var listeners = /* @__PURE__ */ new Set();
|
|
158
245
|
var emitAuthEvent = (event, user) => {
|
|
@@ -187,9 +274,58 @@ var onAuthChange = (callback) => {
|
|
|
187
274
|
};
|
|
188
275
|
};
|
|
189
276
|
var login = async (email, password) => {
|
|
277
|
+
if (!isBrowser()) {
|
|
278
|
+
const identityUrl = getServerIdentityUrl();
|
|
279
|
+
const cookies = getCookies();
|
|
280
|
+
const body = new URLSearchParams({
|
|
281
|
+
grant_type: "password",
|
|
282
|
+
username: email,
|
|
283
|
+
password
|
|
284
|
+
});
|
|
285
|
+
let res;
|
|
286
|
+
try {
|
|
287
|
+
res = await fetch(`${identityUrl}/token`, {
|
|
288
|
+
method: "POST",
|
|
289
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
290
|
+
body: body.toString()
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
throw new AuthError(error.message, void 0, { cause: error });
|
|
294
|
+
}
|
|
295
|
+
if (!res.ok) {
|
|
296
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
297
|
+
throw new AuthError(
|
|
298
|
+
errorBody.msg || errorBody.error_description || `Login failed (${res.status})`,
|
|
299
|
+
res.status
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
const data = await res.json();
|
|
303
|
+
const accessToken = data.access_token;
|
|
304
|
+
let userRes;
|
|
305
|
+
try {
|
|
306
|
+
userRes = await fetch(`${identityUrl}/user`, {
|
|
307
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
308
|
+
});
|
|
309
|
+
} catch (error) {
|
|
310
|
+
throw new AuthError(error.message, void 0, { cause: error });
|
|
311
|
+
}
|
|
312
|
+
if (!userRes.ok) {
|
|
313
|
+
const errorBody = await userRes.json().catch(() => ({}));
|
|
314
|
+
throw new AuthError(
|
|
315
|
+
errorBody.msg || `Failed to fetch user data (${userRes.status})`,
|
|
316
|
+
userRes.status
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
const userData = await userRes.json();
|
|
320
|
+
const user = toUser(userData);
|
|
321
|
+
setAuthCookies(cookies, accessToken, data.refresh_token);
|
|
322
|
+
return user;
|
|
323
|
+
}
|
|
190
324
|
const client = getClient();
|
|
191
325
|
try {
|
|
192
326
|
const gotrueUser = await client.login(email, password, persistSession);
|
|
327
|
+
const jwt = await gotrueUser.jwt();
|
|
328
|
+
setBrowserAuthCookies(jwt);
|
|
193
329
|
const user = toUser(gotrueUser);
|
|
194
330
|
emitAuthEvent("login", user);
|
|
195
331
|
return user;
|
|
@@ -198,6 +334,34 @@ var login = async (email, password) => {
|
|
|
198
334
|
}
|
|
199
335
|
};
|
|
200
336
|
var signup = async (email, password, data) => {
|
|
337
|
+
if (!isBrowser()) {
|
|
338
|
+
const identityUrl = getServerIdentityUrl();
|
|
339
|
+
const cookies = getCookies();
|
|
340
|
+
let res;
|
|
341
|
+
try {
|
|
342
|
+
res = await fetch(`${identityUrl}/signup`, {
|
|
343
|
+
method: "POST",
|
|
344
|
+
headers: { "Content-Type": "application/json" },
|
|
345
|
+
body: JSON.stringify({ email, password, data })
|
|
346
|
+
});
|
|
347
|
+
} catch (error) {
|
|
348
|
+
throw new AuthError(error.message, void 0, { cause: error });
|
|
349
|
+
}
|
|
350
|
+
if (!res.ok) {
|
|
351
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
352
|
+
throw new AuthError(errorBody.msg || `Signup failed (${res.status})`, res.status);
|
|
353
|
+
}
|
|
354
|
+
const responseData = await res.json();
|
|
355
|
+
const user = toUser(responseData);
|
|
356
|
+
if (responseData.confirmed_at) {
|
|
357
|
+
const responseRecord = responseData;
|
|
358
|
+
const accessToken = responseRecord.access_token;
|
|
359
|
+
if (accessToken) {
|
|
360
|
+
setAuthCookies(cookies, accessToken, responseRecord.refresh_token);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return user;
|
|
364
|
+
}
|
|
201
365
|
const client = getClient();
|
|
202
366
|
try {
|
|
203
367
|
const response = await client.signup(email, password, data);
|
|
@@ -211,12 +375,30 @@ var signup = async (email, password, data) => {
|
|
|
211
375
|
}
|
|
212
376
|
};
|
|
213
377
|
var logout = async () => {
|
|
378
|
+
if (!isBrowser()) {
|
|
379
|
+
const identityUrl = getServerIdentityUrl();
|
|
380
|
+
const cookies = getCookies();
|
|
381
|
+
const jwt = cookies.get(NF_JWT_COOKIE);
|
|
382
|
+
if (jwt) {
|
|
383
|
+
try {
|
|
384
|
+
await fetch(`${identityUrl}/logout`, {
|
|
385
|
+
method: "POST",
|
|
386
|
+
headers: { Authorization: `Bearer ${jwt}` }
|
|
387
|
+
});
|
|
388
|
+
} catch (error) {
|
|
389
|
+
throw new AuthError(error.message, void 0, { cause: error });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
deleteAuthCookies(cookies);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
214
395
|
const client = getClient();
|
|
215
396
|
try {
|
|
216
397
|
const currentUser = client.currentUser();
|
|
217
398
|
if (currentUser) {
|
|
218
399
|
await currentUser.logout();
|
|
219
400
|
}
|
|
401
|
+
deleteBrowserAuthCookies();
|
|
220
402
|
emitAuthEvent("logout", null);
|
|
221
403
|
} catch (error) {
|
|
222
404
|
throw new AuthError(error.message, void 0, { cause: error });
|
|
@@ -239,16 +421,18 @@ var handleAuthCallback = async () => {
|
|
|
239
421
|
const params = new URLSearchParams(hash);
|
|
240
422
|
const accessToken = params.get("access_token");
|
|
241
423
|
if (accessToken) {
|
|
424
|
+
const refreshToken = params.get("refresh_token") ?? "";
|
|
242
425
|
const gotrueUser = await client.createUser(
|
|
243
426
|
{
|
|
244
427
|
access_token: accessToken,
|
|
245
428
|
token_type: params.get("token_type") ?? "bearer",
|
|
246
429
|
expires_in: Number(params.get("expires_in")),
|
|
247
430
|
expires_at: Number(params.get("expires_at")),
|
|
248
|
-
refresh_token:
|
|
431
|
+
refresh_token: refreshToken
|
|
249
432
|
},
|
|
250
433
|
persistSession
|
|
251
434
|
);
|
|
435
|
+
setBrowserAuthCookies(accessToken, refreshToken || void 0);
|
|
252
436
|
const user = toUser(gotrueUser);
|
|
253
437
|
clearHash();
|
|
254
438
|
emitAuthEvent("login", user);
|
|
@@ -257,6 +441,8 @@ var handleAuthCallback = async () => {
|
|
|
257
441
|
const confirmationToken = params.get("confirmation_token");
|
|
258
442
|
if (confirmationToken) {
|
|
259
443
|
const gotrueUser = await client.confirm(confirmationToken, persistSession);
|
|
444
|
+
const jwt = await gotrueUser.jwt();
|
|
445
|
+
setBrowserAuthCookies(jwt);
|
|
260
446
|
const user = toUser(gotrueUser);
|
|
261
447
|
clearHash();
|
|
262
448
|
emitAuthEvent("login", user);
|
|
@@ -265,6 +451,8 @@ var handleAuthCallback = async () => {
|
|
|
265
451
|
const recoveryToken = params.get("recovery_token");
|
|
266
452
|
if (recoveryToken) {
|
|
267
453
|
const gotrueUser = await client.recover(recoveryToken, persistSession);
|
|
454
|
+
const jwt = await gotrueUser.jwt();
|
|
455
|
+
setBrowserAuthCookies(jwt);
|
|
268
456
|
const user = toUser(gotrueUser);
|
|
269
457
|
clearHash();
|
|
270
458
|
emitAuthEvent("login", user);
|
|
@@ -277,8 +465,29 @@ var handleAuthCallback = async () => {
|
|
|
277
465
|
}
|
|
278
466
|
const emailChangeToken = params.get("email_change_token");
|
|
279
467
|
if (emailChangeToken) {
|
|
280
|
-
const
|
|
281
|
-
|
|
468
|
+
const currentUser = client.currentUser();
|
|
469
|
+
if (!currentUser) {
|
|
470
|
+
throw new AuthError("Email change verification requires an active browser session");
|
|
471
|
+
}
|
|
472
|
+
const jwt = await currentUser.jwt();
|
|
473
|
+
const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
|
|
474
|
+
const emailChangeRes = await fetch(`${identityUrl}/user`, {
|
|
475
|
+
method: "PUT",
|
|
476
|
+
headers: {
|
|
477
|
+
"Content-Type": "application/json",
|
|
478
|
+
Authorization: `Bearer ${jwt}`
|
|
479
|
+
},
|
|
480
|
+
body: JSON.stringify({ email_change_token: emailChangeToken })
|
|
481
|
+
});
|
|
482
|
+
if (!emailChangeRes.ok) {
|
|
483
|
+
const errorBody = await emailChangeRes.json().catch(() => ({}));
|
|
484
|
+
throw new AuthError(
|
|
485
|
+
errorBody.msg || `Email change verification failed (${emailChangeRes.status})`,
|
|
486
|
+
emailChangeRes.status
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
const emailChangeData = await emailChangeRes.json();
|
|
490
|
+
const user = toUser(emailChangeData);
|
|
282
491
|
clearHash();
|
|
283
492
|
emitAuthEvent("user_updated", user);
|
|
284
493
|
return { type: "email_change", user };
|
|
@@ -291,8 +500,43 @@ var handleAuthCallback = async () => {
|
|
|
291
500
|
var clearHash = () => {
|
|
292
501
|
history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
293
502
|
};
|
|
503
|
+
var hydrateSession = async () => {
|
|
504
|
+
if (!isBrowser()) return null;
|
|
505
|
+
const client = getClient();
|
|
506
|
+
const currentUser = client.currentUser();
|
|
507
|
+
if (currentUser) return toUser(currentUser);
|
|
508
|
+
const accessToken = getCookie(NF_JWT_COOKIE);
|
|
509
|
+
if (!accessToken) return null;
|
|
510
|
+
const refreshToken = getCookie(NF_REFRESH_COOKIE) ?? "";
|
|
511
|
+
const gotrueUser = await client.createUser(
|
|
512
|
+
{
|
|
513
|
+
access_token: accessToken,
|
|
514
|
+
token_type: "bearer",
|
|
515
|
+
expires_in: 3600,
|
|
516
|
+
expires_at: Math.floor(Date.now() / 1e3) + 3600,
|
|
517
|
+
refresh_token: refreshToken
|
|
518
|
+
},
|
|
519
|
+
persistSession
|
|
520
|
+
);
|
|
521
|
+
const user = toUser(gotrueUser);
|
|
522
|
+
emitAuthEvent("login", user);
|
|
523
|
+
return user;
|
|
524
|
+
};
|
|
294
525
|
|
|
295
526
|
// src/account.ts
|
|
527
|
+
var ensureCurrentUser = async () => {
|
|
528
|
+
const client = getClient();
|
|
529
|
+
let currentUser = client.currentUser();
|
|
530
|
+
if (!currentUser && isBrowser()) {
|
|
531
|
+
try {
|
|
532
|
+
await hydrateSession();
|
|
533
|
+
} catch {
|
|
534
|
+
}
|
|
535
|
+
currentUser = client.currentUser();
|
|
536
|
+
}
|
|
537
|
+
if (!currentUser) throw new AuthError("No user is currently logged in");
|
|
538
|
+
return currentUser;
|
|
539
|
+
};
|
|
296
540
|
var requestPasswordRecovery = async (email) => {
|
|
297
541
|
const client = getClient();
|
|
298
542
|
try {
|
|
@@ -336,20 +580,37 @@ var acceptInvite = async (token, password) => {
|
|
|
336
580
|
}
|
|
337
581
|
};
|
|
338
582
|
var verifyEmailChange = async (token) => {
|
|
339
|
-
|
|
583
|
+
if (!isBrowser()) throw new AuthError("verifyEmailChange() is only available in the browser");
|
|
584
|
+
const currentUser = await ensureCurrentUser();
|
|
585
|
+
const jwt = await currentUser.jwt();
|
|
586
|
+
const identityUrl = `${window.location.origin}${IDENTITY_PATH}`;
|
|
340
587
|
try {
|
|
341
|
-
const
|
|
342
|
-
|
|
588
|
+
const res = await fetch(`${identityUrl}/user`, {
|
|
589
|
+
method: "PUT",
|
|
590
|
+
headers: {
|
|
591
|
+
"Content-Type": "application/json",
|
|
592
|
+
Authorization: `Bearer ${jwt}`
|
|
593
|
+
},
|
|
594
|
+
body: JSON.stringify({ email_change_token: token })
|
|
595
|
+
});
|
|
596
|
+
if (!res.ok) {
|
|
597
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
598
|
+
throw new AuthError(
|
|
599
|
+
errorBody.msg || `Email change verification failed (${res.status})`,
|
|
600
|
+
res.status
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
const userData = await res.json();
|
|
604
|
+
const user = toUser(userData);
|
|
343
605
|
emitAuthEvent("user_updated", user);
|
|
344
606
|
return user;
|
|
345
607
|
} catch (error) {
|
|
608
|
+
if (error instanceof AuthError) throw error;
|
|
346
609
|
throw new AuthError(error.message, void 0, { cause: error });
|
|
347
610
|
}
|
|
348
611
|
};
|
|
349
612
|
var updateUser = async (updates) => {
|
|
350
|
-
const
|
|
351
|
-
const currentUser = client.currentUser();
|
|
352
|
-
if (!currentUser) throw new AuthError("No user is currently logged in");
|
|
613
|
+
const currentUser = await ensureCurrentUser();
|
|
353
614
|
try {
|
|
354
615
|
const updatedUser = await currentUser.update(updates);
|
|
355
616
|
const user = toUser(updatedUser);
|