better-auth 1.7.16 → 1.7.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/auth/trusted-origins.mjs +5 -3
- package/dist/cookies/index.mjs +1 -0
- package/dist/db/internal-adapter.mjs +5 -5
- package/dist/package.mjs +1 -1
- package/dist/plugins/device-authorization/routes.mjs +2 -2
- package/dist/plugins/phone-number/index.mjs +2 -2
- package/dist/plugins/two-factor/backup-codes/index.d.mts +2 -2
- package/dist/plugins/two-factor/index.d.mts +6 -6
- package/dist/plugins/two-factor/index.mjs +2 -1
- package/dist/plugins/two-factor/otp/index.d.mts +2 -2
- package/dist/plugins/two-factor/totp/index.d.mts +2 -2
- package/dist/plugins/two-factor/verify-two-factor.mjs +3 -1
- package/package.json +10 -10
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { wildcardMatch } from "../utils/wildcard.mjs";
|
|
2
2
|
import { getHost, getOrigin, getProtocol } from "../utils/url.mjs";
|
|
3
|
+
import * as z from "zod";
|
|
3
4
|
//#region src/auth/trusted-origins.ts
|
|
4
5
|
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f-\u009f]/;
|
|
5
6
|
const RELATIVE_URL_PARSER_ORIGIN = "https://better-auth.invalid";
|
|
6
7
|
const ENCODED_PATH_SEPARATOR_PATTERN = /%2[fF]|%5[cC]/;
|
|
7
8
|
const IP_WILDCARD_HOST_PATTERN = /^(?=.*\d)[\d*?.]+$/;
|
|
8
|
-
const
|
|
9
|
+
const ipv4Schema = z.ipv4();
|
|
9
10
|
/**
|
|
10
11
|
* A wildcard host made only of digits, dots and wildcards (`192.168.*.*`,
|
|
11
12
|
* `10.*`) names an address range, so it admits only an IPv4 address. A plain
|
|
@@ -13,9 +14,10 @@ const IPV4_HOST_PATTERN = /^\d{1,3}(?:\.\d{1,3}){3}$/;
|
|
|
13
14
|
* hosts keep the documented subdomain matching.
|
|
14
15
|
*/
|
|
15
16
|
const isAllowedIPv4Host = (patternAuthority, authority) => {
|
|
16
|
-
const patternHost = patternAuthority.split(":")
|
|
17
|
+
const [patternHost = ""] = patternAuthority.split(":");
|
|
17
18
|
if (!IP_WILDCARD_HOST_PATTERN.test(patternHost)) return true;
|
|
18
|
-
|
|
19
|
+
const [host = ""] = authority.split(":");
|
|
20
|
+
return z.validate(ipv4Schema, host);
|
|
19
21
|
};
|
|
20
22
|
/**
|
|
21
23
|
* Resolves `.` and `..` segments in a path after percent-decoding so a
|
package/dist/cookies/index.mjs
CHANGED
|
@@ -177,6 +177,7 @@ async function setSessionCookie(ctx, session, dontRememberMe, overrides) {
|
|
|
177
177
|
...overrides
|
|
178
178
|
});
|
|
179
179
|
if (dontRememberMe) await ctx.setSignedCookie(ctx.context.authCookies.dontRememberToken.name, "true", ctx.context.secret, ctx.context.authCookies.dontRememberToken.attributes);
|
|
180
|
+
else if (dontRememberMeCookie) expireCookie(ctx, ctx.context.authCookies.dontRememberToken);
|
|
180
181
|
await setCookieCache(ctx, session, dontRememberMe);
|
|
181
182
|
ctx.context.setNewSession(session);
|
|
182
183
|
}
|
|
@@ -104,10 +104,9 @@ const createInternalAdapter = (adapter, ctx) => {
|
|
|
104
104
|
}
|
|
105
105
|
await secondaryStorage.delete(activeSessionsKey);
|
|
106
106
|
}
|
|
107
|
-
async function queueCachedUserSessionDeletion(userId, sessionReferences) {
|
|
107
|
+
async function queueCachedUserSessionDeletion(userId, sessionReferences, revokedAt = Date.now()) {
|
|
108
108
|
if (!secondaryStorage) return;
|
|
109
109
|
const references = sessionReferences ?? await getActiveSessionReferences(userId);
|
|
110
|
-
const revokedAt = Date.now();
|
|
111
110
|
await queueAfterTransactionHook(() => deleteCachedUserSessions(userId, references, revokedAt), { onError(error) {
|
|
112
111
|
logger.error("Failed to delete committed user sessions from secondary storage", error);
|
|
113
112
|
} });
|
|
@@ -532,24 +531,25 @@ const createInternalAdapter = (adapter, ctx) => {
|
|
|
532
531
|
}], "account", void 0);
|
|
533
532
|
},
|
|
534
533
|
deleteUserSessions: async (userId) => {
|
|
534
|
+
const revokedAt = Date.now();
|
|
535
535
|
const sessionReferences = await getActiveSessionReferences(userId);
|
|
536
536
|
if (secondaryStorage) {
|
|
537
537
|
if (!options.session?.storeSessionInDatabase) {
|
|
538
|
-
await queueCachedUserSessionDeletion(userId, sessionReferences);
|
|
538
|
+
await queueCachedUserSessionDeletion(userId, sessionReferences, revokedAt);
|
|
539
539
|
return;
|
|
540
540
|
}
|
|
541
541
|
if (ctx.options.session?.preserveSessionInDatabase) {
|
|
542
542
|
if (await endPreservedSessions([{
|
|
543
543
|
field: "userId",
|
|
544
544
|
value: userId
|
|
545
|
-
}]) !== null) await queueCachedUserSessionDeletion(userId, sessionReferences);
|
|
545
|
+
}]) !== null) await queueCachedUserSessionDeletion(userId, sessionReferences, revokedAt);
|
|
546
546
|
return;
|
|
547
547
|
}
|
|
548
548
|
}
|
|
549
549
|
if (await deleteManyWithHooks([{
|
|
550
550
|
field: "userId",
|
|
551
551
|
value: userId
|
|
552
|
-
}], "session", void 0) !== null) await queueCachedUserSessionDeletion(userId, sessionReferences);
|
|
552
|
+
}], "session", void 0) !== null) await queueCachedUserSessionDeletion(userId, sessionReferences, revokedAt);
|
|
553
553
|
},
|
|
554
554
|
deleteSessions: async (sessionTokens) => {
|
|
555
555
|
if (secondaryStorage) {
|
package/dist/package.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ms } from "../../utils/time.mjs";
|
|
2
2
|
import { generateRandomString } from "../../crypto/random.mjs";
|
|
3
|
-
import { getAuthoritativeSessionFromCtx
|
|
3
|
+
import { getAuthoritativeSessionFromCtx } from "../../api/routes/session.mjs";
|
|
4
4
|
import { DEVICE_AUTHORIZATION_ERROR_CODES } from "./error-codes.mjs";
|
|
5
5
|
import { APIError } from "@better-auth/core/error";
|
|
6
6
|
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
@@ -651,7 +651,7 @@ const deviceDeny = createAuthEndpoint("/device/deny", {
|
|
|
651
651
|
} }
|
|
652
652
|
} }
|
|
653
653
|
}, async (ctx) => {
|
|
654
|
-
const session = await
|
|
654
|
+
const session = await getAuthoritativeSessionFromCtx(ctx);
|
|
655
655
|
if (!session) throw new APIError("UNAUTHORIZED", {
|
|
656
656
|
error: "unauthorized",
|
|
657
657
|
error_description: DEVICE_AUTHORIZATION_ERROR_CODES.AUTHENTICATION_REQUIRED.message
|
|
@@ -22,7 +22,7 @@ const phoneNumber = (options) => {
|
|
|
22
22
|
init() {
|
|
23
23
|
return { options: { databaseHooks: { user: {
|
|
24
24
|
create: { async before(data, ctx) {
|
|
25
|
-
if (
|
|
25
|
+
if (data[opts.phoneNumber] == null || data[opts.phoneNumberVerified] === true) return;
|
|
26
26
|
if (ctx?.body?.[opts.phoneNumber]) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.PHONE_NUMBER_CANNOT_BE_SET);
|
|
27
27
|
return { data: {
|
|
28
28
|
...data,
|
|
@@ -35,7 +35,7 @@ const phoneNumber = (options) => {
|
|
|
35
35
|
...data,
|
|
36
36
|
[opts.phoneNumberVerified]: false
|
|
37
37
|
} };
|
|
38
|
-
if (!ctx ||
|
|
38
|
+
if (!ctx || data[opts.phoneNumber] == null || data[opts.phoneNumberVerified] === true || ctx.path === "/admin/update-user") return;
|
|
39
39
|
return { data: {
|
|
40
40
|
...data,
|
|
41
41
|
[opts.phoneNumber]: void 0
|
|
@@ -170,7 +170,7 @@ export declare const backupCode2fa: (opts: BackupCodeOptions) => {
|
|
|
170
170
|
};
|
|
171
171
|
}, {
|
|
172
172
|
token: string | undefined;
|
|
173
|
-
user: (
|
|
173
|
+
user: ({
|
|
174
174
|
id: string;
|
|
175
175
|
createdAt: Date;
|
|
176
176
|
updatedAt: Date;
|
|
@@ -178,7 +178,7 @@ export declare const backupCode2fa: (opts: BackupCodeOptions) => {
|
|
|
178
178
|
emailVerified: boolean;
|
|
179
179
|
name: string;
|
|
180
180
|
image?: string | null | undefined;
|
|
181
|
-
}) | UserWithTwoFactor;
|
|
181
|
+
} & Record<string, any>) | UserWithTwoFactor;
|
|
182
182
|
}>;
|
|
183
183
|
/**
|
|
184
184
|
* ### Endpoint
|
|
@@ -295,7 +295,7 @@ export declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
|
|
|
295
295
|
};
|
|
296
296
|
}, {
|
|
297
297
|
token: string | undefined;
|
|
298
|
-
user: (
|
|
298
|
+
user: ({
|
|
299
299
|
id: string;
|
|
300
300
|
createdAt: Date;
|
|
301
301
|
updatedAt: Date;
|
|
@@ -303,7 +303,7 @@ export declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
|
|
|
303
303
|
emailVerified: boolean;
|
|
304
304
|
name: string;
|
|
305
305
|
image?: string | null | undefined;
|
|
306
|
-
}) | UserWithTwoFactor;
|
|
306
|
+
} & Record<string, any>) | UserWithTwoFactor;
|
|
307
307
|
}>;
|
|
308
308
|
generateBackupCodes: import("better-call").StrictEndpoint<"/two-factor/generate-backup-codes", {
|
|
309
309
|
method: "POST";
|
|
@@ -490,7 +490,7 @@ export declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
|
|
|
490
490
|
user: UserWithTwoFactor;
|
|
491
491
|
} | {
|
|
492
492
|
token: string;
|
|
493
|
-
user:
|
|
493
|
+
user: {
|
|
494
494
|
id: string;
|
|
495
495
|
createdAt: Date;
|
|
496
496
|
updatedAt: Date;
|
|
@@ -498,7 +498,7 @@ export declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
|
|
|
498
498
|
emailVerified: boolean;
|
|
499
499
|
name: string;
|
|
500
500
|
image?: string | null | undefined;
|
|
501
|
-
}
|
|
501
|
+
} & Record<string, any>;
|
|
502
502
|
}>;
|
|
503
503
|
generateTOTP: import("better-call").StrictEndpoint<string, {
|
|
504
504
|
method: "POST";
|
|
@@ -621,7 +621,7 @@ export declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
|
|
|
621
621
|
user: UserWithTwoFactor;
|
|
622
622
|
} | {
|
|
623
623
|
token: string;
|
|
624
|
-
user:
|
|
624
|
+
user: {
|
|
625
625
|
id: string;
|
|
626
626
|
createdAt: Date;
|
|
627
627
|
updatedAt: Date;
|
|
@@ -629,7 +629,7 @@ export declare const twoFactor: <O extends TwoFactorOptions>(options?: O) => {
|
|
|
629
629
|
emailVerified: boolean;
|
|
630
630
|
name: string;
|
|
631
631
|
image?: string | null | undefined;
|
|
632
|
-
}
|
|
632
|
+
} & Record<string, any>;
|
|
633
633
|
}>;
|
|
634
634
|
};
|
|
635
635
|
options: NoInfer<O>;
|
|
@@ -27,7 +27,7 @@ const signInPaths = /* @__PURE__ */ new Set([
|
|
|
27
27
|
const signInTwoFactorStates = /* @__PURE__ */ new WeakMap();
|
|
28
28
|
async function readSignInTwoFactorState(ctx, trustDeviceMaxAge) {
|
|
29
29
|
const data = ctx.context.newSession;
|
|
30
|
-
if (!
|
|
30
|
+
if (!data?.user.twoFactorEnabled) return {
|
|
31
31
|
requiresTwoFactor: false,
|
|
32
32
|
trustDeviceCookie: null
|
|
33
33
|
};
|
|
@@ -90,6 +90,7 @@ const twoFactor = (options) => {
|
|
|
90
90
|
return state;
|
|
91
91
|
}
|
|
92
92
|
async function shouldRequireTwoFactor(ctx) {
|
|
93
|
+
if (!signInPaths.has(ctx.path)) return false;
|
|
93
94
|
return (await getSignInTwoFactorState(ctx)).requiresTwoFactor;
|
|
94
95
|
}
|
|
95
96
|
return {
|
|
@@ -198,7 +198,7 @@ export declare const otp2fa: (options?: OTPOptions | undefined) => {
|
|
|
198
198
|
user: UserWithTwoFactor;
|
|
199
199
|
} | {
|
|
200
200
|
token: string;
|
|
201
|
-
user:
|
|
201
|
+
user: {
|
|
202
202
|
id: string;
|
|
203
203
|
createdAt: Date;
|
|
204
204
|
updatedAt: Date;
|
|
@@ -206,7 +206,7 @@ export declare const otp2fa: (options?: OTPOptions | undefined) => {
|
|
|
206
206
|
emailVerified: boolean;
|
|
207
207
|
name: string;
|
|
208
208
|
image?: string | null | undefined;
|
|
209
|
-
}
|
|
209
|
+
} & Record<string, any>;
|
|
210
210
|
}>;
|
|
211
211
|
};
|
|
212
212
|
};
|
|
@@ -201,7 +201,7 @@ export declare const totp2fa: (options?: TOTPOptions | undefined) => {
|
|
|
201
201
|
user: UserWithTwoFactor;
|
|
202
202
|
} | {
|
|
203
203
|
token: string;
|
|
204
|
-
user:
|
|
204
|
+
user: {
|
|
205
205
|
id: string;
|
|
206
206
|
createdAt: Date;
|
|
207
207
|
updatedAt: Date;
|
|
@@ -209,7 +209,7 @@ export declare const totp2fa: (options?: TOTPOptions | undefined) => {
|
|
|
209
209
|
emailVerified: boolean;
|
|
210
210
|
name: string;
|
|
211
211
|
image?: string | null | undefined;
|
|
212
|
-
}
|
|
212
|
+
} & Record<string, any>;
|
|
213
213
|
}>;
|
|
214
214
|
};
|
|
215
215
|
};
|
|
@@ -2,6 +2,7 @@ import { parseUserOutput } from "../../db/schema.mjs";
|
|
|
2
2
|
import { generateRandomString } from "../../crypto/random.mjs";
|
|
3
3
|
import { expireCookie, setSessionCookie } from "../../cookies/index.mjs";
|
|
4
4
|
import { getAuthoritativeSessionFromCtx } from "../../api/routes/session.mjs";
|
|
5
|
+
import { isSessionOutsideStore } from "../../api/state/session-source.mjs";
|
|
5
6
|
import { TWO_FACTOR_ERROR_CODES } from "./error-code.mjs";
|
|
6
7
|
import { TRUST_DEVICE_COOKIE_NAME, TWO_FACTOR_COOKIE_NAME } from "./constant.mjs";
|
|
7
8
|
import { APIError } from "@better-auth/core/error";
|
|
@@ -11,7 +12,8 @@ async function verifyTwoFactor(ctx) {
|
|
|
11
12
|
const invalid = (errorKey) => {
|
|
12
13
|
throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES[errorKey]);
|
|
13
14
|
};
|
|
14
|
-
const
|
|
15
|
+
const requestSession = ctx.context.session;
|
|
16
|
+
const session = requestSession && await isSessionOutsideStore(requestSession.session.token) ? requestSession : await getAuthoritativeSessionFromCtx(ctx);
|
|
15
17
|
if (!session) {
|
|
16
18
|
const twoFactorCookie = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);
|
|
17
19
|
const signedTwoFactorCookie = await ctx.getSignedCookie(twoFactorCookie.name, ctx.context.secret);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.17",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -503,13 +503,13 @@
|
|
|
503
503
|
"kysely": "^0.28.17 || ^0.29.0",
|
|
504
504
|
"nanostores": "^1.3.0",
|
|
505
505
|
"zod": "^4.5.4",
|
|
506
|
-
"@better-auth/core": "1.7.
|
|
507
|
-
"@better-auth/drizzle-adapter": "1.7.
|
|
508
|
-
"@better-auth/kysely-adapter": "1.7.
|
|
509
|
-
"@better-auth/memory-adapter": "1.7.
|
|
510
|
-
"@better-auth/mongo-adapter": "1.7.
|
|
511
|
-
"@better-auth/prisma-adapter": "1.7.
|
|
512
|
-
"@better-auth/telemetry": "1.7.
|
|
506
|
+
"@better-auth/core": "1.7.17",
|
|
507
|
+
"@better-auth/drizzle-adapter": "1.7.17",
|
|
508
|
+
"@better-auth/kysely-adapter": "1.7.17",
|
|
509
|
+
"@better-auth/memory-adapter": "1.7.17",
|
|
510
|
+
"@better-auth/mongo-adapter": "1.7.17",
|
|
511
|
+
"@better-auth/prisma-adapter": "1.7.17",
|
|
512
|
+
"@better-auth/telemetry": "1.7.17"
|
|
513
513
|
},
|
|
514
514
|
"devDependencies": {
|
|
515
515
|
"@lynx-js/react": "^0.121.2",
|
|
@@ -529,8 +529,8 @@
|
|
|
529
529
|
"msw": "^2.14.6",
|
|
530
530
|
"next": "^16.4.2",
|
|
531
531
|
"oauth2-mock-server": "^9.0.0",
|
|
532
|
-
"react": "^19.
|
|
533
|
-
"react-dom": "^19.
|
|
532
|
+
"react": "^19.3.1",
|
|
533
|
+
"react-dom": "^19.3.1",
|
|
534
534
|
"solid-js": "^1.9.11",
|
|
535
535
|
"tsdown": "0.23.1",
|
|
536
536
|
"type-fest": "^5.7.0",
|