better-auth 1.6.15 → 1.6.17
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/api/index.d.mts +2 -2
- package/dist/api/index.mjs +3 -4
- package/dist/api/middlewares/origin-check.mjs +6 -1
- package/dist/api/rate-limiter/index.mjs +259 -73
- package/dist/api/routes/account.mjs +31 -11
- package/dist/api/routes/callback.mjs +3 -3
- package/dist/api/routes/index.d.mts +1 -1
- package/dist/api/routes/password.mjs +3 -4
- package/dist/api/routes/session.d.mts +12 -1
- package/dist/api/routes/session.mjs +16 -2
- package/dist/api/routes/sign-in.mjs +5 -5
- package/dist/api/routes/sign-up.mjs +2 -2
- package/dist/api/routes/update-session.mjs +9 -4
- package/dist/api/routes/update-user.mjs +10 -12
- package/dist/auth/base.mjs +11 -7
- package/dist/client/equality.d.mts +19 -0
- package/dist/client/equality.mjs +42 -0
- package/dist/client/index.d.mts +5 -4
- package/dist/client/index.mjs +2 -1
- package/dist/client/lynx/index.d.mts +6 -5
- package/dist/client/path-to-object.d.mts +5 -2
- package/dist/client/plugins/index.d.mts +4 -1
- package/dist/client/plugins/index.mjs +4 -1
- package/dist/client/query.d.mts +4 -3
- package/dist/client/query.mjs +27 -17
- package/dist/client/react/index.d.mts +6 -5
- package/dist/client/session-atom.mjs +129 -4
- package/dist/client/session-refresh.d.mts +3 -18
- package/dist/client/session-refresh.mjs +38 -49
- package/dist/client/solid/index.d.mts +6 -5
- package/dist/client/svelte/index.d.mts +6 -5
- package/dist/client/types.d.mts +2 -2
- package/dist/client/vanilla.d.mts +6 -5
- package/dist/client/vue/index.d.mts +6 -5
- package/dist/context/create-context.mjs +3 -2
- package/dist/context/store-capabilities.mjs +12 -0
- package/dist/cookies/index.mjs +30 -2
- package/dist/db/internal-adapter.mjs +56 -0
- package/dist/oauth2/link-account.d.mts +13 -0
- package/dist/oauth2/link-account.mjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/plugins/access/access.mjs +49 -19
- package/dist/plugins/admin/access/statement.d.mts +10 -10
- package/dist/plugins/admin/access/statement.mjs +2 -0
- package/dist/plugins/admin/admin.d.mts +6 -3
- package/dist/plugins/admin/client.d.mts +6 -4
- package/dist/plugins/admin/error-codes.d.mts +2 -0
- package/dist/plugins/admin/error-codes.mjs +3 -1
- package/dist/plugins/admin/routes.mjs +73 -5
- package/dist/plugins/admin/schema.d.mts +1 -0
- package/dist/plugins/admin/schema.mjs +2 -1
- package/dist/plugins/captcha/constants.mjs +8 -1
- package/dist/plugins/captcha/index.mjs +8 -2
- package/dist/plugins/captcha/types.d.mts +21 -0
- package/dist/plugins/captcha/verify-handlers/captchafox.mjs +2 -0
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +7 -2
- package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs +7 -2
- package/dist/plugins/captcha/verify-handlers/h-captcha.mjs +2 -0
- package/dist/plugins/device-authorization/routes.mjs +16 -9
- package/dist/plugins/email-otp/routes.mjs +23 -53
- package/dist/plugins/generic-oauth/index.mjs +7 -2
- package/dist/plugins/generic-oauth/routes.mjs +20 -9
- package/dist/plugins/haveibeenpwned/index.d.mts +1 -1
- package/dist/plugins/haveibeenpwned/index.mjs +5 -1
- package/dist/plugins/index.d.mts +5 -1
- package/dist/plugins/index.mjs +4 -1
- package/dist/plugins/jwt/index.mjs +2 -2
- package/dist/plugins/mcp/client/index.mjs +1 -0
- package/dist/plugins/mcp/index.mjs +8 -0
- package/dist/plugins/multi-session/index.mjs +7 -5
- package/dist/plugins/oauth-popup/client.d.mts +82 -0
- package/dist/plugins/oauth-popup/client.mjs +203 -0
- package/dist/plugins/oauth-popup/constants.d.mts +11 -0
- package/dist/plugins/oauth-popup/constants.mjs +11 -0
- package/dist/plugins/oauth-popup/error-codes.d.mts +11 -0
- package/dist/plugins/oauth-popup/error-codes.mjs +10 -0
- package/dist/plugins/oauth-popup/index.d.mts +67 -0
- package/dist/plugins/oauth-popup/index.mjs +227 -0
- package/dist/plugins/oauth-popup/types.d.mts +30 -0
- package/dist/plugins/oauth-proxy/index.mjs +2 -2
- package/dist/plugins/oauth-proxy/utils.mjs +16 -2
- package/dist/plugins/oidc-provider/index.mjs +10 -0
- package/dist/plugins/one-tap/client.mjs +12 -6
- package/dist/plugins/one-tap/index.d.mts +1 -0
- package/dist/plugins/one-tap/index.mjs +9 -5
- package/dist/plugins/one-time-token/index.mjs +1 -3
- package/dist/plugins/open-api/generator.mjs +7 -4
- package/dist/plugins/organization/adapter.d.mts +29 -1
- package/dist/plugins/organization/adapter.mjs +66 -6
- package/dist/plugins/organization/organization.mjs +2 -0
- package/dist/plugins/organization/routes/crud-invites.mjs +55 -31
- package/dist/plugins/organization/routes/crud-members.mjs +42 -6
- package/dist/plugins/organization/routes/crud-team.mjs +51 -5
- package/dist/plugins/organization/schema.d.mts +2 -0
- package/dist/plugins/phone-number/routes.mjs +41 -36
- package/dist/plugins/siwe/index.mjs +30 -3
- package/dist/plugins/siwe/parse-message.mjs +60 -0
- package/dist/plugins/two-factor/backup-codes/index.mjs +1 -1
- package/dist/plugins/two-factor/index.mjs +9 -1
- package/dist/plugins/two-factor/otp/index.mjs +11 -13
- package/dist/plugins/two-factor/totp/index.mjs +1 -1
- package/dist/plugins/two-factor/verify-two-factor.mjs +6 -2
- package/dist/plugins/username/index.mjs +6 -6
- package/dist/test-utils/test-instance.d.mts +6 -5
- package/package.json +10 -10
|
@@ -45,6 +45,17 @@ declare const getSession: <Option extends BetterAuthOptions>() => better_call0.S
|
|
|
45
45
|
session: Session$1<Option["session"], Option["plugins"]>;
|
|
46
46
|
user: User$1<Option["user"], Option["plugins"]>;
|
|
47
47
|
} | null>;
|
|
48
|
+
/**
|
|
49
|
+
* Whether the deployment keeps sessions in a durable server-side store
|
|
50
|
+
* (a database or secondary storage) rather than only in the signed cookie.
|
|
51
|
+
*
|
|
52
|
+
* Sensitive operations use this to decide whether the cookie cache is merely an
|
|
53
|
+
* optimization that must be bypassed for an authoritative read (`true`), or the
|
|
54
|
+
* only place the session lives and therefore the authority itself (`false`, for
|
|
55
|
+
* stateless / DB-less deployments). Pass the result as `disableCookieCache` so a
|
|
56
|
+
* revoked-but-cached session cannot authorize a sensitive action.
|
|
57
|
+
*/
|
|
58
|
+
declare const isStateful: (ctx: GenericEndpointContext) => boolean;
|
|
48
59
|
declare const getSessionFromCtx: <U extends Record<string, any> = Record<string, any>, S extends Record<string, any> = Record<string, any>>(ctx: GenericEndpointContext, config?: {
|
|
49
60
|
disableCookieCache?: boolean;
|
|
50
61
|
disableRefresh?: boolean;
|
|
@@ -409,4 +420,4 @@ declare const revokeOtherSessions: better_call0.StrictEndpoint<"/revoke-other-se
|
|
|
409
420
|
status: boolean;
|
|
410
421
|
}>;
|
|
411
422
|
//#endregion
|
|
412
|
-
export { freshSessionMiddleware, getSession, getSessionFromCtx, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware };
|
|
423
|
+
export { freshSessionMiddleware, getSession, getSessionFromCtx, isStateful, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAPIError } from "../../utils/is-api-error.mjs";
|
|
2
|
+
import { hasServerSessionStore } from "../../context/store-capabilities.mjs";
|
|
2
3
|
import { symmetricDecodeJWT, verifyJWT } from "../../crypto/jwt.mjs";
|
|
3
4
|
import { parseSessionOutput, parseUserOutput } from "../../db/schema.mjs";
|
|
4
5
|
import { getDate } from "../../utils/date.mjs";
|
|
@@ -265,6 +266,17 @@ const getSession = () => createAuthEndpoint("/get-session", {
|
|
|
265
266
|
throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.FAILED_TO_GET_SESSION);
|
|
266
267
|
}
|
|
267
268
|
});
|
|
269
|
+
/**
|
|
270
|
+
* Whether the deployment keeps sessions in a durable server-side store
|
|
271
|
+
* (a database or secondary storage) rather than only in the signed cookie.
|
|
272
|
+
*
|
|
273
|
+
* Sensitive operations use this to decide whether the cookie cache is merely an
|
|
274
|
+
* optimization that must be bypassed for an authoritative read (`true`), or the
|
|
275
|
+
* only place the session lives and therefore the authority itself (`false`, for
|
|
276
|
+
* stateless / DB-less deployments). Pass the result as `disableCookieCache` so a
|
|
277
|
+
* revoked-but-cached session cannot authorize a sensitive action.
|
|
278
|
+
*/
|
|
279
|
+
const isStateful = (ctx) => hasServerSessionStore(ctx.context.options);
|
|
268
280
|
const getSessionFromCtx = async (ctx, config) => {
|
|
269
281
|
if (ctx.context.session) return ctx.context.session;
|
|
270
282
|
const session = await getSession()({
|
|
@@ -276,7 +288,9 @@ const getSessionFromCtx = async (ctx, config) => {
|
|
|
276
288
|
returnStatus: false,
|
|
277
289
|
query: {
|
|
278
290
|
...config,
|
|
279
|
-
...ctx.query
|
|
291
|
+
...ctx.query,
|
|
292
|
+
disableCookieCache: config?.disableCookieCache || ctx.query?.disableCookieCache,
|
|
293
|
+
disableRefresh: config?.disableRefresh || ctx.query?.disableRefresh
|
|
280
294
|
}
|
|
281
295
|
}).catch(() => {
|
|
282
296
|
return null;
|
|
@@ -486,4 +500,4 @@ const revokeOtherSessions = createAuthEndpoint("/revoke-other-sessions", {
|
|
|
486
500
|
return ctx.json({ status: true });
|
|
487
501
|
});
|
|
488
502
|
//#endregion
|
|
489
|
-
export { freshSessionMiddleware, getSession, getSessionFromCtx, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware };
|
|
503
|
+
export { freshSessionMiddleware, getSession, getSessionFromCtx, isStateful, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware };
|
|
@@ -80,7 +80,7 @@ const signInSocial = () => createAuthEndpoint("/sign-in/social", {
|
|
|
80
80
|
}
|
|
81
81
|
const { token, nonce } = c.body.idToken;
|
|
82
82
|
if (!await provider.verifyIdToken(token, nonce)) {
|
|
83
|
-
c.context.logger.
|
|
83
|
+
c.context.logger.warn("Invalid id token", { provider: c.body.provider });
|
|
84
84
|
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.INVALID_TOKEN);
|
|
85
85
|
}
|
|
86
86
|
const userInfo = await provider.getUserInfo({
|
|
@@ -205,26 +205,26 @@ const signInEmail = () => createAuthEndpoint("/sign-in/email", {
|
|
|
205
205
|
const user = await ctx.context.internalAdapter.findUserByEmail(email, { includeAccounts: true });
|
|
206
206
|
if (!user) {
|
|
207
207
|
await ctx.context.password.hash(password);
|
|
208
|
-
ctx.context.logger.
|
|
208
|
+
ctx.context.logger.warn("User not found");
|
|
209
209
|
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD);
|
|
210
210
|
}
|
|
211
211
|
const credentialAccount = user.accounts.find((a) => a.providerId === "credential");
|
|
212
212
|
if (!credentialAccount) {
|
|
213
213
|
await ctx.context.password.hash(password);
|
|
214
|
-
ctx.context.logger.
|
|
214
|
+
ctx.context.logger.warn("Credential account not found");
|
|
215
215
|
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD);
|
|
216
216
|
}
|
|
217
217
|
const currentPassword = credentialAccount?.password;
|
|
218
218
|
if (!currentPassword) {
|
|
219
219
|
await ctx.context.password.hash(password);
|
|
220
|
-
ctx.context.logger.
|
|
220
|
+
ctx.context.logger.warn("Password not found");
|
|
221
221
|
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD);
|
|
222
222
|
}
|
|
223
223
|
if (!await ctx.context.password.verify({
|
|
224
224
|
hash: currentPassword,
|
|
225
225
|
password
|
|
226
226
|
})) {
|
|
227
|
-
ctx.context.logger.
|
|
227
|
+
ctx.context.logger.warn("Invalid password");
|
|
228
228
|
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.INVALID_EMAIL_OR_PASSWORD);
|
|
229
229
|
}
|
|
230
230
|
if (ctx.context.options?.emailAndPassword?.requireEmailVerification && !user.user.emailVerified) {
|
|
@@ -150,12 +150,12 @@ const signUpEmail = () => createAuthEndpoint("/sign-up/email", {
|
|
|
150
150
|
if (!password || typeof password !== "string") throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD);
|
|
151
151
|
const minPasswordLength = ctx.context.password.config.minPasswordLength;
|
|
152
152
|
if (password.length < minPasswordLength) {
|
|
153
|
-
ctx.context.logger.
|
|
153
|
+
ctx.context.logger.warn("Password is too short");
|
|
154
154
|
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT);
|
|
155
155
|
}
|
|
156
156
|
const maxPasswordLength = ctx.context.password.config.maxPasswordLength;
|
|
157
157
|
if (password.length > maxPasswordLength) {
|
|
158
|
-
ctx.context.logger.
|
|
158
|
+
ctx.context.logger.warn("Password is too long");
|
|
159
159
|
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
160
160
|
}
|
|
161
161
|
const shouldReturnGenericDuplicateResponse = ctx.context.options.emailAndPassword.requireEmailVerification || ctx.context.options.emailAndPassword.autoSignIn === false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseSessionInput, parseSessionOutput } from "../../db/schema.mjs";
|
|
2
|
-
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
3
|
-
import { sessionMiddleware } from "./session.mjs";
|
|
2
|
+
import { deleteSessionCookie, setSessionCookie } from "../../cookies/index.mjs";
|
|
3
|
+
import { isStateful, sessionMiddleware } from "./session.mjs";
|
|
4
4
|
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
5
5
|
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
6
6
|
import * as z from "zod";
|
|
@@ -34,10 +34,15 @@ const updateSession = () => createAuthEndpoint("/update-session", {
|
|
|
34
34
|
const session = ctx.context.session;
|
|
35
35
|
const additionalFields = parseSessionInput(ctx.context.options, body, "update");
|
|
36
36
|
if (Object.keys(additionalFields).length === 0) throw APIError.fromStatus("BAD_REQUEST", { message: "No fields to update" });
|
|
37
|
-
const
|
|
37
|
+
const updatedSession = await ctx.context.internalAdapter.updateSession(session.session.token, {
|
|
38
38
|
...additionalFields,
|
|
39
39
|
updatedAt: /* @__PURE__ */ new Date()
|
|
40
|
-
})
|
|
40
|
+
});
|
|
41
|
+
if (!updatedSession && isStateful(ctx)) {
|
|
42
|
+
deleteSessionCookie(ctx);
|
|
43
|
+
throw APIError.from("UNAUTHORIZED", BASE_ERROR_CODES.FAILED_TO_GET_SESSION);
|
|
44
|
+
}
|
|
45
|
+
const newSession = updatedSession ?? {
|
|
41
46
|
...session.session,
|
|
42
47
|
...additionalFields,
|
|
43
48
|
updatedAt: /* @__PURE__ */ new Date()
|
|
@@ -2,7 +2,7 @@ import { originCheck } from "../middlewares/origin-check.mjs";
|
|
|
2
2
|
import { parseUserInput, parseUserOutput } from "../../db/schema.mjs";
|
|
3
3
|
import { generateRandomString } from "../../crypto/random.mjs";
|
|
4
4
|
import { deleteSessionCookie, setSessionCookie } from "../../cookies/index.mjs";
|
|
5
|
-
import { getSessionFromCtx, sensitiveSessionMiddleware, sessionMiddleware } from "./session.mjs";
|
|
5
|
+
import { getSessionFromCtx, isStateful, sensitiveSessionMiddleware, sessionMiddleware } from "./session.mjs";
|
|
6
6
|
import { createEmailVerificationToken } from "./email-verification.mjs";
|
|
7
7
|
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
8
8
|
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
@@ -150,12 +150,12 @@ const changePassword = createAuthEndpoint("/change-password", {
|
|
|
150
150
|
const session = ctx.context.session;
|
|
151
151
|
const minPasswordLength = ctx.context.password.config.minPasswordLength;
|
|
152
152
|
if (newPassword.length < minPasswordLength) {
|
|
153
|
-
ctx.context.logger.
|
|
153
|
+
ctx.context.logger.warn("Password is too short");
|
|
154
154
|
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT);
|
|
155
155
|
}
|
|
156
156
|
const maxPasswordLength = ctx.context.password.config.maxPasswordLength;
|
|
157
157
|
if (newPassword.length > maxPasswordLength) {
|
|
158
|
-
ctx.context.logger.
|
|
158
|
+
ctx.context.logger.warn("Password is too long");
|
|
159
159
|
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
160
160
|
}
|
|
161
161
|
const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account) => account.providerId === "credential" && account.password);
|
|
@@ -182,7 +182,7 @@ const changePassword = createAuthEndpoint("/change-password", {
|
|
|
182
182
|
user: parseUserOutput(ctx.context.options, session.user)
|
|
183
183
|
});
|
|
184
184
|
});
|
|
185
|
-
const setPassword = createAuthEndpoint({
|
|
185
|
+
const setPassword = createAuthEndpoint.serverOnly({
|
|
186
186
|
method: "POST",
|
|
187
187
|
body: z.object({ newPassword: z.string().meta({ description: "The new password to set is required" }) }),
|
|
188
188
|
use: [sensitiveSessionMiddleware]
|
|
@@ -191,12 +191,12 @@ const setPassword = createAuthEndpoint({
|
|
|
191
191
|
const session = ctx.context.session;
|
|
192
192
|
const minPasswordLength = ctx.context.password.config.minPasswordLength;
|
|
193
193
|
if (newPassword.length < minPasswordLength) {
|
|
194
|
-
ctx.context.logger.
|
|
194
|
+
ctx.context.logger.warn("Password is too short");
|
|
195
195
|
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT);
|
|
196
196
|
}
|
|
197
197
|
const maxPasswordLength = ctx.context.password.config.maxPasswordLength;
|
|
198
198
|
if (newPassword.length > maxPasswordLength) {
|
|
199
|
-
ctx.context.logger.
|
|
199
|
+
ctx.context.logger.warn("Password is too long");
|
|
200
200
|
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
201
201
|
}
|
|
202
202
|
const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account) => account.providerId === "credential" && account.password);
|
|
@@ -354,17 +354,15 @@ const deleteUserCallback = createAuthEndpoint("/delete-user/callback", {
|
|
|
354
354
|
code: "NOT_FOUND"
|
|
355
355
|
});
|
|
356
356
|
}
|
|
357
|
-
const session = await getSessionFromCtx(ctx);
|
|
357
|
+
const session = await getSessionFromCtx(ctx, { disableCookieCache: isStateful(ctx) });
|
|
358
358
|
if (!session) throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO);
|
|
359
|
-
const token = await ctx.context.internalAdapter.
|
|
360
|
-
if (!token || token.
|
|
361
|
-
if (token.value !== session.user.id) throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.INVALID_TOKEN);
|
|
359
|
+
const token = await ctx.context.internalAdapter.consumeVerificationValue(`delete-account-${ctx.query.token}`);
|
|
360
|
+
if (!token || token.value !== session.user.id) throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.INVALID_TOKEN);
|
|
362
361
|
const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete;
|
|
363
362
|
if (beforeDelete) await beforeDelete(session.user, ctx.request);
|
|
364
363
|
await ctx.context.internalAdapter.deleteUser(session.user.id);
|
|
365
364
|
await ctx.context.internalAdapter.deleteUserSessions(session.user.id);
|
|
366
365
|
await ctx.context.internalAdapter.deleteAccounts(session.user.id);
|
|
367
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(`delete-account-${ctx.query.token}`);
|
|
368
366
|
deleteSessionCookie(ctx);
|
|
369
367
|
const afterDelete = ctx.context.options.user.deleteUser?.afterDelete;
|
|
370
368
|
if (afterDelete) await afterDelete(session.user, ctx.request);
|
|
@@ -414,7 +412,7 @@ const changeEmail = createAuthEndpoint("/change-email", {
|
|
|
414
412
|
}
|
|
415
413
|
const newEmail = ctx.body.newEmail.toLowerCase();
|
|
416
414
|
if (newEmail === ctx.context.session.user.email) {
|
|
417
|
-
ctx.context.logger.
|
|
415
|
+
ctx.context.logger.warn("Email is the same");
|
|
418
416
|
throw APIError.fromStatus("BAD_REQUEST", { message: "Email is the same" });
|
|
419
417
|
}
|
|
420
418
|
/**
|
package/dist/auth/base.mjs
CHANGED
|
@@ -14,16 +14,20 @@ const createBetterAuth = (options, initFn) => {
|
|
|
14
14
|
let handlerCtx;
|
|
15
15
|
if (isDynamicBaseURLConfig(options.baseURL)) handlerCtx = await resolveRequestContext(ctx, request, resolveDynamicTrustedProxyHeaders(ctx.options));
|
|
16
16
|
else {
|
|
17
|
-
handlerCtx = ctx;
|
|
17
|
+
handlerCtx = Object.create(Object.getPrototypeOf(ctx), Object.getOwnPropertyDescriptors(ctx));
|
|
18
|
+
let trustOptions = ctx.options;
|
|
18
19
|
if (!ctx.options.baseURL) {
|
|
19
20
|
const baseURL = getBaseURL(void 0, basePath, request, void 0, ctx.options.advanced?.trustedProxyHeaders);
|
|
20
|
-
if (baseURL)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (!baseURL) throw new BetterAuthError("Could not get base URL from request. Please provide a valid base URL.");
|
|
22
|
+
handlerCtx.baseURL = baseURL;
|
|
23
|
+
handlerCtx.options = {
|
|
24
|
+
...ctx.options,
|
|
25
|
+
baseURL: getOrigin(baseURL) || void 0
|
|
26
|
+
};
|
|
27
|
+
trustOptions = handlerCtx.options;
|
|
24
28
|
}
|
|
25
|
-
handlerCtx.trustedOrigins = await getTrustedOrigins(
|
|
26
|
-
handlerCtx.trustedProviders = await getTrustedProviders(
|
|
29
|
+
handlerCtx.trustedOrigins = await getTrustedOrigins(trustOptions, request);
|
|
30
|
+
handlerCtx.trustedProviders = await getTrustedProviders(trustOptions, request);
|
|
27
31
|
}
|
|
28
32
|
const { handler } = router(handlerCtx, options);
|
|
29
33
|
return runWithAdapter(handlerCtx.adapter, () => handler(request));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Store, StoreValue } from "nanostores";
|
|
2
|
+
|
|
3
|
+
//#region src/client/equality.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Deep structural equality for JSON-serializable values.
|
|
6
|
+
* Handles: primitives, null, arrays, and plain objects.
|
|
7
|
+
* Short-circuits on referential equality at every recursion level.
|
|
8
|
+
*/
|
|
9
|
+
declare function isJsonEqual(a: unknown, b: unknown): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Attach an equality gate to a nanostores atom via `onSet`.
|
|
12
|
+
* When `isEqual(currentValue, newValue)` returns true, the `set()` call
|
|
13
|
+
* is aborted: no listeners fire, no framework re-renders occur.
|
|
14
|
+
*
|
|
15
|
+
* Returns the unsubscribe function from `onSet`.
|
|
16
|
+
*/
|
|
17
|
+
declare function withEquality<S extends Store>(store: S, isEqual: (a: StoreValue<S>, b: StoreValue<S>) => boolean): () => void;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { isJsonEqual, withEquality };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { onSet } from "nanostores";
|
|
2
|
+
//#region src/client/equality.ts
|
|
3
|
+
function isPlainObject(value) {
|
|
4
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5
|
+
const prototype = Object.getPrototypeOf(value);
|
|
6
|
+
return prototype === Object.prototype || prototype === null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Deep structural equality for JSON-serializable values.
|
|
10
|
+
* Handles: primitives, null, arrays, and plain objects.
|
|
11
|
+
* Short-circuits on referential equality at every recursion level.
|
|
12
|
+
*/
|
|
13
|
+
function isJsonEqual(a, b) {
|
|
14
|
+
if (a === b) return true;
|
|
15
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
16
|
+
if (a.length !== b.length) return false;
|
|
17
|
+
for (let i = 0; i < a.length; i++) if (!isJsonEqual(a[i], b[i])) return false;
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
21
|
+
const keysA = Object.keys(a);
|
|
22
|
+
const keysB = Object.keys(b);
|
|
23
|
+
if (keysA.length !== keysB.length) return false;
|
|
24
|
+
for (const key of keysA) if (!(key in b) || !isJsonEqual(a[key], b[key])) return false;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Attach an equality gate to a nanostores atom via `onSet`.
|
|
31
|
+
* When `isEqual(currentValue, newValue)` returns true, the `set()` call
|
|
32
|
+
* is aborted: no listeners fire, no framework re-renders occur.
|
|
33
|
+
*
|
|
34
|
+
* Returns the unsubscribe function from `onSet`.
|
|
35
|
+
*/
|
|
36
|
+
function withEquality(store, isEqual) {
|
|
37
|
+
return onSet(store, ({ newValue, abort }) => {
|
|
38
|
+
if (isEqual(store.value, newValue)) abort();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { isJsonEqual, withEquality };
|
package/dist/client/index.d.mts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ExtractPluginField, HasRequiredKeys, InferPluginFieldFromTuple, IsAny, OverrideMerge, Prettify, PrettifyDeep, RequiredKeysOf, StripEmptyObjects, UnionToIntersection } from "../types/helper.mjs";
|
|
2
|
-
import { CamelCase, InferCtx, InferRoute, InferRoutes, InferSignUpEmailCtx, InferUserUpdateCtx, MergeRoutes, PathToObject, ProxyRequest } from "./path-to-object.mjs";
|
|
2
|
+
import { CamelCase, InferCtx, InferRoute, InferRoutes, InferSessionUpdateCtx, InferSignUpEmailCtx, InferUserUpdateCtx, MergeRoutes, PathToObject, ProxyRequest } from "./path-to-object.mjs";
|
|
3
3
|
import { BetterAuthClientOptions, BetterAuthClientPlugin, ClientAtomListener, ClientStore, InferActions, InferAdditionalFromClient, InferClientAPI, InferErrorCodes, InferSessionFromClient, InferUserFromClient, IsSignal, SessionQueryParams } from "./types.mjs";
|
|
4
4
|
import { BroadcastChannel, BroadcastListener, BroadcastMessage, getGlobalBroadcastChannel, kBroadcastChannel } from "./broadcast-channel.mjs";
|
|
5
|
+
import { isJsonEqual, withEquality } from "./equality.mjs";
|
|
5
6
|
import { FocusListener, FocusManager, kFocusManager } from "./focus-manager.mjs";
|
|
6
7
|
import { OnlineListener, OnlineManager, kOnlineManager } from "./online-manager.mjs";
|
|
7
8
|
import { parseJSON } from "./parser.mjs";
|
|
8
|
-
import { AuthQueryAtom, useAuthQuery } from "./query.mjs";
|
|
9
|
-
import { SessionRefreshOptions,
|
|
9
|
+
import { AuthQueryAtom, AuthQueryState, useAuthQuery } from "./query.mjs";
|
|
10
|
+
import { SessionRefreshOptions, createSessionRefreshManager } from "./session-refresh.mjs";
|
|
10
11
|
import { AuthClient, createAuthClient } from "./vanilla.mjs";
|
|
11
12
|
import { AccessControl, ArrayElement, ExactRoleStatements, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, Statements, SubArray, Subset } from "../plugins/access/types.mjs";
|
|
12
13
|
import { AuthorizeResponse, createAccessControl, role } from "../plugins/access/access.mjs";
|
|
@@ -31,4 +32,4 @@ declare function InferAuth<O extends {
|
|
|
31
32
|
options: BetterAuthOptions;
|
|
32
33
|
}>(): O["options"];
|
|
33
34
|
//#endregion
|
|
34
|
-
export { AccessControl, ArrayElement, AuthClient, AuthQueryAtom, AuthorizeResponse, BetterAuthClientOptions, BetterAuthClientPlugin, BroadcastChannel, BroadcastListener, BroadcastMessage, CamelCase, ClientAtomListener, ClientStore, type DBPrimitive, DefaultOrganizationPlugin, DynamicAccessControlEndpoints, ExactRoleStatements, ExtractPluginField, type FocusListener, type FocusManager, HasRequiredKeys, InferActions, InferAdditionalFromClient, InferAuth, InferClientAPI, InferCtx, InferErrorCodes, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPlugin, InferPluginFieldFromTuple, InferRoute, InferRoutes, InferSessionFromClient, InferSignUpEmailCtx, InferTeam, InferUserFromClient, InferUserUpdateCtx, Invitation, InvitationInput, InvitationStatus, IsAny, IsSignal, Member, MemberInput, MergeRoutes, type OnlineListener, type OnlineManager, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, OverrideMerge, PathToObject, Prettify, PrettifyDeep, ProxyRequest, RequiredKeysOf, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, SessionQueryParams, SessionRefreshOptions,
|
|
35
|
+
export { AccessControl, ArrayElement, AuthClient, AuthQueryAtom, AuthQueryState, AuthorizeResponse, BetterAuthClientOptions, BetterAuthClientPlugin, BroadcastChannel, BroadcastListener, BroadcastMessage, CamelCase, ClientAtomListener, ClientStore, type DBPrimitive, DefaultOrganizationPlugin, DynamicAccessControlEndpoints, ExactRoleStatements, ExtractPluginField, type FocusListener, type FocusManager, HasRequiredKeys, InferActions, InferAdditionalFromClient, InferAuth, InferClientAPI, InferCtx, InferErrorCodes, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPlugin, InferPluginFieldFromTuple, InferRoute, InferRoutes, InferSessionFromClient, InferSessionUpdateCtx, InferSignUpEmailCtx, InferTeam, InferUserFromClient, InferUserUpdateCtx, Invitation, InvitationInput, InvitationStatus, IsAny, IsSignal, Member, MemberInput, MergeRoutes, type OnlineListener, type OnlineManager, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, OverrideMerge, PathToObject, Prettify, PrettifyDeep, ProxyRequest, RequiredKeysOf, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, SessionQueryParams, SessionRefreshOptions, Statements, StripEmptyObjects, SubArray, Subset, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, type UnionToIntersection, createAccessControl, createAuthClient, createSessionRefreshManager, defaultRolesSchema, getGlobalBroadcastChannel, getOrgAdapter, hasPermission, invitationSchema, invitationStatus, isJsonEqual, kBroadcastChannel, kFocusManager, kOnlineManager, memberSchema, organization, organizationRoleSchema, organizationSchema, parseJSON, parseRoles, role, roleSchema, teamMemberSchema, teamSchema, useAuthQuery, withEquality };
|
package/dist/client/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PACKAGE_VERSION } from "../version.mjs";
|
|
2
2
|
import { getGlobalBroadcastChannel, kBroadcastChannel } from "./broadcast-channel.mjs";
|
|
3
|
+
import { isJsonEqual, withEquality } from "./equality.mjs";
|
|
3
4
|
import { kFocusManager } from "./focus-manager.mjs";
|
|
4
5
|
import { kOnlineManager } from "./online-manager.mjs";
|
|
5
6
|
import { parseJSON } from "./parser.mjs";
|
|
@@ -18,4 +19,4 @@ function InferAuth() {
|
|
|
18
19
|
return {};
|
|
19
20
|
}
|
|
20
21
|
//#endregion
|
|
21
|
-
export { InferAuth, InferPlugin, createAuthClient, createSessionRefreshManager, getGlobalBroadcastChannel, kBroadcastChannel, kFocusManager, kOnlineManager, parseJSON, useAuthQuery };
|
|
22
|
+
export { InferAuth, InferPlugin, createAuthClient, createSessionRefreshManager, getGlobalBroadcastChannel, isJsonEqual, kBroadcastChannel, kFocusManager, kOnlineManager, parseJSON, useAuthQuery, withEquality };
|
|
@@ -69,11 +69,6 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
|
|
|
69
69
|
priority?: RequestPriority | undefined;
|
|
70
70
|
cache?: RequestCache | undefined;
|
|
71
71
|
credentials?: RequestCredentials;
|
|
72
|
-
headers?: (HeadersInit & (HeadersInit | {
|
|
73
|
-
accept: "application/json" | "text/plain" | "application/octet-stream";
|
|
74
|
-
"content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
|
|
75
|
-
authorization: "Bearer" | "Basic";
|
|
76
|
-
})) | undefined;
|
|
77
72
|
integrity?: string | undefined;
|
|
78
73
|
keepalive?: boolean | undefined;
|
|
79
74
|
method: string;
|
|
@@ -103,6 +98,12 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
|
|
|
103
98
|
prefix: string | (() => string | undefined) | undefined;
|
|
104
99
|
value: string | (() => string | undefined) | undefined;
|
|
105
100
|
}) | undefined;
|
|
101
|
+
headers?: {} | {
|
|
102
|
+
[x: string]: string | undefined;
|
|
103
|
+
accept?: ((string & {}) | "application/json" | "text/plain" | "application/octet-stream") | undefined;
|
|
104
|
+
"content-type"?: ((string & {}) | "application/x-www-form-urlencoded" | "application/json" | "text/plain" | "application/octet-stream" | "multipart/form-data") | undefined;
|
|
105
|
+
authorization?: ((string & {}) | `Bearer ${string}` | `Basic ${string}`) | undefined;
|
|
106
|
+
} | undefined;
|
|
106
107
|
body?: any;
|
|
107
108
|
query?: any;
|
|
108
109
|
params?: any;
|
|
@@ -30,6 +30,9 @@ type InferUserUpdateCtx<ClientOpts extends BetterAuthClientOptions, FetchOptions
|
|
|
30
30
|
name?: string | undefined;
|
|
31
31
|
fetchOptions?: FetchOptions | undefined;
|
|
32
32
|
} & Partial<UnionToIntersection<InferAdditionalFromClient<ClientOpts, "user", "input">>>;
|
|
33
|
+
type InferSessionUpdateCtx<ClientOpts extends BetterAuthClientOptions, FetchOptions extends ClientFetchOption> = {
|
|
34
|
+
fetchOptions?: FetchOptions | undefined;
|
|
35
|
+
} & Partial<UnionToIntersection<InferAdditionalFromClient<ClientOpts, "session", "input">>>;
|
|
33
36
|
type InferCtxQuery<C extends InputContext<any, any>, FetchOptions extends ClientFetchOption> = C["query"] extends Record<string, any> ? {
|
|
34
37
|
query: C["query"];
|
|
35
38
|
fetchOptions?: FetchOptions | undefined;
|
|
@@ -51,7 +54,7 @@ type InferRoute<API, COpts extends BetterAuthClientOptions> = API extends Record
|
|
|
51
54
|
scope: "http";
|
|
52
55
|
} | {
|
|
53
56
|
scope: "server";
|
|
54
|
-
} ? {} : PathToObject<T["path"], T extends ((ctx: infer C) => infer R) ? C extends InputContext<any, any> ? <FetchOptions extends ClientFetchOption<Partial<C["body"]> & Record<string, any>, Partial<C["query"]> & Record<string, any>, C["params"]>>(...data: HasRequiredKeys<InferCtx<C, FetchOptions>> extends true ? [Prettify$1<T["path"] extends `/sign-up/email` ? InferSignUpEmailCtx<COpts, FetchOptions> : InferCtx<C, FetchOptions>>, FetchOptions?] : [Prettify$1<T["path"] extends `/update-user` ? InferUserUpdateCtx<COpts, FetchOptions> : InferCtx<C, FetchOptions>>?, FetchOptions?]) => Promise<BetterFetchResponse<T["options"]["metadata"] extends {
|
|
57
|
+
} ? {} : PathToObject<T["path"], T extends ((ctx: infer C) => infer R) ? C extends InputContext<any, any> ? <FetchOptions extends ClientFetchOption<Partial<C["body"]> & Record<string, any>, Partial<C["query"]> & Record<string, any>, C["params"]>>(...data: HasRequiredKeys<InferCtx<C, FetchOptions>> extends true ? [Prettify$1<T["path"] extends `/sign-up/email` ? InferSignUpEmailCtx<COpts, FetchOptions> : InferCtx<C, FetchOptions>>, FetchOptions?] : [Prettify$1<T["path"] extends `/update-user` ? InferUserUpdateCtx<COpts, FetchOptions> : T["path"] extends `/update-session` ? InferSessionUpdateCtx<COpts, FetchOptions> : InferCtx<C, FetchOptions>>?, FetchOptions?]) => Promise<BetterFetchResponse<T["options"]["metadata"] extends {
|
|
55
58
|
CUSTOM_SESSION: boolean;
|
|
56
59
|
} ? MergeCustomSessionWithInferred<NonNullable<Awaited<R>>, COpts> : T["path"] extends "/get-session" ? {
|
|
57
60
|
user: InferUserFromClient<COpts>;
|
|
@@ -69,4 +72,4 @@ type ProxyRequest = {
|
|
|
69
72
|
[key: string]: any;
|
|
70
73
|
};
|
|
71
74
|
//#endregion
|
|
72
|
-
export { CamelCase, InferCtx, InferRoute, InferRoutes, InferSignUpEmailCtx, InferUserUpdateCtx, MergeRoutes, PathToObject, ProxyRequest };
|
|
75
|
+
export { CamelCase, InferCtx, InferRoute, InferRoutes, InferSessionUpdateCtx, InferSignUpEmailCtx, InferUserUpdateCtx, MergeRoutes, PathToObject, ProxyRequest };
|
|
@@ -19,6 +19,8 @@ import { JWKOptions, JWSAlgorithms, Jwk, JwtOptions } from "../../plugins/jwt/ty
|
|
|
19
19
|
import { AuthorizationQuery, Client, CodeVerificationValue, OAuthAccessToken, OIDCMetadata, OIDCOptions, TokenBody } from "../../plugins/oidc-provider/types.mjs";
|
|
20
20
|
import { MULTI_SESSION_ERROR_CODES } from "../../plugins/multi-session/error-codes.mjs";
|
|
21
21
|
import { MultiSessionConfig } from "../../plugins/multi-session/index.mjs";
|
|
22
|
+
import { POPUP_TOKEN_STORAGE_KEY } from "../../plugins/oauth-popup/constants.mjs";
|
|
23
|
+
import { OAUTH_POPUP_ERROR_CODES } from "../../plugins/oauth-popup/error-codes.mjs";
|
|
22
24
|
import { OneTimeTokenOptions } from "../../plugins/one-time-token/index.mjs";
|
|
23
25
|
import { PhoneNumberOptions, UserWithPhoneNumber } from "../../plugins/phone-number/types.mjs";
|
|
24
26
|
import { BackupCodeOptions, backupCode2fa, encodeBackupCodes, generateBackupCodes, getBackupCodes, verifyBackupCode } from "../../plugins/two-factor/backup-codes/index.mjs";
|
|
@@ -44,6 +46,7 @@ import { jwtClient } from "../../plugins/jwt/client.mjs";
|
|
|
44
46
|
import { LastLoginMethodClientConfig, lastLoginMethodClient } from "../../plugins/last-login-method/client.mjs";
|
|
45
47
|
import { magicLinkClient } from "../../plugins/magic-link/client.mjs";
|
|
46
48
|
import { multiSessionClient } from "../../plugins/multi-session/client.mjs";
|
|
49
|
+
import { SignInPopupOptions, SignInPopupResult, createSignInPopup, getStoredPopupToken, oauthPopupClient, popupBearerFetchPlugin } from "../../plugins/oauth-popup/client.mjs";
|
|
47
50
|
import { OidcClientPlugin, oidcClient } from "../../plugins/oidc-provider/client.mjs";
|
|
48
51
|
import { GoogleOneTapActionOptions, GoogleOneTapOptions, GsiButtonConfiguration, oneTapClient } from "../../plugins/one-tap/client.mjs";
|
|
49
52
|
import { oneTimeTokenClient } from "../../plugins/one-time-token/client.mjs";
|
|
@@ -53,4 +56,4 @@ import { phoneNumberClient } from "../../plugins/phone-number/client.mjs";
|
|
|
53
56
|
import { siweClient } from "../../plugins/siwe/client.mjs";
|
|
54
57
|
import { usernameClient } from "../../plugins/username/client.mjs";
|
|
55
58
|
import { InferServerPlugin } from "./infer-plugin.mjs";
|
|
56
|
-
export { ADMIN_ERROR_CODES, ANONYMOUS_ERROR_CODES, AdminClientOptions, AdminOptions, AnonymousOptions, AnonymousSession, Auth0Options, AuthorizationQuery, BackupCodeOptions, BaseOAuthProviderOptions, Client, CodeVerificationValue, EMAIL_OTP_ERROR_CODES, ExtractPluginField, type FieldAttributeToObject, GENERIC_OAUTH_ERROR_CODES, GenericOAuthConfig, GenericOAuthOptions, GoogleOneTapActionOptions, GoogleOneTapOptions, GsiButtonConfiguration, GumroadOptions, HasRequiredKeys, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginFieldFromTuple, InferServerPlugin, InferTeam, Invitation, InvitationInput, InvitationStatus, IsAny, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodClientConfig, LineOptions, MULTI_SESSION_ERROR_CODES, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAuthAccessToken, OIDCMetadata, OIDCOptions, ORGANIZATION_ERROR_CODES, OTPOptions, OidcClientPlugin, OktaOptions, OneTimeTokenOptions, Organization, OrganizationClientOptions, OrganizationInput, OrganizationRole, OrganizationSchema, OverrideMerge, PHONE_NUMBER_ERROR_CODES, PatreonOptions, PhoneNumberOptions, Prettify, PrettifyDeep, type RemoveFieldsWithReturnedFalse, RequiredKeysOf, SessionWithImpersonatedBy, SlackOptions, StripEmptyObjects, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamInput, TeamMember, TeamMemberInput, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UnionToIntersection, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, adminClient, anonymousClient, auth0, backupCode2fa, clientSideHasPermission, customSessionClient, defaultRolesSchema, deviceAuthorizationClient, emailOTPClient, encodeBackupCodes, generateBackupCodes, genericOAuthClient, getBackupCodes, gumroad, hubspot, inferAdditionalFields, inferOrgAdditionalFields, invitationSchema, invitationStatus, jwtClient, keycloak, lastLoginMethodClient, line, magicLinkClient, memberSchema, microsoftEntraId, multiSessionClient, oidcClient, okta, oneTapClient, oneTimeTokenClient, organizationClient, organizationRoleSchema, organizationSchema, otp2fa, patreon, phoneNumberClient, roleSchema, schema, siweClient, slack, teamMemberSchema, teamSchema, totp2fa, twoFactorClient, usernameClient, verifyBackupCode };
|
|
59
|
+
export { ADMIN_ERROR_CODES, ANONYMOUS_ERROR_CODES, AdminClientOptions, AdminOptions, AnonymousOptions, AnonymousSession, Auth0Options, AuthorizationQuery, BackupCodeOptions, BaseOAuthProviderOptions, Client, CodeVerificationValue, EMAIL_OTP_ERROR_CODES, ExtractPluginField, type FieldAttributeToObject, GENERIC_OAUTH_ERROR_CODES, GenericOAuthConfig, GenericOAuthOptions, GoogleOneTapActionOptions, GoogleOneTapOptions, GsiButtonConfiguration, GumroadOptions, HasRequiredKeys, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginFieldFromTuple, InferServerPlugin, InferTeam, Invitation, InvitationInput, InvitationStatus, IsAny, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodClientConfig, LineOptions, MULTI_SESSION_ERROR_CODES, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAUTH_POPUP_ERROR_CODES, OAuthAccessToken, OIDCMetadata, OIDCOptions, ORGANIZATION_ERROR_CODES, OTPOptions, OidcClientPlugin, OktaOptions, OneTimeTokenOptions, Organization, OrganizationClientOptions, OrganizationInput, OrganizationRole, OrganizationSchema, OverrideMerge, PHONE_NUMBER_ERROR_CODES, POPUP_TOKEN_STORAGE_KEY, PatreonOptions, PhoneNumberOptions, Prettify, PrettifyDeep, type RemoveFieldsWithReturnedFalse, RequiredKeysOf, SessionWithImpersonatedBy, SignInPopupOptions, SignInPopupResult, SlackOptions, StripEmptyObjects, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamInput, TeamMember, TeamMemberInput, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UnionToIntersection, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, adminClient, anonymousClient, auth0, backupCode2fa, clientSideHasPermission, createSignInPopup, customSessionClient, defaultRolesSchema, deviceAuthorizationClient, emailOTPClient, encodeBackupCodes, generateBackupCodes, genericOAuthClient, getBackupCodes, getStoredPopupToken, gumroad, hubspot, inferAdditionalFields, inferOrgAdditionalFields, invitationSchema, invitationStatus, jwtClient, keycloak, lastLoginMethodClient, line, magicLinkClient, memberSchema, microsoftEntraId, multiSessionClient, oauthPopupClient, oidcClient, okta, oneTapClient, oneTimeTokenClient, organizationClient, organizationRoleSchema, organizationSchema, otp2fa, patreon, phoneNumberClient, popupBearerFetchPlugin, roleSchema, schema, siweClient, slack, teamMemberSchema, teamSchema, totp2fa, twoFactorClient, usernameClient, verifyBackupCode };
|
|
@@ -14,6 +14,9 @@ import { lastLoginMethodClient } from "../../plugins/last-login-method/client.mj
|
|
|
14
14
|
import { magicLinkClient } from "../../plugins/magic-link/client.mjs";
|
|
15
15
|
import { MULTI_SESSION_ERROR_CODES } from "../../plugins/multi-session/error-codes.mjs";
|
|
16
16
|
import { multiSessionClient } from "../../plugins/multi-session/client.mjs";
|
|
17
|
+
import { POPUP_TOKEN_STORAGE_KEY } from "../../plugins/oauth-popup/constants.mjs";
|
|
18
|
+
import { OAUTH_POPUP_ERROR_CODES } from "../../plugins/oauth-popup/error-codes.mjs";
|
|
19
|
+
import { createSignInPopup, getStoredPopupToken, oauthPopupClient, popupBearerFetchPlugin } from "../../plugins/oauth-popup/client.mjs";
|
|
17
20
|
import { oidcClient } from "../../plugins/oidc-provider/client.mjs";
|
|
18
21
|
import { oneTapClient } from "../../plugins/one-tap/client.mjs";
|
|
19
22
|
import { oneTimeTokenClient } from "../../plugins/one-time-token/client.mjs";
|
|
@@ -27,4 +30,4 @@ import { twoFactorClient } from "../../plugins/two-factor/client.mjs";
|
|
|
27
30
|
import { USERNAME_ERROR_CODES } from "../../plugins/username/error-codes.mjs";
|
|
28
31
|
import { usernameClient } from "../../plugins/username/client.mjs";
|
|
29
32
|
import { InferServerPlugin } from "./infer-plugin.mjs";
|
|
30
|
-
export { ADMIN_ERROR_CODES, ANONYMOUS_ERROR_CODES, EMAIL_OTP_ERROR_CODES, GENERIC_OAUTH_ERROR_CODES, InferServerPlugin, MULTI_SESSION_ERROR_CODES, ORGANIZATION_ERROR_CODES, PHONE_NUMBER_ERROR_CODES, TWO_FACTOR_ERROR_CODES, USERNAME_ERROR_CODES, adminClient, anonymousClient, clientSideHasPermission, customSessionClient, deviceAuthorizationClient, emailOTPClient, genericOAuthClient, inferAdditionalFields, inferOrgAdditionalFields, jwtClient, lastLoginMethodClient, magicLinkClient, multiSessionClient, oidcClient, oneTapClient, oneTimeTokenClient, organizationClient, phoneNumberClient, siweClient, twoFactorClient, usernameClient };
|
|
33
|
+
export { ADMIN_ERROR_CODES, ANONYMOUS_ERROR_CODES, EMAIL_OTP_ERROR_CODES, GENERIC_OAUTH_ERROR_CODES, InferServerPlugin, MULTI_SESSION_ERROR_CODES, OAUTH_POPUP_ERROR_CODES, ORGANIZATION_ERROR_CODES, PHONE_NUMBER_ERROR_CODES, POPUP_TOKEN_STORAGE_KEY, TWO_FACTOR_ERROR_CODES, USERNAME_ERROR_CODES, adminClient, anonymousClient, clientSideHasPermission, createSignInPopup, customSessionClient, deviceAuthorizationClient, emailOTPClient, genericOAuthClient, getStoredPopupToken, inferAdditionalFields, inferOrgAdditionalFields, jwtClient, lastLoginMethodClient, magicLinkClient, multiSessionClient, oauthPopupClient, oidcClient, oneTapClient, oneTimeTokenClient, organizationClient, phoneNumberClient, popupBearerFetchPlugin, siweClient, twoFactorClient, usernameClient };
|
package/dist/client/query.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ import { PreinitializedWritableAtom } from "nanostores";
|
|
|
4
4
|
import { BetterFetch, BetterFetchError } from "@better-fetch/fetch";
|
|
5
5
|
|
|
6
6
|
//#region src/client/query.d.ts
|
|
7
|
-
type
|
|
7
|
+
type AuthQueryState<T> = {
|
|
8
8
|
data: null | T;
|
|
9
9
|
error: null | BetterFetchError;
|
|
10
10
|
isPending: boolean;
|
|
@@ -12,11 +12,12 @@ type AuthQueryAtom<T> = PreinitializedWritableAtom<{
|
|
|
12
12
|
refetch: (queryParams?: {
|
|
13
13
|
query?: SessionQueryParams;
|
|
14
14
|
} | undefined) => Promise<void>;
|
|
15
|
-
}
|
|
15
|
+
};
|
|
16
|
+
type AuthQueryAtom<T> = PreinitializedWritableAtom<AuthQueryState<T>>;
|
|
16
17
|
declare const useAuthQuery: <T>(initializedAtom: PreinitializedWritableAtom<any> | PreinitializedWritableAtom<any>[], path: string, $fetch: BetterFetch, options?: (((value: {
|
|
17
18
|
data: null | T;
|
|
18
19
|
error: null | BetterFetchError;
|
|
19
20
|
isPending: boolean;
|
|
20
21
|
}) => ClientFetchOption) | ClientFetchOption) | undefined) => AuthQueryAtom<T>;
|
|
21
22
|
//#endregion
|
|
22
|
-
export { AuthQueryAtom, useAuthQuery };
|
|
23
|
+
export { AuthQueryAtom, AuthQueryState, useAuthQuery };
|
package/dist/client/query.mjs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { isJsonEqual, withEquality } from "./equality.mjs";
|
|
1
2
|
import { atom, onMount } from "nanostores";
|
|
2
3
|
//#region src/client/query.ts
|
|
3
4
|
const isServer = () => typeof window === "undefined";
|
|
5
|
+
function isAuthQueryStateEqual(a, b) {
|
|
6
|
+
return isJsonEqual(a.data, b.data) && a.error === b.error && a.isPending === b.isPending && a.isRefetching === b.isRefetching && a.refetch === b.refetch;
|
|
7
|
+
}
|
|
4
8
|
const useAuthQuery = (initializedAtom, path, $fetch, options) => {
|
|
5
9
|
const value = atom({
|
|
6
10
|
data: null,
|
|
@@ -9,6 +13,7 @@ const useAuthQuery = (initializedAtom, path, $fetch, options) => {
|
|
|
9
13
|
isRefetching: false,
|
|
10
14
|
refetch: (queryParams) => fn(queryParams)
|
|
11
15
|
});
|
|
16
|
+
withEquality(value, isAuthQueryStateEqual);
|
|
12
17
|
const fn = async (queryParams) => {
|
|
13
18
|
return new Promise((resolve) => {
|
|
14
19
|
const opts = typeof options === "function" ? options({
|
|
@@ -23,8 +28,10 @@ const useAuthQuery = (initializedAtom, path, $fetch, options) => {
|
|
|
23
28
|
...queryParams?.query
|
|
24
29
|
},
|
|
25
30
|
async onSuccess(context) {
|
|
31
|
+
const current = value.get();
|
|
32
|
+
const stableData = current.data != null && context.data != null && isJsonEqual(current.data, context.data) ? current.data : context.data;
|
|
26
33
|
value.set({
|
|
27
|
-
data:
|
|
34
|
+
data: stableData,
|
|
28
35
|
error: null,
|
|
29
36
|
isPending: false,
|
|
30
37
|
isRefetching: false,
|
|
@@ -73,23 +80,26 @@ const useAuthQuery = (initializedAtom, path, $fetch, options) => {
|
|
|
73
80
|
};
|
|
74
81
|
initializedAtom = Array.isArray(initializedAtom) ? initializedAtom : [initializedAtom];
|
|
75
82
|
let isInitialized = false;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
const cleanups = [];
|
|
84
|
+
for (const initAtom of initializedAtom) {
|
|
85
|
+
const unbind = initAtom.subscribe(async () => {
|
|
86
|
+
if (isServer()) return;
|
|
87
|
+
if (isInitialized) await fn();
|
|
88
|
+
else onMount(value, () => {
|
|
89
|
+
const timeoutId = setTimeout(async () => {
|
|
90
|
+
if (!isInitialized) {
|
|
91
|
+
isInitialized = true;
|
|
92
|
+
await fn();
|
|
93
|
+
}
|
|
94
|
+
}, 0);
|
|
95
|
+
return () => {
|
|
96
|
+
for (const u of cleanups) u();
|
|
97
|
+
clearTimeout(timeoutId);
|
|
98
|
+
};
|
|
99
|
+
});
|
|
91
100
|
});
|
|
92
|
-
|
|
101
|
+
cleanups.push(unbind);
|
|
102
|
+
}
|
|
93
103
|
return value;
|
|
94
104
|
};
|
|
95
105
|
//#endregion
|
|
@@ -70,11 +70,6 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
|
|
|
70
70
|
priority?: RequestPriority | undefined;
|
|
71
71
|
cache?: RequestCache | undefined;
|
|
72
72
|
credentials?: RequestCredentials;
|
|
73
|
-
headers?: (HeadersInit & (HeadersInit | {
|
|
74
|
-
accept: "application/json" | "text/plain" | "application/octet-stream";
|
|
75
|
-
"content-type": "application/json" | "text/plain" | "application/x-www-form-urlencoded" | "multipart/form-data" | "application/octet-stream";
|
|
76
|
-
authorization: "Bearer" | "Basic";
|
|
77
|
-
})) | undefined;
|
|
78
73
|
integrity?: string | undefined;
|
|
79
74
|
keepalive?: boolean | undefined;
|
|
80
75
|
method: string;
|
|
@@ -104,6 +99,12 @@ declare function createAuthClient<Option extends BetterAuthClientOptions>(option
|
|
|
104
99
|
prefix: string | (() => string | undefined) | undefined;
|
|
105
100
|
value: string | (() => string | undefined) | undefined;
|
|
106
101
|
}) | undefined;
|
|
102
|
+
headers?: {} | {
|
|
103
|
+
[x: string]: string | undefined;
|
|
104
|
+
accept?: ((string & {}) | "application/json" | "text/plain" | "application/octet-stream") | undefined;
|
|
105
|
+
"content-type"?: ((string & {}) | "application/x-www-form-urlencoded" | "application/json" | "text/plain" | "application/octet-stream" | "multipart/form-data") | undefined;
|
|
106
|
+
authorization?: ((string & {}) | `Bearer ${string}` | `Basic ${string}`) | undefined;
|
|
107
|
+
} | undefined;
|
|
107
108
|
body?: any;
|
|
108
109
|
query?: any;
|
|
109
110
|
params?: any;
|