better-auth 1.6.10 → 1.6.12
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 +8 -2
- package/dist/api/routes/callback.d.mts +1 -1
- package/dist/api/routes/callback.mjs +36 -40
- package/dist/api/routes/email-verification.d.mts +1 -0
- package/dist/api/routes/email-verification.mjs +4 -3
- package/dist/api/routes/session.mjs +14 -9
- package/dist/api/routes/sign-in.d.mts +1 -0
- package/dist/api/routes/sign-in.mjs +2 -1
- package/dist/api/routes/sign-up.d.mts +1 -0
- package/dist/api/routes/sign-up.mjs +9 -7
- package/dist/api/routes/update-user.mjs +5 -5
- package/dist/client/index.d.mts +2 -2
- package/dist/client/parser.mjs +0 -1
- package/dist/client/plugins/index.d.mts +3 -3
- package/dist/client/proxy.mjs +2 -1
- package/dist/context/helpers.mjs +3 -2
- package/dist/cookies/cookie-utils.d.mts +24 -1
- package/dist/cookies/cookie-utils.mjs +85 -22
- package/dist/cookies/index.d.mts +2 -3
- package/dist/cookies/index.mjs +39 -11
- package/dist/cookies/session-store.mjs +4 -23
- package/dist/db/get-migration.mjs +4 -4
- package/dist/db/index.d.mts +2 -2
- package/dist/db/index.mjs +3 -2
- package/dist/db/internal-adapter.mjs +96 -1
- package/dist/db/schema.d.mts +15 -2
- package/dist/db/schema.mjs +26 -1
- package/dist/db/with-hooks.d.mts +1 -0
- package/dist/db/with-hooks.mjs +58 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/oauth2/errors.mjs +16 -1
- package/dist/oauth2/link-account.mjs +6 -4
- package/dist/oauth2/state.mjs +8 -2
- package/dist/package.mjs +1 -1
- package/dist/plugins/access/access.d.mts +3 -15
- package/dist/plugins/access/access.mjs +11 -6
- package/dist/plugins/access/index.d.mts +2 -2
- package/dist/plugins/access/types.d.mts +11 -4
- package/dist/plugins/admin/access/statement.d.mts +29 -93
- package/dist/plugins/admin/admin.mjs +0 -4
- package/dist/plugins/admin/client.d.mts +1 -1
- package/dist/plugins/admin/routes.mjs +1 -0
- package/dist/plugins/anonymous/client.d.mts +1 -0
- package/dist/plugins/anonymous/error-codes.d.mts +1 -0
- package/dist/plugins/anonymous/error-codes.mjs +1 -0
- package/dist/plugins/anonymous/index.d.mts +1 -0
- package/dist/plugins/anonymous/index.mjs +16 -2
- package/dist/plugins/bearer/index.mjs +4 -9
- package/dist/plugins/captcha/index.mjs +2 -2
- package/dist/plugins/device-authorization/error-codes.mjs +1 -0
- package/dist/plugins/device-authorization/index.d.mts +1 -0
- package/dist/plugins/device-authorization/routes.mjs +34 -3
- package/dist/plugins/generic-oauth/index.d.mts +1 -1
- package/dist/plugins/generic-oauth/index.mjs +6 -6
- package/dist/plugins/generic-oauth/routes.mjs +34 -32
- package/dist/plugins/generic-oauth/types.d.mts +7 -0
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/last-login-method/client.mjs +2 -2
- package/dist/plugins/magic-link/index.d.mts +8 -1
- package/dist/plugins/magic-link/index.mjs +4 -17
- package/dist/plugins/mcp/authorize.mjs +8 -2
- package/dist/plugins/mcp/index.mjs +73 -34
- package/dist/plugins/multi-session/index.mjs +2 -2
- package/dist/plugins/oauth-proxy/index.mjs +44 -31
- package/dist/plugins/oauth-proxy/utils.mjs +3 -10
- package/dist/plugins/oidc-provider/authorize.mjs +8 -2
- package/dist/plugins/oidc-provider/index.mjs +63 -37
- package/dist/plugins/one-tap/index.mjs +13 -8
- package/dist/plugins/open-api/generator.mjs +16 -5
- package/dist/plugins/organization/access/statement.d.mts +68 -201
- package/dist/plugins/organization/adapter.mjs +61 -56
- package/dist/plugins/organization/client.d.mts +3 -1
- package/dist/plugins/organization/error-codes.d.mts +2 -0
- package/dist/plugins/organization/error-codes.mjs +3 -1
- package/dist/plugins/organization/routes/crud-access-control.d.mts +2 -2
- package/dist/plugins/organization/routes/crud-invites.mjs +7 -2
- package/dist/plugins/organization/types.d.mts +12 -2
- package/dist/plugins/two-factor/index.mjs +3 -2
- package/dist/plugins/username/index.d.mts +24 -2
- package/dist/plugins/username/index.mjs +49 -3
- package/dist/state.d.mts +2 -2
- package/dist/state.mjs +18 -4
- package/dist/test-utils/headers.mjs +2 -7
- package/dist/test-utils/test-instance.d.mts +25 -6
- package/dist/test-utils/test-instance.mjs +11 -2
- package/dist/utils/index.d.mts +1 -1
- package/dist/utils/url.d.mts +2 -1
- package/dist/utils/url.mjs +9 -3
- package/package.json +15 -14
|
@@ -90,9 +90,20 @@ declare const username: (options?: UsernameOptions | undefined) => {
|
|
|
90
90
|
name: string;
|
|
91
91
|
image?: string | null | undefined;
|
|
92
92
|
} & Record<string, unknown>, context: _better_auth_core0.GenericEndpointContext | null): Promise<{
|
|
93
|
+
data: {
|
|
94
|
+
username: string;
|
|
95
|
+
displayUsername: string;
|
|
96
|
+
id: string;
|
|
97
|
+
createdAt: Date;
|
|
98
|
+
updatedAt: Date;
|
|
99
|
+
email: string;
|
|
100
|
+
emailVerified: boolean;
|
|
101
|
+
name: string;
|
|
102
|
+
image?: string | null | undefined;
|
|
103
|
+
};
|
|
104
|
+
} | {
|
|
93
105
|
data: {
|
|
94
106
|
displayUsername?: string | undefined;
|
|
95
|
-
username?: string | undefined;
|
|
96
107
|
id: string;
|
|
97
108
|
createdAt: Date;
|
|
98
109
|
updatedAt: Date;
|
|
@@ -115,7 +126,18 @@ declare const username: (options?: UsernameOptions | undefined) => {
|
|
|
115
126
|
}> & Record<string, unknown>, context: _better_auth_core0.GenericEndpointContext | null): Promise<{
|
|
116
127
|
data: {
|
|
117
128
|
displayUsername?: string | undefined;
|
|
118
|
-
username
|
|
129
|
+
username: string;
|
|
130
|
+
id?: string | undefined;
|
|
131
|
+
createdAt?: Date | undefined;
|
|
132
|
+
updatedAt?: Date | undefined;
|
|
133
|
+
email?: string | undefined;
|
|
134
|
+
emailVerified?: boolean | undefined;
|
|
135
|
+
name?: string | undefined;
|
|
136
|
+
image?: string | null | undefined;
|
|
137
|
+
};
|
|
138
|
+
} | {
|
|
139
|
+
data: {
|
|
140
|
+
displayUsername?: string | undefined;
|
|
119
141
|
id?: string | undefined;
|
|
120
142
|
createdAt?: Date | undefined;
|
|
121
143
|
updatedAt?: Date | undefined;
|
|
@@ -28,6 +28,31 @@ const username = (options) => {
|
|
|
28
28
|
const displayUsernameNormalizer = (displayUsername) => {
|
|
29
29
|
return options?.displayUsernameNormalization ? options.displayUsernameNormalization(displayUsername) : displayUsername;
|
|
30
30
|
};
|
|
31
|
+
const minUsernameLength = options?.minUsernameLength || 3;
|
|
32
|
+
const maxUsernameLength = options?.maxUsernameLength || 30;
|
|
33
|
+
const validator = options?.usernameValidator || defaultUsernameValidator;
|
|
34
|
+
const pathsWithHttpHookValidation = ["/sign-up/email", "/update-user"];
|
|
35
|
+
async function validateUsername(username, displayUsername, adapter, currentUserId) {
|
|
36
|
+
const usernameToValidate = options?.validationOrder?.username === "post-normalization" ? normalizer(username) : username;
|
|
37
|
+
if (usernameToValidate.length < minUsernameLength) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_TOO_SHORT);
|
|
38
|
+
if (usernameToValidate.length > maxUsernameLength) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_TOO_LONG);
|
|
39
|
+
if (!await validator(usernameToValidate)) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.INVALID_USERNAME);
|
|
40
|
+
const normalizedUsername = normalizer(username);
|
|
41
|
+
const existingUser = await adapter.findOne({
|
|
42
|
+
model: "user",
|
|
43
|
+
where: [{
|
|
44
|
+
field: "username",
|
|
45
|
+
value: normalizedUsername
|
|
46
|
+
}]
|
|
47
|
+
});
|
|
48
|
+
if (existingUser) {
|
|
49
|
+
if (!currentUserId || existingUser.id !== currentUserId) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_IS_ALREADY_TAKEN);
|
|
50
|
+
}
|
|
51
|
+
if (displayUsername && options?.displayUsernameValidator) {
|
|
52
|
+
const displayUsernameToValidate = options?.validationOrder?.displayUsername === "post-normalization" ? displayUsernameNormalizer(displayUsername) : displayUsername;
|
|
53
|
+
if (!await options.displayUsernameValidator(displayUsernameToValidate)) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.INVALID_DISPLAY_USERNAME);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
31
56
|
return {
|
|
32
57
|
id: "username",
|
|
33
58
|
version: PACKAGE_VERSION,
|
|
@@ -36,18 +61,39 @@ const username = (options) => {
|
|
|
36
61
|
create: { async before(user, context) {
|
|
37
62
|
const username = "username" in user ? user.username : null;
|
|
38
63
|
const displayUsername = "displayUsername" in user ? user.displayUsername : null;
|
|
64
|
+
const currentPath = context?.path;
|
|
65
|
+
const skipValidation = currentPath && pathsWithHttpHookValidation.includes(currentPath);
|
|
66
|
+
if (username) {
|
|
67
|
+
if (!skipValidation) await validateUsername(username, displayUsername, ctx.adapter);
|
|
68
|
+
return { data: {
|
|
69
|
+
...user,
|
|
70
|
+
username: normalizer(username),
|
|
71
|
+
displayUsername: displayUsername ? displayUsernameNormalizer(displayUsername) : username
|
|
72
|
+
} };
|
|
73
|
+
}
|
|
39
74
|
return { data: {
|
|
40
75
|
...user,
|
|
41
|
-
...username ? { username: normalizer(username) } : {},
|
|
42
76
|
...displayUsername ? { displayUsername: displayUsernameNormalizer(displayUsername) } : {}
|
|
43
77
|
} };
|
|
44
78
|
} },
|
|
45
79
|
update: { async before(user, context) {
|
|
46
80
|
const username = "username" in user ? user.username : null;
|
|
47
81
|
const displayUsername = "displayUsername" in user ? user.displayUsername : null;
|
|
82
|
+
const currentPath = context?.path;
|
|
83
|
+
const skipValidation = currentPath && pathsWithHttpHookValidation.includes(currentPath);
|
|
84
|
+
if (username) {
|
|
85
|
+
if (!skipValidation) {
|
|
86
|
+
const currentUserId = context?.context?.session?.user?.id || ("id" in user ? user.id : null);
|
|
87
|
+
await validateUsername(username, displayUsername, ctx.adapter, currentUserId);
|
|
88
|
+
}
|
|
89
|
+
return { data: {
|
|
90
|
+
...user,
|
|
91
|
+
username: normalizer(username),
|
|
92
|
+
...displayUsername ? { displayUsername: displayUsernameNormalizer(displayUsername) } : {}
|
|
93
|
+
} };
|
|
94
|
+
}
|
|
48
95
|
return { data: {
|
|
49
96
|
...user,
|
|
50
|
-
...username ? { username: normalizer(username) } : {},
|
|
51
97
|
...displayUsername ? { displayUsername: displayUsernameNormalizer(displayUsername) } : {}
|
|
52
98
|
} };
|
|
53
99
|
} }
|
|
@@ -153,7 +199,7 @@ const username = (options) => {
|
|
|
153
199
|
if (!ctx.context.options?.emailVerification?.sendVerificationEmail) throw APIError.from("FORBIDDEN", USERNAME_ERROR_CODES.EMAIL_NOT_VERIFIED);
|
|
154
200
|
if (ctx.context.options?.emailVerification?.sendOnSignIn) {
|
|
155
201
|
const token = await createEmailVerificationToken(ctx.context.secret, user.email, void 0, ctx.context.options.emailVerification?.expiresIn);
|
|
156
|
-
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`;
|
|
202
|
+
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`;
|
|
157
203
|
await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailVerification.sendVerificationEmail({
|
|
158
204
|
user,
|
|
159
205
|
url,
|
package/dist/state.d.mts
CHANGED
|
@@ -27,8 +27,8 @@ declare function generateGenericState(c: GenericEndpointContext, stateData: Stat
|
|
|
27
27
|
state: string;
|
|
28
28
|
codeVerifier: string;
|
|
29
29
|
}>;
|
|
30
|
-
declare function parseGenericState(c: GenericEndpointContext, state: string, settings?: {
|
|
31
|
-
cookieName
|
|
30
|
+
declare function parseGenericState(c: GenericEndpointContext, state: string | undefined, settings?: {
|
|
31
|
+
cookieName?: string;
|
|
32
32
|
skipStateCookieCheck?: boolean;
|
|
33
33
|
}): Promise<{
|
|
34
34
|
[x: string]: unknown;
|
package/dist/state.mjs
CHANGED
|
@@ -20,10 +20,19 @@ const stateDataSchema = z.looseObject({
|
|
|
20
20
|
var StateError = class extends BetterAuthError {
|
|
21
21
|
code;
|
|
22
22
|
details;
|
|
23
|
+
/**
|
|
24
|
+
* The per-flow `errorCallbackURL` recovered from the parsed state, when the
|
|
25
|
+
* failure happened after the state was successfully parsed (for example a
|
|
26
|
+
* nonce or state-cookie mismatch). It was origin-validated at sign-in, so
|
|
27
|
+
* the callback can safely redirect there instead of the default error page.
|
|
28
|
+
* Absent when the state could not be parsed at all.
|
|
29
|
+
*/
|
|
30
|
+
errorURL;
|
|
23
31
|
constructor(message, options) {
|
|
24
32
|
super(message, options);
|
|
25
33
|
this.code = options.code;
|
|
26
34
|
this.details = options.details;
|
|
35
|
+
this.errorURL = options.errorURL;
|
|
27
36
|
}
|
|
28
37
|
};
|
|
29
38
|
async function generateGenericState(c, stateData, settings) {
|
|
@@ -62,6 +71,7 @@ async function generateGenericState(c, stateData, settings) {
|
|
|
62
71
|
};
|
|
63
72
|
}
|
|
64
73
|
async function parseGenericState(c, state, settings) {
|
|
74
|
+
if (!state) throw new StateError("State not found in OAuth callback", { code: "state_not_found" });
|
|
65
75
|
const storeStateStrategy = c.context.oauthConfig.storeStateStrategy;
|
|
66
76
|
let parsedData;
|
|
67
77
|
if (storeStateStrategy === "cookie") {
|
|
@@ -86,7 +96,8 @@ async function parseGenericState(c, state, settings) {
|
|
|
86
96
|
}
|
|
87
97
|
if (!parsedData.oauthState || parsedData.oauthState !== state) throw new StateError("State mismatch: OAuth state parameter does not match stored state", {
|
|
88
98
|
code: "state_security_mismatch",
|
|
89
|
-
details: { state }
|
|
99
|
+
details: { state },
|
|
100
|
+
errorURL: parsedData.errorURL
|
|
90
101
|
});
|
|
91
102
|
expireCookie(c, stateCookie);
|
|
92
103
|
} else {
|
|
@@ -98,20 +109,23 @@ async function parseGenericState(c, state, settings) {
|
|
|
98
109
|
parsedData = stateDataSchema.parse(JSON.parse(data.value));
|
|
99
110
|
if (parsedData.oauthState !== void 0 && parsedData.oauthState !== state) throw new StateError("State mismatch: OAuth state parameter does not match stored state", {
|
|
100
111
|
code: "state_security_mismatch",
|
|
101
|
-
details: { state }
|
|
112
|
+
details: { state },
|
|
113
|
+
errorURL: parsedData.errorURL
|
|
102
114
|
});
|
|
103
115
|
const stateCookie = c.context.createAuthCookie(settings?.cookieName ?? "state");
|
|
104
116
|
const stateCookieValue = await c.getSignedCookie(stateCookie.name, c.context.secret);
|
|
105
117
|
if (!(settings?.skipStateCookieCheck ?? c.context.oauthConfig.skipStateCookieCheck) && (!stateCookieValue || stateCookieValue !== state)) throw new StateError("State mismatch: State not persisted correctly", {
|
|
106
118
|
code: "state_security_mismatch",
|
|
107
|
-
details: { state }
|
|
119
|
+
details: { state },
|
|
120
|
+
errorURL: parsedData.errorURL
|
|
108
121
|
});
|
|
109
122
|
expireCookie(c, stateCookie);
|
|
110
123
|
await c.context.internalAdapter.deleteVerificationByIdentifier(state);
|
|
111
124
|
}
|
|
112
125
|
if (parsedData.expiresAt < Date.now()) throw new StateError("Invalid state: request expired", {
|
|
113
126
|
code: "state_mismatch",
|
|
114
|
-
details: { expiresAt: parsedData.expiresAt }
|
|
127
|
+
details: { expiresAt: parsedData.expiresAt },
|
|
128
|
+
errorURL: parsedData.errorURL
|
|
115
129
|
});
|
|
116
130
|
return parsedData;
|
|
117
131
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applySetCookies } from "../cookies/cookie-utils.mjs";
|
|
1
2
|
//#region src/test-utils/headers.ts
|
|
2
3
|
/**
|
|
3
4
|
* converts set cookie containing headers to
|
|
@@ -9,13 +10,7 @@ function convertSetCookieToCookie(headers) {
|
|
|
9
10
|
if (name.toLowerCase() === "set-cookie") setCookieHeaders.push(value);
|
|
10
11
|
});
|
|
11
12
|
if (setCookieHeaders.length === 0) return headers;
|
|
12
|
-
|
|
13
|
-
const cookies = existingCookies ? existingCookies.split("; ") : [];
|
|
14
|
-
setCookieHeaders.forEach((setCookie) => {
|
|
15
|
-
const cookiePair = setCookie.split(";")[0];
|
|
16
|
-
cookies.push(cookiePair.trim());
|
|
17
|
-
});
|
|
18
|
-
headers.set("cookie", cookies.join("; "));
|
|
13
|
+
applySetCookies(headers, setCookieHeaders);
|
|
19
14
|
return headers;
|
|
20
15
|
}
|
|
21
16
|
//#endregion
|
|
@@ -240,7 +240,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
240
240
|
allowedMediaTypes: string[];
|
|
241
241
|
scope: "server";
|
|
242
242
|
};
|
|
243
|
-
},
|
|
243
|
+
}, never>;
|
|
244
244
|
readonly getSession: better_call0.StrictEndpoint<"/get-session", {
|
|
245
245
|
method: ("GET" | "POST")[];
|
|
246
246
|
operationId: string;
|
|
@@ -339,6 +339,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
339
339
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
340
340
|
rememberMe: zod.ZodOptional<zod.ZodBoolean>;
|
|
341
341
|
}, zod_v4_core0.$strip>, zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
342
|
+
cloneRequest: true;
|
|
342
343
|
metadata: {
|
|
343
344
|
allowedMediaTypes: string[];
|
|
344
345
|
$Infer: {
|
|
@@ -505,6 +506,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
505
506
|
method: "POST";
|
|
506
507
|
operationId: string;
|
|
507
508
|
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
|
|
509
|
+
cloneRequest: true;
|
|
508
510
|
body: zod.ZodObject<{
|
|
509
511
|
email: zod.ZodString;
|
|
510
512
|
password: zod.ZodString;
|
|
@@ -736,6 +738,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
736
738
|
readonly sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
|
|
737
739
|
method: "POST";
|
|
738
740
|
operationId: string;
|
|
741
|
+
cloneRequest: true;
|
|
739
742
|
body: zod.ZodObject<{
|
|
740
743
|
email: zod.ZodEmail;
|
|
741
744
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
@@ -2242,7 +2245,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
2242
2245
|
allowedMediaTypes: string[];
|
|
2243
2246
|
scope: "server";
|
|
2244
2247
|
};
|
|
2245
|
-
},
|
|
2248
|
+
}, never>;
|
|
2246
2249
|
readonly getSession: better_call0.StrictEndpoint<"/get-session", {
|
|
2247
2250
|
method: ("GET" | "POST")[];
|
|
2248
2251
|
operationId: string;
|
|
@@ -2341,6 +2344,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
2341
2344
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
2342
2345
|
rememberMe: zod.ZodOptional<zod.ZodBoolean>;
|
|
2343
2346
|
}, zod_v4_core0.$strip>, zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
2347
|
+
cloneRequest: true;
|
|
2344
2348
|
metadata: {
|
|
2345
2349
|
allowedMediaTypes: string[];
|
|
2346
2350
|
$Infer: {
|
|
@@ -2507,6 +2511,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
2507
2511
|
method: "POST";
|
|
2508
2512
|
operationId: string;
|
|
2509
2513
|
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
|
|
2514
|
+
cloneRequest: true;
|
|
2510
2515
|
body: zod.ZodObject<{
|
|
2511
2516
|
email: zod.ZodString;
|
|
2512
2517
|
password: zod.ZodString;
|
|
@@ -2738,6 +2743,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
2738
2743
|
readonly sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
|
|
2739
2744
|
method: "POST";
|
|
2740
2745
|
operationId: string;
|
|
2746
|
+
cloneRequest: true;
|
|
2741
2747
|
body: zod.ZodObject<{
|
|
2742
2748
|
email: zod.ZodEmail;
|
|
2743
2749
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
@@ -4247,7 +4253,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
4247
4253
|
allowedMediaTypes: string[];
|
|
4248
4254
|
scope: "server";
|
|
4249
4255
|
};
|
|
4250
|
-
},
|
|
4256
|
+
}, never>;
|
|
4251
4257
|
readonly getSession: better_call0.StrictEndpoint<"/get-session", {
|
|
4252
4258
|
method: ("GET" | "POST")[];
|
|
4253
4259
|
operationId: string;
|
|
@@ -4346,6 +4352,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
4346
4352
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
4347
4353
|
rememberMe: zod.ZodOptional<zod.ZodBoolean>;
|
|
4348
4354
|
}, zod_v4_core0.$strip>, zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
4355
|
+
cloneRequest: true;
|
|
4349
4356
|
metadata: {
|
|
4350
4357
|
allowedMediaTypes: string[];
|
|
4351
4358
|
$Infer: {
|
|
@@ -4512,6 +4519,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
4512
4519
|
method: "POST";
|
|
4513
4520
|
operationId: string;
|
|
4514
4521
|
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
|
|
4522
|
+
cloneRequest: true;
|
|
4515
4523
|
body: zod.ZodObject<{
|
|
4516
4524
|
email: zod.ZodString;
|
|
4517
4525
|
password: zod.ZodString;
|
|
@@ -4743,6 +4751,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
4743
4751
|
readonly sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
|
|
4744
4752
|
method: "POST";
|
|
4745
4753
|
operationId: string;
|
|
4754
|
+
cloneRequest: true;
|
|
4746
4755
|
body: zod.ZodObject<{
|
|
4747
4756
|
email: zod.ZodEmail;
|
|
4748
4757
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
@@ -6249,7 +6258,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
6249
6258
|
allowedMediaTypes: string[];
|
|
6250
6259
|
scope: "server";
|
|
6251
6260
|
};
|
|
6252
|
-
},
|
|
6261
|
+
}, never>;
|
|
6253
6262
|
readonly getSession: better_call0.StrictEndpoint<"/get-session", {
|
|
6254
6263
|
method: ("GET" | "POST")[];
|
|
6255
6264
|
operationId: string;
|
|
@@ -6348,6 +6357,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
6348
6357
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
6349
6358
|
rememberMe: zod.ZodOptional<zod.ZodBoolean>;
|
|
6350
6359
|
}, zod_v4_core0.$strip>, zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
6360
|
+
cloneRequest: true;
|
|
6351
6361
|
metadata: {
|
|
6352
6362
|
allowedMediaTypes: string[];
|
|
6353
6363
|
$Infer: {
|
|
@@ -6514,6 +6524,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
6514
6524
|
method: "POST";
|
|
6515
6525
|
operationId: string;
|
|
6516
6526
|
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
|
|
6527
|
+
cloneRequest: true;
|
|
6517
6528
|
body: zod.ZodObject<{
|
|
6518
6529
|
email: zod.ZodString;
|
|
6519
6530
|
password: zod.ZodString;
|
|
@@ -6745,6 +6756,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
6745
6756
|
readonly sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
|
|
6746
6757
|
method: "POST";
|
|
6747
6758
|
operationId: string;
|
|
6759
|
+
cloneRequest: true;
|
|
6748
6760
|
body: zod.ZodObject<{
|
|
6749
6761
|
email: zod.ZodEmail;
|
|
6750
6762
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
@@ -8325,7 +8337,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
8325
8337
|
allowedMediaTypes: string[];
|
|
8326
8338
|
scope: "server";
|
|
8327
8339
|
};
|
|
8328
|
-
},
|
|
8340
|
+
}, never>;
|
|
8329
8341
|
readonly getSession: better_call0.StrictEndpoint<"/get-session", {
|
|
8330
8342
|
method: ("GET" | "POST")[];
|
|
8331
8343
|
operationId: string;
|
|
@@ -8424,6 +8436,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
8424
8436
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
8425
8437
|
rememberMe: zod.ZodOptional<zod.ZodBoolean>;
|
|
8426
8438
|
}, zod_v4_core0.$strip>, zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
8439
|
+
cloneRequest: true;
|
|
8427
8440
|
metadata: {
|
|
8428
8441
|
allowedMediaTypes: string[];
|
|
8429
8442
|
$Infer: {
|
|
@@ -8590,6 +8603,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
8590
8603
|
method: "POST";
|
|
8591
8604
|
operationId: string;
|
|
8592
8605
|
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
|
|
8606
|
+
cloneRequest: true;
|
|
8593
8607
|
body: zod.ZodObject<{
|
|
8594
8608
|
email: zod.ZodString;
|
|
8595
8609
|
password: zod.ZodString;
|
|
@@ -8821,6 +8835,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
8821
8835
|
readonly sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
|
|
8822
8836
|
method: "POST";
|
|
8823
8837
|
operationId: string;
|
|
8838
|
+
cloneRequest: true;
|
|
8824
8839
|
body: zod.ZodObject<{
|
|
8825
8840
|
email: zod.ZodEmail;
|
|
8826
8841
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
@@ -10327,7 +10342,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
10327
10342
|
allowedMediaTypes: string[];
|
|
10328
10343
|
scope: "server";
|
|
10329
10344
|
};
|
|
10330
|
-
},
|
|
10345
|
+
}, never>;
|
|
10331
10346
|
readonly getSession: better_call0.StrictEndpoint<"/get-session", {
|
|
10332
10347
|
method: ("GET" | "POST")[];
|
|
10333
10348
|
operationId: string;
|
|
@@ -10426,6 +10441,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
10426
10441
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
10427
10442
|
rememberMe: zod.ZodOptional<zod.ZodBoolean>;
|
|
10428
10443
|
}, zod_v4_core0.$strip>, zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
10444
|
+
cloneRequest: true;
|
|
10429
10445
|
metadata: {
|
|
10430
10446
|
allowedMediaTypes: string[];
|
|
10431
10447
|
$Infer: {
|
|
@@ -10592,6 +10608,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
10592
10608
|
method: "POST";
|
|
10593
10609
|
operationId: string;
|
|
10594
10610
|
use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
|
|
10611
|
+
cloneRequest: true;
|
|
10595
10612
|
body: zod.ZodObject<{
|
|
10596
10613
|
email: zod.ZodString;
|
|
10597
10614
|
password: zod.ZodString;
|
|
@@ -10823,6 +10840,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
10823
10840
|
readonly sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
|
|
10824
10841
|
method: "POST";
|
|
10825
10842
|
operationId: string;
|
|
10843
|
+
cloneRequest: true;
|
|
10826
10844
|
body: zod.ZodObject<{
|
|
10827
10845
|
email: zod.ZodEmail;
|
|
10828
10846
|
callbackURL: zod.ZodOptional<zod.ZodString>;
|
|
@@ -12151,6 +12169,7 @@ declare function getTestInstance<O extends Partial<BetterAuthOptions>, C extends
|
|
|
12151
12169
|
USER_ALREADY_EXISTS: _better_auth_core_utils_error_codes0.RawError<"USER_ALREADY_EXISTS">;
|
|
12152
12170
|
USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL: _better_auth_core_utils_error_codes0.RawError<"USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL">;
|
|
12153
12171
|
EMAIL_CAN_NOT_BE_UPDATED: _better_auth_core_utils_error_codes0.RawError<"EMAIL_CAN_NOT_BE_UPDATED">;
|
|
12172
|
+
CHANGE_EMAIL_DISABLED: _better_auth_core_utils_error_codes0.RawError<"CHANGE_EMAIL_DISABLED">;
|
|
12154
12173
|
CREDENTIAL_ACCOUNT_NOT_FOUND: _better_auth_core_utils_error_codes0.RawError<"CREDENTIAL_ACCOUNT_NOT_FOUND">;
|
|
12155
12174
|
ACCOUNT_NOT_FOUND: _better_auth_core_utils_error_codes0.RawError<"ACCOUNT_NOT_FOUND">;
|
|
12156
12175
|
SESSION_EXPIRED: _better_auth_core_utils_error_codes0.RawError<"SESSION_EXPIRED">;
|
|
@@ -7,6 +7,7 @@ import { createAuthClient } from "../client/vanilla.mjs";
|
|
|
7
7
|
import { bearer } from "../plugins/bearer/index.mjs";
|
|
8
8
|
import { sql } from "kysely";
|
|
9
9
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
10
11
|
import { afterAll } from "vitest";
|
|
11
12
|
//#region src/test-utils/test-instance.ts
|
|
12
13
|
const cleanupSet = /* @__PURE__ */ new Set();
|
|
@@ -19,10 +20,17 @@ afterAll(async () => {
|
|
|
19
20
|
});
|
|
20
21
|
async function getTestInstance(options, config) {
|
|
21
22
|
const testWith = config?.testWith || "sqlite";
|
|
23
|
+
const postgresSchema = testWith === "postgres" ? `ba_test_${randomUUID().replaceAll("-", "_")}` : void 0;
|
|
24
|
+
const quotePostgresIdentifier = (identifier) => `"${identifier.replaceAll("\"", "\"\"")}"`;
|
|
22
25
|
async function getPostgres() {
|
|
23
26
|
const { Kysely, PostgresDialect } = await import("kysely");
|
|
24
27
|
const { Pool } = await import("pg");
|
|
25
|
-
|
|
28
|
+
const pool = new Pool({
|
|
29
|
+
connectionString: "postgres://user:password@localhost:5432/better_auth",
|
|
30
|
+
options: postgresSchema ? `-c search_path=${postgresSchema},public` : void 0
|
|
31
|
+
});
|
|
32
|
+
if (postgresSchema) await pool.query(`CREATE SCHEMA IF NOT EXISTS ${quotePostgresIdentifier(postgresSchema)}`);
|
|
33
|
+
return new Kysely({ dialect: new PostgresDialect({ pool }) });
|
|
26
34
|
}
|
|
27
35
|
async function getSqlite() {
|
|
28
36
|
const { DatabaseSync } = await import("node:sqlite");
|
|
@@ -103,7 +111,8 @@ async function getTestInstance(options, config) {
|
|
|
103
111
|
}
|
|
104
112
|
if (testWith === "postgres") {
|
|
105
113
|
const postgres = await getPostgres();
|
|
106
|
-
await sql`DROP SCHEMA
|
|
114
|
+
if (postgresSchema) await sql.raw(`DROP SCHEMA IF EXISTS ${quotePostgresIdentifier(postgresSchema)} CASCADE`).execute(postgres);
|
|
115
|
+
else await sql`DROP SCHEMA public CASCADE; CREATE SCHEMA public;`.execute(postgres);
|
|
107
116
|
await postgres.destroy();
|
|
108
117
|
return;
|
|
109
118
|
}
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { generateState, parseState } from "../oauth2/state.mjs";
|
|
2
2
|
import { StateData, generateGenericState, parseGenericState } from "../state.mjs";
|
|
3
3
|
import { HIDE_METADATA } from "./hide-metadata.mjs";
|
|
4
|
-
import { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL } from "./url.mjs";
|
|
4
|
+
import { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL, trimTrailingSlashes } from "./url.mjs";
|
package/dist/utils/url.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseURLConfig, DynamicBaseURLConfig } from "@better-auth/core";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/url.d.ts
|
|
4
|
+
declare function trimTrailingSlashes(value: string): string;
|
|
4
5
|
declare function getBaseURL(url?: string, path?: string, request?: Request, loadEnv?: boolean, trustedProxyHeaders?: boolean | undefined): string | undefined;
|
|
5
6
|
declare function getOrigin(url: string): string | null;
|
|
6
7
|
declare function getProtocol(url: string): string | null;
|
|
@@ -74,4 +75,4 @@ declare function resolveDynamicBaseURL(config: DynamicBaseURLConfig, source: Req
|
|
|
74
75
|
*/
|
|
75
76
|
declare function resolveBaseURL(config: BaseURLConfig | undefined, basePath: string, source?: Request | Headers, loadEnv?: boolean, trustedProxyHeaders?: boolean): string | undefined;
|
|
76
77
|
//#endregion
|
|
77
|
-
export { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL };
|
|
78
|
+
export { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL, trimTrailingSlashes };
|
package/dist/utils/url.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { wildcardMatch } from "./wildcard.mjs";
|
|
|
2
2
|
import { env } from "@better-auth/core/env";
|
|
3
3
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
4
4
|
//#region src/utils/url.ts
|
|
5
|
+
const SLASH_CHAR_CODE = "/".charCodeAt(0);
|
|
5
6
|
/**
|
|
6
7
|
* Minimal loopback check for dev scheme inference only. Reachable from
|
|
7
8
|
* `client/config.ts` via `getBaseURL`, so we MUST NOT import the full
|
|
@@ -17,9 +18,14 @@ function isLoopbackForDevScheme(host) {
|
|
|
17
18
|
const hostname = host.replace(/:\d+$/, "").replace(/^\[|\]$/g, "").toLowerCase();
|
|
18
19
|
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "::1" || hostname.startsWith("127.");
|
|
19
20
|
}
|
|
21
|
+
function trimTrailingSlashes(value) {
|
|
22
|
+
let end = value.length;
|
|
23
|
+
while (end > 0 && value.charCodeAt(end - 1) === SLASH_CHAR_CODE) end--;
|
|
24
|
+
return end === value.length ? value : value.slice(0, end);
|
|
25
|
+
}
|
|
20
26
|
function checkHasPath(url) {
|
|
21
27
|
try {
|
|
22
|
-
return (new URL(url).pathname
|
|
28
|
+
return (trimTrailingSlashes(new URL(url).pathname) || "/") !== "/";
|
|
23
29
|
} catch {
|
|
24
30
|
throw new BetterAuthError(`Invalid base URL: ${url}. Please provide a valid base URL.`);
|
|
25
31
|
}
|
|
@@ -36,7 +42,7 @@ function assertHasProtocol(url) {
|
|
|
36
42
|
function withPath(url, path = "/api/auth") {
|
|
37
43
|
assertHasProtocol(url);
|
|
38
44
|
if (checkHasPath(url)) return url;
|
|
39
|
-
const trimmedUrl = url
|
|
45
|
+
const trimmedUrl = trimTrailingSlashes(url);
|
|
40
46
|
if (!path || path === "/") return trimmedUrl;
|
|
41
47
|
path = path.startsWith("/") ? path : `/${path}`;
|
|
42
48
|
return `${trimmedUrl}${path}`;
|
|
@@ -229,4 +235,4 @@ function resolveBaseURL(config, basePath, source, loadEnv, trustedProxyHeaders)
|
|
|
229
235
|
return getBaseURL(void 0, basePath, request, loadEnv, trustedProxyHeaders);
|
|
230
236
|
}
|
|
231
237
|
//#endregion
|
|
232
|
-
export { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL };
|
|
238
|
+
export { getBaseURL, getHost, getHostFromSource, getOrigin, getProtocol, getProtocolFromSource, isDynamicBaseURLConfig, isRequestLike, matchesHostPattern, resolveBaseURL, resolveDynamicBaseURL, trimTrailingSlashes };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.12",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -479,29 +479,29 @@
|
|
|
479
479
|
}
|
|
480
480
|
},
|
|
481
481
|
"dependencies": {
|
|
482
|
-
"@better-auth/utils": "0.4.
|
|
482
|
+
"@better-auth/utils": "0.4.1",
|
|
483
483
|
"@better-fetch/fetch": "1.1.21",
|
|
484
484
|
"@noble/ciphers": "^2.1.1",
|
|
485
485
|
"@noble/hashes": "^2.0.1",
|
|
486
486
|
"better-call": "1.3.5",
|
|
487
487
|
"defu": "^6.1.4",
|
|
488
488
|
"jose": "^6.1.3",
|
|
489
|
-
"kysely": "^0.28.
|
|
489
|
+
"kysely": "^0.28.17 || ^0.29.0",
|
|
490
490
|
"nanostores": "^1.1.1",
|
|
491
491
|
"zod": "^4.3.6",
|
|
492
|
-
"@better-auth/
|
|
493
|
-
"@better-auth/
|
|
494
|
-
"@better-auth/
|
|
495
|
-
"@better-auth/
|
|
496
|
-
"@better-auth/
|
|
497
|
-
"@better-auth/prisma-adapter": "1.6.
|
|
498
|
-
"@better-auth/telemetry": "1.6.
|
|
492
|
+
"@better-auth/core": "1.6.12",
|
|
493
|
+
"@better-auth/drizzle-adapter": "1.6.12",
|
|
494
|
+
"@better-auth/memory-adapter": "1.6.12",
|
|
495
|
+
"@better-auth/mongo-adapter": "1.6.12",
|
|
496
|
+
"@better-auth/kysely-adapter": "1.6.12",
|
|
497
|
+
"@better-auth/prisma-adapter": "1.6.12",
|
|
498
|
+
"@better-auth/telemetry": "1.6.12"
|
|
499
499
|
},
|
|
500
500
|
"devDependencies": {
|
|
501
501
|
"@lynx-js/react": "^0.116.3",
|
|
502
|
-
"@sveltejs/kit": "^2.
|
|
503
|
-
"@tanstack/react-start": "^1.
|
|
504
|
-
"@tanstack/solid-start": "^1.
|
|
502
|
+
"@sveltejs/kit": "^2.60.1",
|
|
503
|
+
"@tanstack/react-start": "^1.168.4",
|
|
504
|
+
"@tanstack/solid-start": "^1.168.4",
|
|
505
505
|
"@types/bun": "^1.3.9",
|
|
506
506
|
"@types/google.accounts": "^0.0.18",
|
|
507
507
|
"@types/pg": "^8.16.0",
|
|
@@ -512,7 +512,7 @@
|
|
|
512
512
|
"happy-dom": "^20.8.9",
|
|
513
513
|
"listhen": "^1.9.0",
|
|
514
514
|
"msw": "^2.12.10",
|
|
515
|
-
"next": "^16.2.
|
|
515
|
+
"next": "^16.2.6",
|
|
516
516
|
"oauth2-mock-server": "^8.2.2",
|
|
517
517
|
"react": "^19.2.4",
|
|
518
518
|
"react-dom": "^19.2.4",
|
|
@@ -520,6 +520,7 @@
|
|
|
520
520
|
"tsdown": "0.21.1",
|
|
521
521
|
"type-fest": "^5.4.4",
|
|
522
522
|
"typescript": "^5.9.3",
|
|
523
|
+
"vite": "^7.3.2",
|
|
523
524
|
"vitest": "^4.1.5",
|
|
524
525
|
"vue": "^3.5.29"
|
|
525
526
|
},
|