@better-auth/core 1.7.0-beta.5 → 1.7.0-beta.7
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 +44 -1
- package/dist/api/index.mjs +40 -1
- package/dist/context/global.mjs +1 -1
- package/dist/context/transaction.d.mts +7 -4
- package/dist/context/transaction.mjs +6 -3
- package/dist/db/adapter/factory.mjs +57 -31
- package/dist/db/adapter/index.d.mts +54 -10
- package/dist/db/adapter/types.d.mts +1 -1
- package/dist/db/type.d.mts +12 -7
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/create-authorization-url.d.mts +3 -1
- package/dist/oauth2/create-authorization-url.mjs +3 -1
- package/dist/oauth2/dpop.d.mts +142 -0
- package/dist/oauth2/dpop.mjs +246 -0
- package/dist/oauth2/index.d.mts +4 -3
- package/dist/oauth2/index.mjs +3 -2
- package/dist/oauth2/oauth-provider.d.mts +37 -3
- package/dist/oauth2/refresh-access-token.mjs +15 -1
- package/dist/oauth2/verify.d.mts +74 -15
- package/dist/oauth2/verify.mjs +172 -20
- package/dist/social-providers/apple.d.mts +2 -0
- package/dist/social-providers/atlassian.d.mts +2 -0
- package/dist/social-providers/cognito.d.mts +2 -0
- package/dist/social-providers/discord.d.mts +2 -0
- package/dist/social-providers/dropbox.d.mts +2 -0
- package/dist/social-providers/facebook.d.mts +2 -0
- package/dist/social-providers/figma.d.mts +2 -0
- package/dist/social-providers/github.d.mts +2 -0
- package/dist/social-providers/gitlab.d.mts +2 -0
- package/dist/social-providers/google.d.mts +2 -0
- package/dist/social-providers/huggingface.d.mts +2 -0
- package/dist/social-providers/index.d.mts +71 -0
- package/dist/social-providers/kakao.d.mts +2 -0
- package/dist/social-providers/kick.d.mts +2 -0
- package/dist/social-providers/line.d.mts +2 -0
- package/dist/social-providers/linear.d.mts +2 -0
- package/dist/social-providers/linkedin.d.mts +2 -0
- package/dist/social-providers/microsoft-entra-id.d.mts +12 -0
- package/dist/social-providers/microsoft-entra-id.mjs +17 -2
- package/dist/social-providers/naver.d.mts +2 -0
- package/dist/social-providers/notion.d.mts +2 -0
- package/dist/social-providers/paybin.d.mts +2 -0
- package/dist/social-providers/paypal.d.mts +2 -0
- package/dist/social-providers/polar.d.mts +2 -0
- package/dist/social-providers/railway.d.mts +2 -0
- package/dist/social-providers/reddit.d.mts +2 -0
- package/dist/social-providers/reddit.mjs +1 -1
- package/dist/social-providers/roblox.d.mts +2 -0
- package/dist/social-providers/salesforce.d.mts +2 -0
- package/dist/social-providers/slack.d.mts +2 -0
- package/dist/social-providers/spotify.d.mts +2 -0
- package/dist/social-providers/tiktok.d.mts +2 -0
- package/dist/social-providers/twitch.d.mts +2 -0
- package/dist/social-providers/twitter.d.mts +2 -0
- package/dist/social-providers/vercel.d.mts +2 -0
- package/dist/social-providers/vk.d.mts +2 -0
- package/dist/social-providers/wechat.d.mts +2 -0
- package/dist/social-providers/wechat.mjs +1 -1
- package/dist/social-providers/zoom.d.mts +2 -0
- package/dist/types/context.d.mts +17 -0
- package/dist/types/init-options.d.mts +45 -5
- package/dist/types/plugin-client.d.mts +12 -2
- package/dist/utils/host.d.mts +1 -1
- package/dist/utils/host.mjs +7 -0
- package/dist/utils/url.mjs +4 -3
- package/package.json +5 -5
- package/src/api/index.ts +82 -0
- package/src/context/transaction.ts +45 -12
- package/src/db/adapter/factory.ts +127 -72
- package/src/db/adapter/index.ts +54 -9
- package/src/db/adapter/types.ts +1 -0
- package/src/db/type.ts +12 -7
- package/src/oauth2/create-authorization-url.ts +4 -0
- package/src/oauth2/dpop.ts +568 -0
- package/src/oauth2/index.ts +45 -1
- package/src/oauth2/oauth-provider.ts +40 -2
- package/src/oauth2/refresh-access-token.ts +27 -3
- package/src/oauth2/verify-id-token.ts +2 -0
- package/src/oauth2/verify.ts +329 -66
- package/src/social-providers/microsoft-entra-id.ts +44 -1
- package/src/social-providers/reddit.ts +5 -1
- package/src/social-providers/wechat.ts +8 -1
- package/src/types/context.ts +18 -0
- package/src/types/init-options.ts +40 -8
- package/src/types/plugin-client.ts +16 -2
- package/src/utils/host.ts +25 -1
- package/src/utils/url.ts +10 -4
|
@@ -14,7 +14,6 @@ import type {
|
|
|
14
14
|
Account,
|
|
15
15
|
DBFieldAttribute,
|
|
16
16
|
ModelNames,
|
|
17
|
-
RateLimit,
|
|
18
17
|
SecondaryStorage,
|
|
19
18
|
Session,
|
|
20
19
|
User,
|
|
@@ -187,12 +186,27 @@ export type DynamicBaseURLConfig = {
|
|
|
187
186
|
export type BaseURLConfig = string | DynamicBaseURLConfig;
|
|
188
187
|
|
|
189
188
|
export interface BetterAuthRateLimitStorage {
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Atomically records one request against `key` within the rolling `window`
|
|
191
|
+
* (in seconds) and reports whether it is allowed.
|
|
192
|
+
*
|
|
193
|
+
* When `allowed` is true the count was incremented within the active window,
|
|
194
|
+
* or the window had elapsed and was reset to start at 1. When `allowed` is
|
|
195
|
+
* false the limit was already reached and `retryAfter` is the number of
|
|
196
|
+
* seconds until the window frees up.
|
|
197
|
+
*
|
|
198
|
+
* Performing the check and the increment in a single step closes the
|
|
199
|
+
* concurrent-bypass gap of the separate `get`/`set` path: N simultaneous
|
|
200
|
+
* requests can no longer all pass a stale read before any increment lands.
|
|
201
|
+
*
|
|
202
|
+
* Custom storages must implement this operation directly. Better Auth no
|
|
203
|
+
* longer accepts separate `get`/`set` rate-limit storage because that shape
|
|
204
|
+
* cannot enforce a distributed limit under concurrent requests.
|
|
205
|
+
*/
|
|
206
|
+
consume: (
|
|
192
207
|
key: string,
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
) => Promise<void>;
|
|
208
|
+
rule: { window: number; max: number },
|
|
209
|
+
) => Promise<{ allowed: boolean; retryAfter: number | null }>;
|
|
196
210
|
}
|
|
197
211
|
|
|
198
212
|
export type BetterAuthRateLimitRule = {
|
|
@@ -1045,6 +1059,20 @@ export type BetterAuthOptions = {
|
|
|
1045
1059
|
* @default "compact"
|
|
1046
1060
|
*/
|
|
1047
1061
|
strategy?: "compact" | "jwt" | "jwe";
|
|
1062
|
+
/**
|
|
1063
|
+
* JWT-specific configuration for `strategy: "jwt"`.
|
|
1064
|
+
*/
|
|
1065
|
+
jwt?: {
|
|
1066
|
+
/**
|
|
1067
|
+
* Which signing key is used for cookie-cache JWTs.
|
|
1068
|
+
*
|
|
1069
|
+
* - `"secret"`: uses the Better Auth secret with HS256.
|
|
1070
|
+
* - `"jwt-plugin"`: uses the installed `jwt()` plugin's asymmetric signing keys.
|
|
1071
|
+
*
|
|
1072
|
+
* @default "secret"
|
|
1073
|
+
*/
|
|
1074
|
+
signingKey?: "secret" | "jwt-plugin";
|
|
1075
|
+
};
|
|
1048
1076
|
/**
|
|
1049
1077
|
* Controls stateless cookie cache refresh behavior.
|
|
1050
1078
|
*
|
|
@@ -1253,9 +1281,13 @@ export type BetterAuthOptions = {
|
|
|
1253
1281
|
*/
|
|
1254
1282
|
storeStateStrategy?: "database" | "cookie";
|
|
1255
1283
|
/**
|
|
1256
|
-
* Store account data after
|
|
1284
|
+
* Store provider account data after an OAuth flow in an encrypted
|
|
1285
|
+
* cookie. This includes OAuth token material such as access tokens,
|
|
1286
|
+
* refresh tokens, ID tokens, scopes, and token expiry.
|
|
1257
1287
|
*
|
|
1258
|
-
* This is useful for database-less
|
|
1288
|
+
* This is useful for database-less flows, but large provider tokens can
|
|
1289
|
+
* still hit browser or proxy cookie/header limits even though Better Auth
|
|
1290
|
+
* chunks oversized account cookies.
|
|
1259
1291
|
*
|
|
1260
1292
|
* @default false
|
|
1261
1293
|
*
|
|
@@ -6,7 +6,21 @@ import type {
|
|
|
6
6
|
import type { Atom, WritableAtom } from "nanostores";
|
|
7
7
|
import type { LiteralString } from "./helper";
|
|
8
8
|
import type { BetterAuthOptions } from "./init-options";
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
type InferableServerPlugin = {
|
|
11
|
+
id?: LiteralString | undefined;
|
|
12
|
+
endpoints?: Record<string, unknown> | undefined;
|
|
13
|
+
schema?: Record<string, { fields: Record<string, unknown> }> | undefined;
|
|
14
|
+
$ERROR_CODES?:
|
|
15
|
+
| Record<
|
|
16
|
+
string,
|
|
17
|
+
{
|
|
18
|
+
readonly code: string;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
>
|
|
22
|
+
| undefined;
|
|
23
|
+
};
|
|
10
24
|
|
|
11
25
|
export interface ClientStore {
|
|
12
26
|
notify: (signal: string) => void;
|
|
@@ -84,7 +98,7 @@ export interface BetterAuthClientPlugin {
|
|
|
84
98
|
* only used for type inference. don't pass the
|
|
85
99
|
* actual plugin
|
|
86
100
|
*/
|
|
87
|
-
$InferServerPlugin?:
|
|
101
|
+
$InferServerPlugin?: InferableServerPlugin | undefined;
|
|
88
102
|
/**
|
|
89
103
|
* Custom actions
|
|
90
104
|
*/
|
package/src/utils/host.ts
CHANGED
|
@@ -49,7 +49,7 @@ export type HostKind =
|
|
|
49
49
|
| "multicast"
|
|
50
50
|
/** IPv4 limited broadcast `255.255.255.255`. */
|
|
51
51
|
| "broadcast"
|
|
52
|
-
/** Other RFC 6890 special-purpose ranges (0/8, 192.0.0/24, 240/4, 2001::/32, etc.). */
|
|
52
|
+
/** Other RFC 6890 special-purpose ranges (0.0.0.0/8, 192.0.0.0/24, 192.88.99.0/24, 240.0.0.0/4, 2001::/32, etc.). */
|
|
53
53
|
| "reserved"
|
|
54
54
|
/** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */
|
|
55
55
|
| "cloudMetadata"
|
|
@@ -183,6 +183,8 @@ function classifyIPv4(ip: string): HostKind {
|
|
|
183
183
|
if (inIPv4Range(n, ipv4ToUint32("224.0.0.0"), 4)) return "multicast";
|
|
184
184
|
if (inIPv4Range(n, ipv4ToUint32("0.0.0.0"), 8)) return "reserved";
|
|
185
185
|
if (inIPv4Range(n, ipv4ToUint32("192.0.0.0"), 24)) return "reserved";
|
|
186
|
+
// 6to4 relay anycast (RFC 7526, deprecated), not globally reachable.
|
|
187
|
+
if (inIPv4Range(n, ipv4ToUint32("192.88.99.0"), 24)) return "reserved";
|
|
186
188
|
if (inIPv4Range(n, ipv4ToUint32("240.0.0.0"), 4)) return "reserved";
|
|
187
189
|
|
|
188
190
|
return "public";
|
|
@@ -231,10 +233,16 @@ function classifyIPv6(expanded: string): HostKind {
|
|
|
231
233
|
|
|
232
234
|
if (firstByte === 0xff) return "multicast";
|
|
233
235
|
if (firstByte === 0xfe && (secondByte & 0xc0) === 0x80) return "linkLocal";
|
|
236
|
+
// fec0::/10 — deprecated site-local (RFC 3879), not globally reachable.
|
|
237
|
+
if (firstByte === 0xfe && (secondByte & 0xc0) === 0xc0) return "reserved";
|
|
234
238
|
if ((firstByte & 0xfe) === 0xfc) return "private";
|
|
235
239
|
|
|
236
240
|
if (expanded.startsWith("2001:0db8:")) return "documentation";
|
|
237
241
|
|
|
242
|
+
// 2001:2::/48 — Benchmarking (RFC 5180). A specific non-globally-reachable
|
|
243
|
+
// block inside the otherwise-mixed 2001::/23 protocol-assignments space.
|
|
244
|
+
if (expanded.startsWith("2001:0002:0000:")) return "benchmarking";
|
|
245
|
+
|
|
238
246
|
if (expanded.startsWith("2002:")) {
|
|
239
247
|
const embedded = extractEmbeddedIPv4(expanded, 1);
|
|
240
248
|
if (embedded && classifyIPv4(embedded) !== "public") return "reserved";
|
|
@@ -247,6 +255,10 @@ function classifyIPv6(expanded: string): HostKind {
|
|
|
247
255
|
return "reserved";
|
|
248
256
|
}
|
|
249
257
|
|
|
258
|
+
// 64:ff9b:1::/48 — Local-Use IPv4/IPv6 Translation (RFC 8215). Distinct from
|
|
259
|
+
// the well-known NAT64 /96 prefix above and not globally reachable.
|
|
260
|
+
if (expanded.startsWith("0064:ff9b:0001:")) return "reserved";
|
|
261
|
+
|
|
250
262
|
if (expanded.startsWith("2001:0000:")) {
|
|
251
263
|
const embedded = extractEmbeddedIPv4(expanded, 6, { xor: true });
|
|
252
264
|
if (embedded && classifyIPv4(embedded) !== "public") return "reserved";
|
|
@@ -255,6 +267,18 @@ function classifyIPv6(expanded: string): HostKind {
|
|
|
255
267
|
|
|
256
268
|
if (expanded.startsWith("0100:0000:0000:0000:")) return "reserved";
|
|
257
269
|
|
|
270
|
+
// 3fff::/20 — Documentation (RFC 9637). The /20 fixes the first 16 bits to
|
|
271
|
+
// `3fff` and the next nibble to 0, so only `3fff:0xxx` is in range.
|
|
272
|
+
if (expanded.startsWith("3fff:0")) return "documentation";
|
|
273
|
+
|
|
274
|
+
// 5f00::/16 — SRv6 SIDs (RFC 9602), not globally reachable.
|
|
275
|
+
if (expanded.startsWith("5f00:")) return "reserved";
|
|
276
|
+
|
|
277
|
+
// ::/96 — deprecated IPv4-compatible IPv6 (RFC 4291 §2.5.5.1). `::` and
|
|
278
|
+
// `::1` are matched above; the rest of the block embeds an IPv4 (e.g.
|
|
279
|
+
// `::127.0.0.1`) and is never a valid public target.
|
|
280
|
+
if (expanded.startsWith("0000:0000:0000:0000:0000:0000:")) return "reserved";
|
|
281
|
+
|
|
258
282
|
return "public";
|
|
259
283
|
}
|
|
260
284
|
|
package/src/utils/url.ts
CHANGED
|
@@ -25,18 +25,24 @@ export function normalizePathname(
|
|
|
25
25
|
return "/";
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// Canonicalize the basePath the same way as the request pathname. A baseURL
|
|
29
|
+
// with a trailing slash yields a basePath like "/api/auth/"; without this it
|
|
30
|
+
// would never match the slash-stripped pathname and the prefix would leak
|
|
31
|
+
// through to disabledPaths and rate-limit special-rule matching.
|
|
32
|
+
const normalizedBasePath = basePath.replace(/\/+$/, "");
|
|
33
|
+
|
|
34
|
+
if (normalizedBasePath === "") {
|
|
29
35
|
return pathname;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
// Check for exact match or proper path boundary (basePath followed by "/" or end)
|
|
33
39
|
// This prevents "/api/auth" from matching "/api/authevil/..."
|
|
34
|
-
if (pathname ===
|
|
40
|
+
if (pathname === normalizedBasePath) {
|
|
35
41
|
return "/";
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
if (pathname.startsWith(
|
|
39
|
-
return pathname.slice(
|
|
44
|
+
if (pathname.startsWith(normalizedBasePath + "/")) {
|
|
45
|
+
return pathname.slice(normalizedBasePath.length).replace(/\/+$/, "") || "/";
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
return pathname;
|