@better-auth/core 1.7.0-beta.4 → 1.7.0-beta.6
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 +47 -4
- 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/get-tables.mjs +3 -3
- package/dist/db/schema/account.d.mts +1 -1
- package/dist/db/schema/account.mjs +1 -1
- package/dist/db/type.d.mts +12 -7
- package/dist/env/env-impl.mjs +1 -1
- package/dist/error/codes.d.mts +5 -0
- package/dist/error/codes.mjs +5 -0
- package/dist/index.d.mts +2 -2
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/create-authorization-url.d.mts +4 -1
- package/dist/oauth2/create-authorization-url.mjs +5 -2
- package/dist/oauth2/dpop.d.mts +142 -0
- package/dist/oauth2/dpop.mjs +246 -0
- package/dist/oauth2/index.d.mts +6 -3
- package/dist/oauth2/index.mjs +5 -2
- package/dist/oauth2/oauth-provider.d.mts +128 -9
- package/dist/oauth2/refresh-access-token.mjs +1 -1
- package/dist/oauth2/scopes.d.mts +76 -0
- package/dist/oauth2/scopes.mjs +96 -0
- package/dist/oauth2/utils.mjs +2 -1
- package/dist/oauth2/verify-id-token.d.mts +26 -0
- package/dist/oauth2/verify-id-token.mjs +62 -0
- package/dist/oauth2/verify.d.mts +88 -15
- package/dist/oauth2/verify.mjs +187 -19
- package/dist/social-providers/apple.d.mts +14 -2
- package/dist/social-providers/apple.mjs +12 -36
- package/dist/social-providers/atlassian.d.mts +5 -1
- package/dist/social-providers/atlassian.mjs +4 -4
- package/dist/social-providers/cognito.d.mts +13 -2
- package/dist/social-providers/cognito.mjs +24 -32
- package/dist/social-providers/discord.d.mts +5 -1
- package/dist/social-providers/discord.mjs +7 -6
- package/dist/social-providers/dropbox.d.mts +5 -1
- package/dist/social-providers/dropbox.mjs +5 -5
- package/dist/social-providers/facebook.d.mts +21 -2
- package/dist/social-providers/facebook.mjs +46 -22
- package/dist/social-providers/figma.d.mts +5 -1
- package/dist/social-providers/figma.mjs +5 -5
- package/dist/social-providers/github.d.mts +5 -1
- package/dist/social-providers/github.mjs +4 -4
- package/dist/social-providers/gitlab.d.mts +5 -1
- package/dist/social-providers/gitlab.mjs +6 -6
- package/dist/social-providers/google.d.mts +29 -3
- package/dist/social-providers/google.mjs +24 -30
- package/dist/social-providers/huggingface.d.mts +5 -1
- package/dist/social-providers/huggingface.mjs +8 -8
- package/dist/social-providers/index.d.mts +222 -42
- package/dist/social-providers/kakao.d.mts +5 -1
- package/dist/social-providers/kakao.mjs +8 -8
- package/dist/social-providers/kick.d.mts +5 -1
- package/dist/social-providers/kick.mjs +4 -4
- package/dist/social-providers/line.d.mts +8 -2
- package/dist/social-providers/line.mjs +12 -14
- package/dist/social-providers/linear.d.mts +5 -1
- package/dist/social-providers/linear.mjs +4 -4
- package/dist/social-providers/linkedin.d.mts +5 -1
- package/dist/social-providers/linkedin.mjs +10 -10
- package/dist/social-providers/microsoft-entra-id.d.mts +41 -6
- package/dist/social-providers/microsoft-entra-id.mjs +40 -36
- package/dist/social-providers/naver.d.mts +5 -1
- package/dist/social-providers/naver.mjs +4 -4
- package/dist/social-providers/notion.d.mts +5 -1
- package/dist/social-providers/notion.mjs +4 -4
- package/dist/social-providers/paybin.d.mts +5 -1
- package/dist/social-providers/paybin.mjs +10 -10
- package/dist/social-providers/paypal.d.mts +5 -2
- package/dist/social-providers/paypal.mjs +8 -13
- package/dist/social-providers/polar.d.mts +5 -1
- package/dist/social-providers/polar.mjs +8 -8
- package/dist/social-providers/railway.d.mts +5 -1
- package/dist/social-providers/railway.mjs +9 -9
- package/dist/social-providers/reddit.d.mts +5 -1
- package/dist/social-providers/reddit.mjs +9 -8
- package/dist/social-providers/roblox.d.mts +5 -1
- package/dist/social-providers/roblox.mjs +5 -5
- package/dist/social-providers/salesforce.d.mts +5 -1
- package/dist/social-providers/salesforce.mjs +8 -8
- package/dist/social-providers/slack.d.mts +5 -1
- package/dist/social-providers/slack.mjs +9 -9
- package/dist/social-providers/spotify.d.mts +5 -1
- package/dist/social-providers/spotify.mjs +5 -5
- package/dist/social-providers/tiktok.d.mts +5 -1
- package/dist/social-providers/tiktok.mjs +9 -5
- package/dist/social-providers/twitch.d.mts +5 -1
- package/dist/social-providers/twitch.mjs +4 -4
- package/dist/social-providers/twitter.d.mts +6 -4
- package/dist/social-providers/twitter.mjs +9 -9
- package/dist/social-providers/vercel.d.mts +5 -1
- package/dist/social-providers/vercel.mjs +4 -7
- package/dist/social-providers/vk.d.mts +5 -1
- package/dist/social-providers/vk.mjs +5 -5
- package/dist/social-providers/wechat.d.mts +5 -1
- package/dist/social-providers/wechat.mjs +10 -6
- package/dist/social-providers/zoom.d.mts +6 -1
- package/dist/social-providers/zoom.mjs +15 -9
- package/dist/types/context.d.mts +27 -8
- package/dist/types/index.d.mts +1 -1
- package/dist/types/init-options.d.mts +137 -6
- package/dist/types/plugin-client.d.mts +12 -2
- package/dist/utils/host.mjs +4 -0
- package/dist/utils/url.mjs +4 -3
- package/package.json +7 -7
- package/src/api/index.ts +82 -0
- package/src/context/transaction.ts +45 -12
- package/src/db/adapter/factory.ts +127 -64
- package/src/db/adapter/index.ts +54 -9
- package/src/db/adapter/types.ts +1 -0
- package/src/db/get-tables.ts +8 -3
- package/src/db/schema/account.ts +14 -2
- package/src/db/type.ts +12 -7
- package/src/env/env-impl.ts +1 -2
- package/src/error/codes.ts +5 -0
- package/src/oauth2/create-authorization-url.ts +2 -2
- package/src/oauth2/dpop.ts +568 -0
- package/src/oauth2/index.ts +61 -2
- package/src/oauth2/oauth-provider.ts +140 -10
- package/src/oauth2/refresh-access-token.ts +2 -2
- package/src/oauth2/scopes.ts +118 -0
- package/src/oauth2/utils.ts +2 -5
- package/src/oauth2/verify-id-token.ts +111 -0
- package/src/oauth2/verify.ts +372 -58
- package/src/social-providers/apple.ts +24 -61
- package/src/social-providers/atlassian.ts +12 -8
- package/src/social-providers/cognito.ts +25 -47
- package/src/social-providers/discord.ts +19 -8
- package/src/social-providers/dropbox.ts +13 -7
- package/src/social-providers/facebook.ts +97 -51
- package/src/social-providers/figma.ts +13 -9
- package/src/social-providers/github.ts +12 -8
- package/src/social-providers/gitlab.ts +14 -8
- package/src/social-providers/google.ts +66 -47
- package/src/social-providers/huggingface.ts +12 -8
- package/src/social-providers/kakao.ts +16 -8
- package/src/social-providers/kick.ts +12 -7
- package/src/social-providers/line.ts +37 -37
- package/src/social-providers/linear.ts +12 -6
- package/src/social-providers/linkedin.ts +14 -10
- package/src/social-providers/microsoft-entra-id.ts +103 -59
- package/src/social-providers/naver.ts +12 -6
- package/src/social-providers/notion.ts +12 -6
- package/src/social-providers/paybin.ts +14 -11
- package/src/social-providers/paypal.ts +6 -25
- package/src/social-providers/polar.ts +12 -8
- package/src/social-providers/railway.ts +13 -9
- package/src/social-providers/reddit.ts +25 -10
- package/src/social-providers/roblox.ts +18 -7
- package/src/social-providers/salesforce.ts +12 -8
- package/src/social-providers/slack.ts +18 -9
- package/src/social-providers/spotify.ts +13 -7
- package/src/social-providers/tiktok.ts +13 -7
- package/src/social-providers/twitch.ts +12 -8
- package/src/social-providers/twitter.ts +17 -8
- package/src/social-providers/vercel.ts +16 -10
- package/src/social-providers/vk.ts +13 -7
- package/src/social-providers/wechat.ts +28 -9
- package/src/social-providers/zoom.ts +19 -6
- package/src/types/context.ts +26 -8
- package/src/types/index.ts +7 -0
- package/src/types/init-options.ts +159 -8
- package/src/types/plugin-client.ts +16 -2
- package/src/utils/host.ts +15 -0
- package/src/utils/url.ts +10 -4
package/src/types/context.ts
CHANGED
|
@@ -10,12 +10,13 @@ import type {
|
|
|
10
10
|
} from "../db";
|
|
11
11
|
import type { DBAdapter, Where } from "../db/adapter";
|
|
12
12
|
import type { createLogger } from "../env";
|
|
13
|
-
import type {
|
|
13
|
+
import type { UpstreamProvider } from "../oauth2";
|
|
14
14
|
import type { BetterAuthCookie, BetterAuthCookies } from "./cookie";
|
|
15
15
|
import type { Awaitable, LiteralString } from "./helper";
|
|
16
16
|
import type {
|
|
17
17
|
BetterAuthOptions,
|
|
18
18
|
BetterAuthRateLimitOptions,
|
|
19
|
+
UserProvisioningSource,
|
|
19
20
|
} from "./init-options";
|
|
20
21
|
import type { BetterAuthPlugin } from "./plugin";
|
|
21
22
|
import type { SecretConfig } from "./secret";
|
|
@@ -87,16 +88,15 @@ export type GenericEndpointContext<
|
|
|
87
88
|
export interface InternalAdapter<
|
|
88
89
|
_Options extends BetterAuthOptions = BetterAuthOptions,
|
|
89
90
|
> {
|
|
90
|
-
createOAuthUser(
|
|
91
|
-
user: Omit<User, "id" | "createdAt" | "updatedAt">,
|
|
92
|
-
account: Omit<Account, "userId" | "id" | "createdAt" | "updatedAt"> &
|
|
93
|
-
Partial<Account>,
|
|
94
|
-
): Promise<{ user: User; account: Account }>;
|
|
95
|
-
|
|
96
91
|
createUser<T extends Record<string, any>>(
|
|
97
92
|
user: Omit<User, "id" | "createdAt" | "updatedAt" | "emailVerified"> &
|
|
98
93
|
Partial<User> &
|
|
99
94
|
Record<string, any>,
|
|
95
|
+
/**
|
|
96
|
+
* Provisioning source. The creation seam adds `action: "create-user"` and
|
|
97
|
+
* runs the `user.validateUserInfo` gate.
|
|
98
|
+
*/
|
|
99
|
+
source: UserProvisioningSource,
|
|
100
100
|
): Promise<T & User>;
|
|
101
101
|
|
|
102
102
|
createAccount<T extends Record<string, any>>(
|
|
@@ -237,6 +237,24 @@ export interface InternalAdapter<
|
|
|
237
237
|
*/
|
|
238
238
|
consumeVerificationValue(identifier: string): Promise<Verification | null>;
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* First-writer-wins create keyed by a deterministic primary key derived from
|
|
242
|
+
* `identifier`. Returns `true` when this caller created the row and `false`
|
|
243
|
+
* when a row for the same identifier already existed.
|
|
244
|
+
*
|
|
245
|
+
* The dual of `consumeVerificationValue`: reserve races to create a marker
|
|
246
|
+
* exactly once, where consume races to delete one exactly once. Use it for
|
|
247
|
+
* replay tombstones (a SAML assertion id, a JWT `jti`) where the first caller
|
|
248
|
+
* wins. The database path is atomic via the primary key. Secondary-storage-only
|
|
249
|
+
* verification is not supported for reservation and runtime implementations
|
|
250
|
+
* should fail closed unless verification is backed by the database.
|
|
251
|
+
*/
|
|
252
|
+
reserveVerificationValue(data: {
|
|
253
|
+
identifier: string;
|
|
254
|
+
value: string;
|
|
255
|
+
expiresAt: Date;
|
|
256
|
+
}): Promise<boolean>;
|
|
257
|
+
|
|
240
258
|
updateVerificationByIdentifier(
|
|
241
259
|
identifier: string,
|
|
242
260
|
data: Partial<Verification>,
|
|
@@ -351,7 +369,7 @@ export type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> =
|
|
|
351
369
|
user: User & Record<string, any>;
|
|
352
370
|
} | null,
|
|
353
371
|
) => void;
|
|
354
|
-
socialProviders:
|
|
372
|
+
socialProviders: UpstreamProvider[];
|
|
355
373
|
authCookies: BetterAuthCookies;
|
|
356
374
|
logger: ReturnType<typeof createLogger>;
|
|
357
375
|
rateLimit: {
|
package/src/types/index.ts
CHANGED
|
@@ -24,6 +24,13 @@ export type {
|
|
|
24
24
|
DynamicBaseURLConfig,
|
|
25
25
|
GenerateIdFn,
|
|
26
26
|
StoreIdentifierOption,
|
|
27
|
+
UserProvisioningSource,
|
|
28
|
+
ValidateUserInfoAction,
|
|
29
|
+
ValidateUserInfoMethod,
|
|
30
|
+
ValidateUserInfoOAuthInfo,
|
|
31
|
+
ValidateUserInfoResult,
|
|
32
|
+
ValidateUserInfoSource,
|
|
33
|
+
ValidateUserInfoSSOInfo,
|
|
27
34
|
} from "./init-options";
|
|
28
35
|
export type {
|
|
29
36
|
BetterAuthPlugin,
|
|
@@ -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,
|
|
@@ -47,6 +46,98 @@ export type GenerateIdFn = (options: {
|
|
|
47
46
|
size?: number | undefined;
|
|
48
47
|
}) => string | false;
|
|
49
48
|
|
|
49
|
+
/**
|
|
50
|
+
* What Better Auth is about to do with an incoming identity when
|
|
51
|
+
* {@link BetterAuthOptions.user}'s `validateUserInfo` runs.
|
|
52
|
+
*
|
|
53
|
+
* - `create-user`: a brand-new user record is about to be created.
|
|
54
|
+
* - `link-account`: a new provider account is about to be linked to an
|
|
55
|
+
* already-existing user.
|
|
56
|
+
* - `sign-in`: an existing OAuth or SSO user is signing in again. This is the
|
|
57
|
+
* one case where the provider can assert *changed* data, so the hook receives
|
|
58
|
+
* the fresh provider email and profile (not the stored row), letting a domain
|
|
59
|
+
* or org policy reject a user whose provider identity moved out of bounds.
|
|
60
|
+
*
|
|
61
|
+
* Non-provider returning sign-ins are not re-validated: they carry only the
|
|
62
|
+
* stored row, which has not changed since `create-user` gated it. Use the admin
|
|
63
|
+
* plugin's ban controls or a `databaseHooks.session.create.before` hook to
|
|
64
|
+
* block those.
|
|
65
|
+
*/
|
|
66
|
+
export type ValidateUserInfoAction = "create-user" | "link-account" | "sign-in";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The authentication method that produced the incoming user info. The named
|
|
70
|
+
* methods cover Better Auth's built-ins; the open `string` keeps it extensible
|
|
71
|
+
* for plugins (for example `"scim"`).
|
|
72
|
+
*/
|
|
73
|
+
export type ValidateUserInfoMethod =
|
|
74
|
+
| "oauth"
|
|
75
|
+
| "sso-oidc"
|
|
76
|
+
| "sso-saml"
|
|
77
|
+
| "email-password"
|
|
78
|
+
| "magic-link"
|
|
79
|
+
| "email-otp"
|
|
80
|
+
| "anonymous"
|
|
81
|
+
| "siwe"
|
|
82
|
+
| "phone-number"
|
|
83
|
+
| "admin"
|
|
84
|
+
| (string & {});
|
|
85
|
+
|
|
86
|
+
/** OAuth-specific provisioning context; present only when `method` is `"oauth"`. */
|
|
87
|
+
export type ValidateUserInfoOAuthInfo = {
|
|
88
|
+
/** The social or generic OAuth provider id (e.g. `"google"`). */
|
|
89
|
+
providerId: string;
|
|
90
|
+
/** The raw provider profile (userinfo or id-token claims), unmapped. */
|
|
91
|
+
profile?: Record<string, unknown> | undefined;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** SSO-specific provisioning context; present for OIDC and SAML SSO methods. */
|
|
95
|
+
export type ValidateUserInfoSSOInfo = {
|
|
96
|
+
/** The configured SSO provider id. */
|
|
97
|
+
providerId: string;
|
|
98
|
+
/** The raw OIDC claims or SAML assertion attributes, unmapped. */
|
|
99
|
+
profile?: Record<string, unknown> | undefined;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/** Provisioning origin passed to `createUser`; the creation seam adds `action: "create-user"` to build {@link ValidateUserInfoSource}. */
|
|
103
|
+
export type UserProvisioningSource = {
|
|
104
|
+
method: ValidateUserInfoMethod;
|
|
105
|
+
/** Provider id and raw profile; present iff `method` is `"oauth"`. */
|
|
106
|
+
oauth?: ValidateUserInfoOAuthInfo | undefined;
|
|
107
|
+
/** Provider id and raw profile; present iff `method` is `"sso-oidc"` or `"sso-saml"`. */
|
|
108
|
+
sso?: ValidateUserInfoSSOInfo | undefined;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The context passed to `validateUserInfo`: the lifecycle
|
|
113
|
+
* {@link ValidateUserInfoAction}, the {@link ValidateUserInfoMethod}, and (for
|
|
114
|
+
* OAuth/SSO provider methods) protocol-specific provider metadata.
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* // Scope to one OAuth provider:
|
|
118
|
+
* if (source.oauth?.providerId !== "google") return;
|
|
119
|
+
* // Branch on the method:
|
|
120
|
+
* if (source.method === "anonymous") return { error: "no_anonymous" };
|
|
121
|
+
* // Inspect SSO claims:
|
|
122
|
+
* if (source.method === "sso-saml" && source.sso?.profile?.department !== "eng") {
|
|
123
|
+
* return { error: "invalid_department" };
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export type ValidateUserInfoSource = UserProvisioningSource & {
|
|
128
|
+
action: ValidateUserInfoAction;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export type ValidateUserInfoResult = {
|
|
132
|
+
/** A short, machine-readable rejection code, surfaced to the client. */
|
|
133
|
+
error: string;
|
|
134
|
+
/**
|
|
135
|
+
* A human-readable reason, surfaced to the client. Do not put sensitive
|
|
136
|
+
* details here.
|
|
137
|
+
*/
|
|
138
|
+
errorDescription?: string | undefined;
|
|
139
|
+
};
|
|
140
|
+
|
|
50
141
|
/**
|
|
51
142
|
* Configuration for dynamic base URL resolution.
|
|
52
143
|
* Allows Better Auth to work with multiple domains (e.g., Vercel preview deployments).
|
|
@@ -95,12 +186,27 @@ export type DynamicBaseURLConfig = {
|
|
|
95
186
|
export type BaseURLConfig = string | DynamicBaseURLConfig;
|
|
96
187
|
|
|
97
188
|
export interface BetterAuthRateLimitStorage {
|
|
98
|
-
|
|
99
|
-
|
|
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: (
|
|
100
207
|
key: string,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
) => Promise<void>;
|
|
208
|
+
rule: { window: number; max: number },
|
|
209
|
+
) => Promise<{ allowed: boolean; retryAfter: number | null }>;
|
|
104
210
|
}
|
|
105
211
|
|
|
106
212
|
export type BetterAuthRateLimitRule = {
|
|
@@ -777,6 +883,33 @@ export type BetterAuthOptions = {
|
|
|
777
883
|
*/
|
|
778
884
|
user?:
|
|
779
885
|
| (BetterAuthDBOptions<"user", keyof BaseUser> & {
|
|
886
|
+
/**
|
|
887
|
+
* Gate which identities Better Auth admits. Called just before
|
|
888
|
+
* `create-user`, `link-account`, and (for OAuth) `sign-in`, across
|
|
889
|
+
* every authentication method, including stateless setups with no
|
|
890
|
+
* persistent database. On `sign-in` the hook receives the *fresh*
|
|
891
|
+
* provider email and profile, so a domain policy can reject a user
|
|
892
|
+
* whose provider identity moved out of bounds.
|
|
893
|
+
*
|
|
894
|
+
* Non-provider returning sign-ins are not re-validated; use the admin
|
|
895
|
+
* plugin's ban controls or a `databaseHooks.session.create.before`
|
|
896
|
+
* hook for those.
|
|
897
|
+
*
|
|
898
|
+
* Return nothing to allow; return `{ error }` to reject. Browser flows
|
|
899
|
+
* redirect to the configured error URL; programmatic flows surface a
|
|
900
|
+
* `403`.
|
|
901
|
+
*
|
|
902
|
+
* TODO: rename to `validateUser` (and the `ValidateUserInfo*` types).
|
|
903
|
+
* "UserInfo" is the OIDC term and misleads for the email/password,
|
|
904
|
+
* SIWE, phone, and admin methods.
|
|
905
|
+
*/
|
|
906
|
+
validateUserInfo?: (
|
|
907
|
+
data: {
|
|
908
|
+
user: Partial<User> & Record<string, unknown>;
|
|
909
|
+
source: ValidateUserInfoSource;
|
|
910
|
+
},
|
|
911
|
+
context: GenericEndpointContext,
|
|
912
|
+
) => Awaitable<void | ValidateUserInfoResult>;
|
|
780
913
|
/**
|
|
781
914
|
* Changing email configuration
|
|
782
915
|
*/
|
|
@@ -926,6 +1059,20 @@ export type BetterAuthOptions = {
|
|
|
926
1059
|
* @default "compact"
|
|
927
1060
|
*/
|
|
928
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
|
+
};
|
|
929
1076
|
/**
|
|
930
1077
|
* Controls stateless cookie cache refresh behavior.
|
|
931
1078
|
*
|
|
@@ -1134,9 +1281,13 @@ export type BetterAuthOptions = {
|
|
|
1134
1281
|
*/
|
|
1135
1282
|
storeStateStrategy?: "database" | "cookie";
|
|
1136
1283
|
/**
|
|
1137
|
-
* 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.
|
|
1138
1287
|
*
|
|
1139
|
-
* 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.
|
|
1140
1291
|
*
|
|
1141
1292
|
* @default false
|
|
1142
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
|
@@ -235,6 +235,10 @@ function classifyIPv6(expanded: string): HostKind {
|
|
|
235
235
|
|
|
236
236
|
if (expanded.startsWith("2001:0db8:")) return "documentation";
|
|
237
237
|
|
|
238
|
+
// 2001:2::/48 — Benchmarking (RFC 5180). A specific non-globally-reachable
|
|
239
|
+
// block inside the otherwise-mixed 2001::/23 protocol-assignments space.
|
|
240
|
+
if (expanded.startsWith("2001:0002:0000:")) return "benchmarking";
|
|
241
|
+
|
|
238
242
|
if (expanded.startsWith("2002:")) {
|
|
239
243
|
const embedded = extractEmbeddedIPv4(expanded, 1);
|
|
240
244
|
if (embedded && classifyIPv4(embedded) !== "public") return "reserved";
|
|
@@ -247,6 +251,10 @@ function classifyIPv6(expanded: string): HostKind {
|
|
|
247
251
|
return "reserved";
|
|
248
252
|
}
|
|
249
253
|
|
|
254
|
+
// 64:ff9b:1::/48 — Local-Use IPv4/IPv6 Translation (RFC 8215). Distinct from
|
|
255
|
+
// the well-known NAT64 /96 prefix above and not globally reachable.
|
|
256
|
+
if (expanded.startsWith("0064:ff9b:0001:")) return "reserved";
|
|
257
|
+
|
|
250
258
|
if (expanded.startsWith("2001:0000:")) {
|
|
251
259
|
const embedded = extractEmbeddedIPv4(expanded, 6, { xor: true });
|
|
252
260
|
if (embedded && classifyIPv4(embedded) !== "public") return "reserved";
|
|
@@ -255,6 +263,13 @@ function classifyIPv6(expanded: string): HostKind {
|
|
|
255
263
|
|
|
256
264
|
if (expanded.startsWith("0100:0000:0000:0000:")) return "reserved";
|
|
257
265
|
|
|
266
|
+
// 3fff::/20 — Documentation (RFC 9637). The /20 fixes the first 16 bits to
|
|
267
|
+
// `3fff` and the next nibble to 0, so only `3fff:0xxx` is in range.
|
|
268
|
+
if (expanded.startsWith("3fff:0")) return "documentation";
|
|
269
|
+
|
|
270
|
+
// 5f00::/16 — SRv6 SIDs (RFC 9602), not globally reachable.
|
|
271
|
+
if (expanded.startsWith("5f00:")) return "reserved";
|
|
272
|
+
|
|
258
273
|
return "public";
|
|
259
274
|
}
|
|
260
275
|
|
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;
|