@better-auth/core 1.7.0-beta.2 → 1.7.0-beta.4
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/context/global.mjs +1 -1
- package/dist/db/adapter/factory.mjs +64 -3
- package/dist/db/adapter/index.d.mts +35 -1
- package/dist/db/adapter/types.d.mts +1 -1
- package/dist/db/type.d.mts +12 -0
- package/dist/error/codes.d.mts +1 -0
- package/dist/error/codes.mjs +1 -0
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/authorization-params.d.mts +12 -0
- package/dist/oauth2/authorization-params.mjs +12 -0
- package/dist/oauth2/basic-credentials.d.mts +30 -0
- package/dist/oauth2/basic-credentials.mjs +64 -0
- package/dist/oauth2/client-assertion.d.mts +38 -22
- package/dist/oauth2/client-assertion.mjs +63 -28
- package/dist/oauth2/client-credentials-token.d.mts +19 -40
- package/dist/oauth2/client-credentials-token.mjs +18 -29
- package/dist/oauth2/create-authorization-url.d.mts +9 -1
- package/dist/oauth2/create-authorization-url.mjs +23 -5
- package/dist/oauth2/index.d.mts +10 -7
- package/dist/oauth2/index.mjs +9 -7
- package/dist/oauth2/oauth-provider.d.mts +21 -2
- package/dist/oauth2/refresh-access-token.d.mts +20 -40
- package/dist/oauth2/refresh-access-token.mjs +19 -32
- package/dist/oauth2/token-endpoint-auth.d.mts +17 -0
- package/dist/oauth2/token-endpoint-auth.mjs +89 -0
- package/dist/oauth2/utils.d.mts +9 -1
- package/dist/oauth2/utils.mjs +12 -1
- package/dist/oauth2/validate-authorization-code.d.mts +17 -52
- package/dist/oauth2/validate-authorization-code.mjs +17 -30
- package/dist/oauth2/verify.mjs +15 -5
- package/dist/social-providers/apple.d.mts +5 -12
- package/dist/social-providers/apple.mjs +14 -3
- package/dist/social-providers/atlassian.d.mts +3 -1
- package/dist/social-providers/atlassian.mjs +5 -2
- package/dist/social-providers/cognito.d.mts +16 -1
- package/dist/social-providers/cognito.mjs +6 -2
- package/dist/social-providers/discord.d.mts +5 -3
- package/dist/social-providers/discord.mjs +16 -3
- package/dist/social-providers/dropbox.d.mts +3 -1
- package/dist/social-providers/dropbox.mjs +5 -4
- package/dist/social-providers/facebook.d.mts +5 -3
- package/dist/social-providers/facebook.mjs +6 -3
- package/dist/social-providers/figma.d.mts +3 -1
- package/dist/social-providers/figma.mjs +3 -2
- package/dist/social-providers/github.d.mts +4 -2
- package/dist/social-providers/github.mjs +5 -4
- package/dist/social-providers/gitlab.d.mts +3 -1
- package/dist/social-providers/gitlab.mjs +3 -2
- package/dist/social-providers/google.d.mts +3 -1
- package/dist/social-providers/google.mjs +5 -2
- package/dist/social-providers/huggingface.d.mts +3 -1
- package/dist/social-providers/huggingface.mjs +3 -2
- package/dist/social-providers/index.d.mts +104 -36
- package/dist/social-providers/kakao.d.mts +3 -1
- package/dist/social-providers/kakao.mjs +3 -2
- package/dist/social-providers/kick.d.mts +3 -1
- package/dist/social-providers/kick.mjs +3 -2
- package/dist/social-providers/line.d.mts +3 -1
- package/dist/social-providers/line.mjs +3 -2
- package/dist/social-providers/linear.d.mts +3 -1
- package/dist/social-providers/linear.mjs +3 -2
- package/dist/social-providers/linkedin.d.mts +5 -3
- package/dist/social-providers/linkedin.mjs +4 -3
- package/dist/social-providers/microsoft-entra-id.d.mts +3 -2
- package/dist/social-providers/microsoft-entra-id.mjs +3 -2
- package/dist/social-providers/naver.d.mts +3 -1
- package/dist/social-providers/naver.mjs +3 -2
- package/dist/social-providers/notion.d.mts +3 -1
- package/dist/social-providers/notion.mjs +5 -2
- package/dist/social-providers/paybin.d.mts +3 -1
- package/dist/social-providers/paybin.mjs +3 -2
- package/dist/social-providers/paypal.d.mts +3 -1
- package/dist/social-providers/paypal.mjs +4 -3
- package/dist/social-providers/polar.d.mts +3 -1
- package/dist/social-providers/polar.mjs +3 -2
- package/dist/social-providers/railway.d.mts +3 -1
- package/dist/social-providers/railway.mjs +3 -2
- package/dist/social-providers/reddit.d.mts +3 -1
- package/dist/social-providers/reddit.mjs +3 -2
- package/dist/social-providers/roblox.d.mts +4 -2
- package/dist/social-providers/roblox.mjs +12 -2
- package/dist/social-providers/salesforce.d.mts +3 -1
- package/dist/social-providers/salesforce.mjs +3 -2
- package/dist/social-providers/slack.d.mts +4 -2
- package/dist/social-providers/slack.mjs +11 -8
- package/dist/social-providers/spotify.d.mts +3 -1
- package/dist/social-providers/spotify.mjs +3 -2
- package/dist/social-providers/tiktok.d.mts +3 -1
- package/dist/social-providers/tiktok.mjs +14 -2
- package/dist/social-providers/twitch.d.mts +3 -1
- package/dist/social-providers/twitch.mjs +3 -2
- package/dist/social-providers/twitter.d.mts +5 -2
- package/dist/social-providers/twitter.mjs +2 -1
- package/dist/social-providers/vercel.d.mts +3 -1
- package/dist/social-providers/vercel.mjs +3 -2
- package/dist/social-providers/vk.d.mts +3 -1
- package/dist/social-providers/vk.mjs +3 -2
- package/dist/social-providers/wechat.d.mts +3 -1
- package/dist/social-providers/wechat.mjs +7 -1
- package/dist/social-providers/zoom.d.mts +4 -2
- package/dist/social-providers/zoom.mjs +10 -17
- package/dist/types/context.d.mts +30 -4
- package/dist/types/init-options.d.mts +29 -5
- package/dist/utils/ip.d.mts +5 -4
- package/dist/utils/ip.mjs +3 -3
- package/dist/utils/redirect-uri.d.mts +20 -0
- package/dist/utils/redirect-uri.mjs +48 -0
- package/dist/utils/string.d.mts +5 -1
- package/dist/utils/string.mjs +20 -1
- package/dist/utils/url.d.mts +18 -1
- package/dist/utils/url.mjs +30 -1
- package/package.json +9 -8
- package/src/db/adapter/factory.ts +121 -3
- package/src/db/adapter/index.ts +32 -0
- package/src/db/adapter/types.ts +1 -0
- package/src/db/get-tables.ts +2 -0
- package/src/db/schema/user.ts +3 -0
- package/src/db/type.ts +12 -0
- package/src/error/codes.ts +1 -0
- package/src/oauth2/authorization-params.ts +28 -0
- package/src/oauth2/basic-credentials.ts +87 -0
- package/src/oauth2/client-assertion.ts +131 -58
- package/src/oauth2/client-credentials-token.ts +48 -72
- package/src/oauth2/create-authorization-url.ts +28 -6
- package/src/oauth2/index.ts +25 -9
- package/src/oauth2/oauth-provider.ts +21 -2
- package/src/oauth2/refresh-access-token.ts +50 -76
- package/src/oauth2/token-endpoint-auth.ts +221 -0
- package/src/oauth2/utils.ts +19 -0
- package/src/oauth2/validate-authorization-code.ts +55 -85
- package/src/oauth2/verify.ts +20 -4
- package/src/social-providers/apple.ts +27 -3
- package/src/social-providers/atlassian.ts +8 -1
- package/src/social-providers/cognito.ts +26 -1
- package/src/social-providers/discord.ts +22 -18
- package/src/social-providers/dropbox.ts +7 -5
- package/src/social-providers/facebook.ts +14 -9
- package/src/social-providers/figma.ts +8 -1
- package/src/social-providers/github.ts +5 -3
- package/src/social-providers/gitlab.ts +2 -0
- package/src/social-providers/google.ts +2 -0
- package/src/social-providers/huggingface.ts +8 -1
- package/src/social-providers/kakao.ts +2 -1
- package/src/social-providers/kick.ts +8 -1
- package/src/social-providers/line.ts +2 -0
- package/src/social-providers/linear.ts +8 -1
- package/src/social-providers/linkedin.ts +5 -3
- package/src/social-providers/microsoft-entra-id.ts +2 -1
- package/src/social-providers/naver.ts +2 -1
- package/src/social-providers/notion.ts +8 -1
- package/src/social-providers/paybin.ts +2 -0
- package/src/social-providers/paypal.ts +7 -1
- package/src/social-providers/polar.ts +8 -1
- package/src/social-providers/railway.ts +8 -1
- package/src/social-providers/reddit.ts +2 -1
- package/src/social-providers/roblox.ts +16 -11
- package/src/social-providers/salesforce.ts +8 -1
- package/src/social-providers/slack.ts +15 -9
- package/src/social-providers/spotify.ts +8 -1
- package/src/social-providers/tiktok.ts +22 -9
- package/src/social-providers/twitch.ts +2 -1
- package/src/social-providers/twitter.ts +1 -0
- package/src/social-providers/vercel.ts +8 -1
- package/src/social-providers/vk.ts +8 -1
- package/src/social-providers/wechat.ts +9 -1
- package/src/social-providers/zoom.ts +15 -19
- package/src/types/context.ts +33 -5
- package/src/types/init-options.ts +29 -5
- package/src/utils/ip.ts +12 -13
- package/src/utils/redirect-uri.ts +54 -0
- package/src/utils/string.ts +37 -0
- package/src/utils/url.ts +28 -0
|
@@ -35,7 +35,8 @@ declare const twitch: (options: TwitchOptions) => {
|
|
|
35
35
|
createAuthorizationURL({
|
|
36
36
|
state,
|
|
37
37
|
scopes,
|
|
38
|
-
redirectURI
|
|
38
|
+
redirectURI,
|
|
39
|
+
additionalParams
|
|
39
40
|
}: {
|
|
40
41
|
state: string;
|
|
41
42
|
codeVerifier: string;
|
|
@@ -43,6 +44,7 @@ declare const twitch: (options: TwitchOptions) => {
|
|
|
43
44
|
redirectURI: string;
|
|
44
45
|
display?: string | undefined;
|
|
45
46
|
loginHint?: string | undefined;
|
|
47
|
+
additionalParams?: Record<string, string> | undefined;
|
|
46
48
|
}): Promise<URL>;
|
|
47
49
|
validateAuthorizationCode: ({
|
|
48
50
|
code,
|
|
@@ -9,7 +9,7 @@ const twitch = (options) => {
|
|
|
9
9
|
return {
|
|
10
10
|
id: "twitch",
|
|
11
11
|
name: "Twitch",
|
|
12
|
-
createAuthorizationURL({ state, scopes, redirectURI }) {
|
|
12
|
+
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
13
13
|
const _scopes = options.disableDefaultScope ? [] : ["user:read:email", "openid"];
|
|
14
14
|
if (options.scope) _scopes.push(...options.scope);
|
|
15
15
|
if (scopes) _scopes.push(...scopes);
|
|
@@ -25,7 +25,8 @@ const twitch = (options) => {
|
|
|
25
25
|
"email_verified",
|
|
26
26
|
"preferred_username",
|
|
27
27
|
"picture"
|
|
28
|
-
]
|
|
28
|
+
],
|
|
29
|
+
additionalParams
|
|
29
30
|
});
|
|
30
31
|
},
|
|
31
32
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
@@ -89,6 +89,7 @@ declare const twitter: (options: TwitterOption) => {
|
|
|
89
89
|
redirectURI: string;
|
|
90
90
|
display?: string | undefined;
|
|
91
91
|
loginHint?: string | undefined;
|
|
92
|
+
additionalParams?: Record<string, string> | undefined;
|
|
92
93
|
}): Promise<URL>;
|
|
93
94
|
validateAuthorizationCode: ({
|
|
94
95
|
code,
|
|
@@ -103,9 +104,11 @@ declare const twitter: (options: TwitterOption) => {
|
|
|
103
104
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
104
105
|
getUserInfo(token: OAuth2Tokens & {
|
|
105
106
|
user?: {
|
|
106
|
-
name
|
|
107
|
+
name?: {
|
|
107
108
|
firstName?: string;
|
|
108
|
-
lastName
|
|
109
|
+
lastName
|
|
110
|
+
|
|
111
|
+
/** The fully resolved URL. */? /** The fully resolved URL. */: string;
|
|
109
112
|
};
|
|
110
113
|
email?: string;
|
|
111
114
|
} | undefined;
|
|
@@ -24,7 +24,8 @@ const twitter = (options) => {
|
|
|
24
24
|
scopes: _scopes,
|
|
25
25
|
state: data.state,
|
|
26
26
|
codeVerifier: data.codeVerifier,
|
|
27
|
-
redirectURI: data.redirectURI
|
|
27
|
+
redirectURI: data.redirectURI,
|
|
28
|
+
additionalParams: data.additionalParams
|
|
28
29
|
});
|
|
29
30
|
},
|
|
30
31
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
@@ -18,7 +18,8 @@ declare const vercel: (options: VercelOptions) => {
|
|
|
18
18
|
state,
|
|
19
19
|
scopes,
|
|
20
20
|
codeVerifier,
|
|
21
|
-
redirectURI
|
|
21
|
+
redirectURI,
|
|
22
|
+
additionalParams
|
|
22
23
|
}: {
|
|
23
24
|
state: string;
|
|
24
25
|
codeVerifier: string;
|
|
@@ -26,6 +27,7 @@ declare const vercel: (options: VercelOptions) => {
|
|
|
26
27
|
redirectURI: string;
|
|
27
28
|
display?: string | undefined;
|
|
28
29
|
loginHint?: string | undefined;
|
|
30
|
+
additionalParams?: Record<string, string> | undefined;
|
|
29
31
|
}): Promise<URL>;
|
|
30
32
|
validateAuthorizationCode: ({
|
|
31
33
|
code,
|
|
@@ -7,7 +7,7 @@ const vercel = (options) => {
|
|
|
7
7
|
return {
|
|
8
8
|
id: "vercel",
|
|
9
9
|
name: "Vercel",
|
|
10
|
-
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
10
|
+
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
|
|
11
11
|
if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Vercel");
|
|
12
12
|
let _scopes = void 0;
|
|
13
13
|
if (options.scope !== void 0 || scopes !== void 0) {
|
|
@@ -22,7 +22,8 @@ const vercel = (options) => {
|
|
|
22
22
|
scopes: _scopes,
|
|
23
23
|
state,
|
|
24
24
|
codeVerifier,
|
|
25
|
-
redirectURI
|
|
25
|
+
redirectURI,
|
|
26
|
+
additionalParams
|
|
26
27
|
});
|
|
27
28
|
},
|
|
28
29
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
@@ -24,7 +24,8 @@ declare const vk: (options: VkOption) => {
|
|
|
24
24
|
state,
|
|
25
25
|
scopes,
|
|
26
26
|
codeVerifier,
|
|
27
|
-
redirectURI
|
|
27
|
+
redirectURI,
|
|
28
|
+
additionalParams
|
|
28
29
|
}: {
|
|
29
30
|
state: string;
|
|
30
31
|
codeVerifier: string;
|
|
@@ -32,6 +33,7 @@ declare const vk: (options: VkOption) => {
|
|
|
32
33
|
redirectURI: string;
|
|
33
34
|
display?: string | undefined;
|
|
34
35
|
loginHint?: string | undefined;
|
|
36
|
+
additionalParams?: Record<string, string> | undefined;
|
|
35
37
|
}): Promise<URL>;
|
|
36
38
|
validateAuthorizationCode: ({
|
|
37
39
|
code,
|
|
@@ -8,7 +8,7 @@ const vk = (options) => {
|
|
|
8
8
|
return {
|
|
9
9
|
id: "vk",
|
|
10
10
|
name: "VK",
|
|
11
|
-
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
11
|
+
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
|
|
12
12
|
const _scopes = options.disableDefaultScope ? [] : ["email", "phone"];
|
|
13
13
|
if (options.scope) _scopes.push(...options.scope);
|
|
14
14
|
if (scopes) _scopes.push(...scopes);
|
|
@@ -19,7 +19,8 @@ const vk = (options) => {
|
|
|
19
19
|
scopes: _scopes,
|
|
20
20
|
state,
|
|
21
21
|
redirectURI,
|
|
22
|
-
codeVerifier
|
|
22
|
+
codeVerifier,
|
|
23
|
+
additionalParams
|
|
23
24
|
});
|
|
24
25
|
},
|
|
25
26
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI, deviceId }) => {
|
|
@@ -56,7 +56,8 @@ declare const wechat: (options: WeChatOptions) => {
|
|
|
56
56
|
createAuthorizationURL({
|
|
57
57
|
state,
|
|
58
58
|
scopes,
|
|
59
|
-
redirectURI
|
|
59
|
+
redirectURI,
|
|
60
|
+
additionalParams
|
|
60
61
|
}: {
|
|
61
62
|
state: string;
|
|
62
63
|
codeVerifier: string;
|
|
@@ -64,6 +65,7 @@ declare const wechat: (options: WeChatOptions) => {
|
|
|
64
65
|
redirectURI: string;
|
|
65
66
|
display?: string | undefined;
|
|
66
67
|
loginHint?: string | undefined;
|
|
68
|
+
additionalParams?: Record<string, string> | undefined;
|
|
67
69
|
}): URL;
|
|
68
70
|
validateAuthorizationCode: ({
|
|
69
71
|
code
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { RESERVED_AUTHORIZATION_PARAMS_SET } from "../oauth2/create-authorization-url.mjs";
|
|
1
2
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
3
|
//#region src/social-providers/wechat.ts
|
|
3
4
|
const wechat = (options) => {
|
|
4
5
|
return {
|
|
5
6
|
id: "wechat",
|
|
6
7
|
name: "WeChat",
|
|
7
|
-
createAuthorizationURL({ state, scopes, redirectURI }) {
|
|
8
|
+
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
8
9
|
const _scopes = options.disableDefaultScope ? [] : ["snsapi_login"];
|
|
9
10
|
options.scope && _scopes.push(...options.scope);
|
|
10
11
|
scopes && _scopes.push(...scopes);
|
|
@@ -15,6 +16,11 @@ const wechat = (options) => {
|
|
|
15
16
|
url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
|
|
16
17
|
url.searchParams.set("state", state);
|
|
17
18
|
url.searchParams.set("lang", options.lang || "cn");
|
|
19
|
+
if (additionalParams) for (const [key, value] of Object.entries(additionalParams)) {
|
|
20
|
+
if (RESERVED_AUTHORIZATION_PARAMS_SET.has(key)) continue;
|
|
21
|
+
if (key === "appid") continue;
|
|
22
|
+
url.searchParams.set(key, value);
|
|
23
|
+
}
|
|
18
24
|
url.hash = "wechat_redirect";
|
|
19
25
|
return url;
|
|
20
26
|
},
|
|
@@ -119,7 +119,8 @@ declare const zoom: (userOptions: ZoomOptions) => {
|
|
|
119
119
|
createAuthorizationURL: ({
|
|
120
120
|
state,
|
|
121
121
|
redirectURI,
|
|
122
|
-
codeVerifier
|
|
122
|
+
codeVerifier,
|
|
123
|
+
additionalParams
|
|
123
124
|
}: {
|
|
124
125
|
state: string;
|
|
125
126
|
codeVerifier: string;
|
|
@@ -127,6 +128,7 @@ declare const zoom: (userOptions: ZoomOptions) => {
|
|
|
127
128
|
redirectURI: string;
|
|
128
129
|
display?: string | undefined;
|
|
129
130
|
loginHint?: string | undefined;
|
|
131
|
+
additionalParams?: Record<string, string> | undefined;
|
|
130
132
|
}) => Promise<URL>;
|
|
131
133
|
validateAuthorizationCode: ({
|
|
132
134
|
code,
|
|
@@ -151,7 +153,7 @@ declare const zoom: (userOptions: ZoomOptions) => {
|
|
|
151
153
|
user: {
|
|
152
154
|
id: string;
|
|
153
155
|
name?: string;
|
|
154
|
-
email
|
|
156
|
+
email?: string | null;
|
|
155
157
|
image?: string;
|
|
156
158
|
emailVerified: boolean;
|
|
157
159
|
[key: string]: any;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
2
2
|
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
3
3
|
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
4
4
|
import { betterFetch } from "@better-fetch/fetch";
|
|
@@ -11,22 +11,15 @@ const zoom = (userOptions) => {
|
|
|
11
11
|
return {
|
|
12
12
|
id: "zoom",
|
|
13
13
|
name: "Zoom",
|
|
14
|
-
createAuthorizationURL: async ({ state, redirectURI, codeVerifier }) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
params.set("code_challenge_method", "S256");
|
|
24
|
-
params.set("code_challenge", codeChallenge);
|
|
25
|
-
}
|
|
26
|
-
const url = new URL("https://zoom.us/oauth/authorize");
|
|
27
|
-
url.search = params.toString();
|
|
28
|
-
return url;
|
|
29
|
-
},
|
|
14
|
+
createAuthorizationURL: async ({ state, redirectURI, codeVerifier, additionalParams }) => createAuthorizationURL({
|
|
15
|
+
id: "zoom",
|
|
16
|
+
options,
|
|
17
|
+
authorizationEndpoint: "https://zoom.us/oauth/authorize",
|
|
18
|
+
state,
|
|
19
|
+
redirectURI,
|
|
20
|
+
codeVerifier: options.pkce ? codeVerifier : void 0,
|
|
21
|
+
additionalParams
|
|
22
|
+
}),
|
|
30
23
|
validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
|
|
31
24
|
return validateAuthorizationCode({
|
|
32
25
|
code,
|
package/dist/types/context.d.mts
CHANGED
|
@@ -83,8 +83,20 @@ interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions
|
|
|
83
83
|
updateSession(sessionToken: string, session: Partial<Session> & Record<string, any>): Promise<Session | null>;
|
|
84
84
|
deleteSession(token: string): Promise<void>;
|
|
85
85
|
deleteAccounts(userId: string): Promise<void>;
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Delete an account by its primary key.
|
|
88
|
+
*
|
|
89
|
+
* @param id - The account row's primary key (the `id` column, not the `accountId` column).
|
|
90
|
+
*/
|
|
91
|
+
deleteAccount(id: string): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Delete every session belonging to a user.
|
|
94
|
+
*/
|
|
95
|
+
deleteUserSessions(userId: string): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Delete sessions by their session tokens.
|
|
98
|
+
*/
|
|
99
|
+
deleteSessions(sessionTokens: string[]): Promise<void>;
|
|
88
100
|
findOAuthUser(email: string, accountId: string, providerId: string): Promise<{
|
|
89
101
|
user: User;
|
|
90
102
|
linkedAccount: Account | null;
|
|
@@ -102,14 +114,28 @@ interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions
|
|
|
102
114
|
updateUserByEmail<T extends Record<string, any>>(email: string, data: Partial<User & Record<string, any>>): Promise<User & T>;
|
|
103
115
|
updatePassword(userId: string, password: string): Promise<void>;
|
|
104
116
|
findAccounts(userId: string): Promise<Account[]>;
|
|
105
|
-
findAccount(accountId: string): Promise<Account | null>;
|
|
106
117
|
findAccountByProviderId(accountId: string, providerId: string): Promise<Account | null>;
|
|
107
118
|
findAccountByUserId(userId: string): Promise<Account[]>;
|
|
108
119
|
updateAccount(id: string, data: Partial<Account>): Promise<Account>;
|
|
109
120
|
createVerificationValue(data: Omit<Verification, "createdAt" | "id" | "updatedAt"> & Partial<Verification>): Promise<Verification>;
|
|
110
121
|
findVerificationValue(identifier: string): Promise<Verification | null>;
|
|
111
122
|
deleteVerificationByIdentifier(identifier: string): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Atomically consume a single-use verification row by `identifier` and
|
|
125
|
+
* return it. Only the first concurrent caller receives the latest row;
|
|
126
|
+
* subsequent callers receive `null`. Consuming one row invalidates the
|
|
127
|
+
* whole identifier so stale rows cannot be replayed. Rows past their
|
|
128
|
+
* `expiresAt` are treated as already invalid: the row is deleted but
|
|
129
|
+
* `null` is returned, so callers do not need to gate on `expiresAt`
|
|
130
|
+
* themselves. Callers MUST gate any state change (issue session, mint
|
|
131
|
+
* token, change password) on a non-null result.
|
|
132
|
+
*
|
|
133
|
+
* Replaces the racy `findVerificationValue` + `deleteVerificationByIdentifier`
|
|
134
|
+
* pair at single-use credential consumption sites.
|
|
135
|
+
*/
|
|
136
|
+
consumeVerificationValue(identifier: string): Promise<Verification | null>;
|
|
112
137
|
updateVerificationByIdentifier(identifier: string, data: Partial<Verification>): Promise<Verification>;
|
|
138
|
+
refreshUserSessions(user: User): Promise<void>;
|
|
113
139
|
}
|
|
114
140
|
type CreateCookieGetterFn = (cookieName: string, overrideAttributes?: Partial<CookieOptions> | undefined) => BetterAuthCookie;
|
|
115
141
|
type CheckPasswordFn<Options extends BetterAuthOptions = BetterAuthOptions> = (userId: string, ctx: GenericEndpointContext<Options>) => Promise<boolean>;
|
|
@@ -165,7 +191,7 @@ type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> = Plugin
|
|
|
165
191
|
* - "cookie": Store state in an encrypted cookie (stateless)
|
|
166
192
|
* - "database": Store state in the database
|
|
167
193
|
*
|
|
168
|
-
* @default "cookie"
|
|
194
|
+
* @default "database" when `database` or `secondaryStorage` is configured, "cookie" otherwise
|
|
169
195
|
*/
|
|
170
196
|
storeStateStrategy: "database" | "cookie";
|
|
171
197
|
};
|
|
@@ -157,12 +157,13 @@ type BetterAuthAdvancedOptions = {
|
|
|
157
157
|
*/
|
|
158
158
|
disableIpTracking?: boolean;
|
|
159
159
|
/**
|
|
160
|
-
* IPv6
|
|
161
|
-
*
|
|
160
|
+
* IPv6 prefix length used to collapse addresses before rate-limit keying.
|
|
161
|
+
* Any integer from 0 to 128 is accepted; common values are 32, 48, 56, 64, 128.
|
|
162
|
+
* Out-of-range values fall back to safe behavior (negative -> mask all, > 128 -> no mask).
|
|
162
163
|
*
|
|
163
164
|
* @default 64
|
|
164
165
|
*/
|
|
165
|
-
ipv6Subnet?:
|
|
166
|
+
ipv6Subnet?: number;
|
|
166
167
|
} | undefined;
|
|
167
168
|
/**
|
|
168
169
|
* Force cookies to always use the `Secure` attribute. By default,
|
|
@@ -906,6 +907,25 @@ type BetterAuthOptions = {
|
|
|
906
907
|
* @default false
|
|
907
908
|
*/
|
|
908
909
|
disableImplicitLinking?: boolean;
|
|
910
|
+
/**
|
|
911
|
+
* Require the existing local user row to have
|
|
912
|
+
* `emailVerified: true` before implicit account linking
|
|
913
|
+
* uses the IdP's `email_verified` claim as ownership
|
|
914
|
+
* proof. Defaults to `true` so an attacker who
|
|
915
|
+
* pre-registers an unverified account at a victim's
|
|
916
|
+
* email cannot have the victim's OAuth identity linked
|
|
917
|
+
* into the attacker-owned row on first sign-in. Set to
|
|
918
|
+
* `false` for backward compatibility on apps whose
|
|
919
|
+
* users sign up via OAuth without verifying their email
|
|
920
|
+
* locally; understand the takeover risk before doing
|
|
921
|
+
* so.
|
|
922
|
+
*
|
|
923
|
+
* @default true
|
|
924
|
+
*
|
|
925
|
+
* @deprecated The option will be removed on the next
|
|
926
|
+
* minor; the gate will become unconditional.
|
|
927
|
+
*/
|
|
928
|
+
requireLocalEmailVerified?: boolean;
|
|
909
929
|
/**
|
|
910
930
|
* List of trusted providers. Can be a static array or a function
|
|
911
931
|
* that returns providers dynamically. The function is called
|
|
@@ -943,7 +963,11 @@ type BetterAuthOptions = {
|
|
|
943
963
|
*/
|
|
944
964
|
allowUnlinkingAll?: boolean;
|
|
945
965
|
/**
|
|
946
|
-
*
|
|
966
|
+
* When enabled, linking an account copies the provider's profile onto
|
|
967
|
+
* the local user, matching the fields persisted on sign-up (`name`,
|
|
968
|
+
* `image`, and any `mapProfileToUser` fields). The local `email` and
|
|
969
|
+
* `emailVerified` are never changed, so a link cannot rebind the
|
|
970
|
+
* account's identity.
|
|
947
971
|
*
|
|
948
972
|
* @default false
|
|
949
973
|
*/
|
|
@@ -976,7 +1000,7 @@ type BetterAuthOptions = {
|
|
|
976
1000
|
* - "cookie": Store state in an encrypted cookie (stateless)
|
|
977
1001
|
* - "database": Store state in the database
|
|
978
1002
|
*
|
|
979
|
-
* @default "cookie"
|
|
1003
|
+
* @default "database" when `database` or `secondaryStorage` is configured, "cookie" otherwise
|
|
980
1004
|
*/
|
|
981
1005
|
storeStateStrategy?: "database" | "cookie";
|
|
982
1006
|
/**
|
package/dist/utils/ip.d.mts
CHANGED
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
*/
|
|
11
11
|
interface NormalizeIPOptions {
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* Common values: 32, 48, 64, 128
|
|
13
|
+
* Prefix length used to collapse IPv6 addresses before keying.
|
|
14
|
+
* Any integer from 0 to 128 is accepted. Common values: 32, 48, 56, 64, 128.
|
|
15
|
+
* Values outside 0-128 are clamped.
|
|
15
16
|
*
|
|
16
|
-
* @default
|
|
17
|
+
* @default 64
|
|
17
18
|
*/
|
|
18
|
-
ipv6Subnet?:
|
|
19
|
+
ipv6Subnet?: number;
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
22
|
* Checks if an IP is valid IPv4 or IPv6
|
package/dist/utils/ip.mjs
CHANGED
|
@@ -60,8 +60,8 @@ function expandIPv6(ipv6) {
|
|
|
60
60
|
*/
|
|
61
61
|
function normalizeIPv6(ipv6, subnetPrefix) {
|
|
62
62
|
const groups = expandIPv6(ipv6);
|
|
63
|
-
if (subnetPrefix && subnetPrefix < 128) {
|
|
64
|
-
let bitsRemaining = subnetPrefix;
|
|
63
|
+
if (subnetPrefix !== void 0 && subnetPrefix < 128) {
|
|
64
|
+
let bitsRemaining = Math.max(0, Math.floor(subnetPrefix));
|
|
65
65
|
return groups.map((group) => {
|
|
66
66
|
if (bitsRemaining <= 0) return "0000";
|
|
67
67
|
if (bitsRemaining >= 16) {
|
|
@@ -99,7 +99,7 @@ function normalizeIP(ip, options = {}) {
|
|
|
99
99
|
if (!isIPv6(ip)) return ip.toLowerCase();
|
|
100
100
|
const ipv4 = extractIPv4FromMapped(ip);
|
|
101
101
|
if (ipv4) return ipv4.toLowerCase();
|
|
102
|
-
return normalizeIPv6(ip, options.ipv6Subnet
|
|
102
|
+
return normalizeIPv6(ip, options.ipv6Subnet ?? 64);
|
|
103
103
|
}
|
|
104
104
|
/**
|
|
105
105
|
* Creates a rate limit key from IP and path
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/redirect-uri.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Zod schema for OAuth redirect URIs and other developer-supplied URLs that the
|
|
6
|
+
* server stores and later hands back to a browser.
|
|
7
|
+
*
|
|
8
|
+
* - Rejects dangerous schemes (`javascript:`, `data:`, `vbscript:`).
|
|
9
|
+
* - Rejects URIs with a fragment component (`#...`) per RFC 6749 §3.1.2.
|
|
10
|
+
* - Requires HTTPS, except for loopback hosts (`127.0.0.0/8`, `[::1]`,
|
|
11
|
+
* `*.localhost` per RFC 6761), where HTTP is allowed for local development.
|
|
12
|
+
* - Allows custom schemes for mobile apps (e.g. `myapp://callback`).
|
|
13
|
+
*
|
|
14
|
+
* This is the single source of truth for redirect-URI validation across the
|
|
15
|
+
* OAuth provider plugins. Consume it from `@better-auth/core/utils/redirect-uri`
|
|
16
|
+
* rather than re-implementing the scheme policy per plugin.
|
|
17
|
+
*/
|
|
18
|
+
declare const SafeUrlSchema: z.ZodURL;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { SafeUrlSchema };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isLoopbackHost } from "./host.mjs";
|
|
2
|
+
import { DANGEROUS_URL_SCHEMES } from "./url.mjs";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
//#region src/utils/redirect-uri.ts
|
|
5
|
+
/**
|
|
6
|
+
* Zod schema for OAuth redirect URIs and other developer-supplied URLs that the
|
|
7
|
+
* server stores and later hands back to a browser.
|
|
8
|
+
*
|
|
9
|
+
* - Rejects dangerous schemes (`javascript:`, `data:`, `vbscript:`).
|
|
10
|
+
* - Rejects URIs with a fragment component (`#...`) per RFC 6749 §3.1.2.
|
|
11
|
+
* - Requires HTTPS, except for loopback hosts (`127.0.0.0/8`, `[::1]`,
|
|
12
|
+
* `*.localhost` per RFC 6761), where HTTP is allowed for local development.
|
|
13
|
+
* - Allows custom schemes for mobile apps (e.g. `myapp://callback`).
|
|
14
|
+
*
|
|
15
|
+
* This is the single source of truth for redirect-URI validation across the
|
|
16
|
+
* OAuth provider plugins. Consume it from `@better-auth/core/utils/redirect-uri`
|
|
17
|
+
* rather than re-implementing the scheme policy per plugin.
|
|
18
|
+
*/
|
|
19
|
+
const SafeUrlSchema = z.url().superRefine((val, ctx) => {
|
|
20
|
+
let u;
|
|
21
|
+
try {
|
|
22
|
+
u = new URL(val);
|
|
23
|
+
} catch {
|
|
24
|
+
ctx.addIssue({
|
|
25
|
+
code: "custom",
|
|
26
|
+
message: "URL must be parseable",
|
|
27
|
+
fatal: true
|
|
28
|
+
});
|
|
29
|
+
return z.NEVER;
|
|
30
|
+
}
|
|
31
|
+
if (DANGEROUS_URL_SCHEMES.includes(u.protocol)) {
|
|
32
|
+
ctx.addIssue({
|
|
33
|
+
code: "custom",
|
|
34
|
+
message: "URL cannot use javascript:, data:, or vbscript: scheme"
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (val.includes("#")) ctx.addIssue({
|
|
39
|
+
code: "custom",
|
|
40
|
+
message: "Redirect URI must not contain a fragment component"
|
|
41
|
+
});
|
|
42
|
+
if (u.protocol === "http:" && !isLoopbackHost(u.host)) ctx.addIssue({
|
|
43
|
+
code: "custom",
|
|
44
|
+
message: "Redirect URI must use HTTPS (HTTP allowed only for loopback hosts)"
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
//#endregion
|
|
48
|
+
export { SafeUrlSchema };
|
package/dist/utils/string.d.mts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
//#region src/utils/string.d.ts
|
|
2
2
|
declare function capitalizeFirstLetter(str: string): string;
|
|
3
|
+
declare function toSnakeCase(input: string): string;
|
|
4
|
+
declare function toKebabCase(input: string): string;
|
|
5
|
+
declare function toCamelCase(input: string): string;
|
|
6
|
+
declare function toPascalCase(input: string): string;
|
|
3
7
|
//#endregion
|
|
4
|
-
export { capitalizeFirstLetter };
|
|
8
|
+
export { capitalizeFirstLetter, toCamelCase, toKebabCase, toPascalCase, toSnakeCase };
|
package/dist/utils/string.mjs
CHANGED
|
@@ -2,5 +2,24 @@
|
|
|
2
2
|
function capitalizeFirstLetter(str) {
|
|
3
3
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
4
4
|
}
|
|
5
|
+
const WORD_PATTERN = /[\p{Ll}\d]+|\p{Lu}+(?!\p{Ll})|\p{Lu}[\p{Ll}\d]+|\p{Lo}+/gu;
|
|
6
|
+
const APOSTROPHE_PATTERN = /['\u2019]/g;
|
|
7
|
+
function splitWords(input) {
|
|
8
|
+
return input.replace(APOSTROPHE_PATTERN, "").match(WORD_PATTERN) ?? [];
|
|
9
|
+
}
|
|
10
|
+
function toSnakeCase(input) {
|
|
11
|
+
return splitWords(input).map((word) => word.toLowerCase()).join("_");
|
|
12
|
+
}
|
|
13
|
+
function toKebabCase(input) {
|
|
14
|
+
return splitWords(input).map((word) => word.toLowerCase()).join("-");
|
|
15
|
+
}
|
|
16
|
+
function toCamelCase(input) {
|
|
17
|
+
return splitWords(input).reduce((acc, word, i) => {
|
|
18
|
+
return acc + (i === 0 ? word.toLowerCase() : `${word[0].toUpperCase()}${word.slice(1)}`);
|
|
19
|
+
}, "");
|
|
20
|
+
}
|
|
21
|
+
function toPascalCase(input) {
|
|
22
|
+
return splitWords(input).map((word) => `${word[0].toUpperCase()}${word.slice(1).toLowerCase()}`).join("");
|
|
23
|
+
}
|
|
5
24
|
//#endregion
|
|
6
|
-
export { capitalizeFirstLetter };
|
|
25
|
+
export { capitalizeFirstLetter, toCamelCase, toKebabCase, toPascalCase, toSnakeCase };
|
package/dist/utils/url.d.mts
CHANGED
|
@@ -16,5 +16,22 @@
|
|
|
16
16
|
* // Returns: "/sso/saml2/callback/provider1"
|
|
17
17
|
*/
|
|
18
18
|
declare function normalizePathname(requestUrl: string, basePath: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Schemes that execute or embed code when navigated to or accepted as a
|
|
21
|
+
* redirect target. These are never safe as an OAuth `redirect_uri` or as a
|
|
22
|
+
* client-side navigation target (`window.location.href`, `location.assign`, ...).
|
|
23
|
+
*/
|
|
24
|
+
declare const DANGEROUS_URL_SCHEMES: string[];
|
|
25
|
+
/**
|
|
26
|
+
* Returns `false` only when `value` is an absolute URL using a dangerous scheme
|
|
27
|
+
* (`javascript:`, `data:`, `vbscript:`). Relative URLs (e.g. `/dashboard`) and
|
|
28
|
+
* safe absolute schemes (`http`, `https`, custom app schemes such as
|
|
29
|
+
* `myapp://`) return `true`.
|
|
30
|
+
*
|
|
31
|
+
* Use this to guard browser navigation sinks and any redirect target that may
|
|
32
|
+
* originate from untrusted input. It is intentionally narrow: it blocks code
|
|
33
|
+
* execution schemes without rejecting relative paths or mobile deep links.
|
|
34
|
+
*/
|
|
35
|
+
declare function isSafeUrlScheme(value: string): boolean;
|
|
19
36
|
//#endregion
|
|
20
|
-
export { normalizePathname };
|
|
37
|
+
export { DANGEROUS_URL_SCHEMES, isSafeUrlScheme, normalizePathname };
|
package/dist/utils/url.mjs
CHANGED
|
@@ -27,5 +27,34 @@ function normalizePathname(requestUrl, basePath) {
|
|
|
27
27
|
if (pathname.startsWith(basePath + "/")) return pathname.slice(basePath.length).replace(/\/+$/, "") || "/";
|
|
28
28
|
return pathname;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Schemes that execute or embed code when navigated to or accepted as a
|
|
32
|
+
* redirect target. These are never safe as an OAuth `redirect_uri` or as a
|
|
33
|
+
* client-side navigation target (`window.location.href`, `location.assign`, ...).
|
|
34
|
+
*/
|
|
35
|
+
const DANGEROUS_URL_SCHEMES = [
|
|
36
|
+
"javascript:",
|
|
37
|
+
"data:",
|
|
38
|
+
"vbscript:"
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Returns `false` only when `value` is an absolute URL using a dangerous scheme
|
|
42
|
+
* (`javascript:`, `data:`, `vbscript:`). Relative URLs (e.g. `/dashboard`) and
|
|
43
|
+
* safe absolute schemes (`http`, `https`, custom app schemes such as
|
|
44
|
+
* `myapp://`) return `true`.
|
|
45
|
+
*
|
|
46
|
+
* Use this to guard browser navigation sinks and any redirect target that may
|
|
47
|
+
* originate from untrusted input. It is intentionally narrow: it blocks code
|
|
48
|
+
* execution schemes without rejecting relative paths or mobile deep links.
|
|
49
|
+
*/
|
|
50
|
+
function isSafeUrlScheme(value) {
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = new URL(value);
|
|
54
|
+
} catch {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return !DANGEROUS_URL_SCHEMES.includes(parsed.protocol);
|
|
58
|
+
}
|
|
30
59
|
//#endregion
|
|
31
|
-
export { normalizePathname };
|
|
60
|
+
export { DANGEROUS_URL_SCHEMES, isSafeUrlScheme, normalizePathname };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/core",
|
|
3
|
-
"version": "1.7.0-beta.
|
|
3
|
+
"version": "1.7.0-beta.4",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -93,12 +93,13 @@
|
|
|
93
93
|
"./instrumentation": {
|
|
94
94
|
"dev-source": "./src/instrumentation/index.ts",
|
|
95
95
|
"types": "./dist/instrumentation/index.d.mts",
|
|
96
|
+
"workerd": "./dist/instrumentation/pure.index.mjs",
|
|
97
|
+
"edge": "./dist/instrumentation/pure.index.mjs",
|
|
98
|
+
"browser": "./dist/instrumentation/pure.index.mjs",
|
|
96
99
|
"node": "./dist/instrumentation/index.mjs",
|
|
97
100
|
"deno": "./dist/instrumentation/index.mjs",
|
|
98
101
|
"bun": "./dist/instrumentation/index.mjs",
|
|
99
|
-
"
|
|
100
|
-
"workerd": "./dist/instrumentation/index.mjs",
|
|
101
|
-
"browser": "./dist/instrumentation/pure.index.mjs",
|
|
102
|
+
"import": "./dist/instrumentation/index.mjs",
|
|
102
103
|
"default": "./dist/instrumentation/index.mjs"
|
|
103
104
|
}
|
|
104
105
|
},
|
|
@@ -151,7 +152,7 @@
|
|
|
151
152
|
"zod": "^4.3.6"
|
|
152
153
|
},
|
|
153
154
|
"devDependencies": {
|
|
154
|
-
"@better-auth/utils": "0.4.
|
|
155
|
+
"@better-auth/utils": "0.4.1",
|
|
155
156
|
"@better-fetch/fetch": "1.1.21",
|
|
156
157
|
"@opentelemetry/api": "^1.9.0",
|
|
157
158
|
"@opentelemetry/sdk-trace-base": "^1.30.0",
|
|
@@ -159,18 +160,18 @@
|
|
|
159
160
|
"better-call": "1.3.5",
|
|
160
161
|
"@cloudflare/workers-types": "^4.20250121.0",
|
|
161
162
|
"jose": "^6.1.3",
|
|
162
|
-
"kysely": "^0.28.
|
|
163
|
+
"kysely": "^0.28.17 || ^0.29.0",
|
|
163
164
|
"nanostores": "^1.1.1",
|
|
164
165
|
"tsdown": "0.21.1"
|
|
165
166
|
},
|
|
166
167
|
"peerDependencies": {
|
|
167
|
-
"@better-auth/utils": "0.4.
|
|
168
|
+
"@better-auth/utils": "0.4.1",
|
|
168
169
|
"@better-fetch/fetch": "1.1.21",
|
|
169
170
|
"@opentelemetry/api": "^1.9.0",
|
|
170
171
|
"better-call": "1.3.5",
|
|
171
172
|
"@cloudflare/workers-types": ">=4",
|
|
172
173
|
"jose": "^6.1.0",
|
|
173
|
-
"kysely": "^0.28.5",
|
|
174
|
+
"kysely": "^0.28.5 || ^0.29.0",
|
|
174
175
|
"nanostores": "^1.0.1"
|
|
175
176
|
},
|
|
176
177
|
"peerDependenciesMeta": {
|