@better-auth/core 1.7.0-beta.3 → 1.7.0-beta.5
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 +3 -3
- package/dist/context/global.mjs +1 -1
- package/dist/db/adapter/factory.mjs +62 -0
- package/dist/db/adapter/index.d.mts +35 -1
- 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 -0
- package/dist/env/env-impl.mjs +1 -1
- package/dist/error/codes.d.mts +6 -0
- package/dist/error/codes.mjs +6 -0
- package/dist/index.d.mts +2 -2
- 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 +13 -2
- package/dist/oauth2/create-authorization-url.mjs +28 -7
- package/dist/oauth2/index.d.mts +13 -8
- package/dist/oauth2/index.mjs +11 -7
- package/dist/oauth2/oauth-provider.d.mts +149 -11
- package/dist/oauth2/refresh-access-token.d.mts +20 -40
- package/dist/oauth2/refresh-access-token.mjs +20 -33
- package/dist/oauth2/scopes.d.mts +76 -0
- package/dist/oauth2/scopes.mjs +96 -0
- 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 +14 -2
- package/dist/oauth2/validate-authorization-code.d.mts +17 -52
- package/dist/oauth2/validate-authorization-code.mjs +17 -30
- 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 +14 -0
- package/dist/oauth2/verify.mjs +38 -12
- package/dist/social-providers/apple.d.mts +18 -20
- package/dist/social-providers/apple.mjs +15 -28
- package/dist/social-providers/atlassian.d.mts +8 -2
- package/dist/social-providers/atlassian.mjs +9 -6
- package/dist/social-providers/cognito.d.mts +29 -3
- package/dist/social-providers/cognito.mjs +30 -34
- package/dist/social-providers/discord.d.mts +8 -2
- package/dist/social-providers/discord.mjs +20 -6
- package/dist/social-providers/dropbox.d.mts +8 -2
- package/dist/social-providers/dropbox.mjs +10 -9
- package/dist/social-providers/facebook.d.mts +24 -3
- package/dist/social-providers/facebook.mjs +51 -24
- package/dist/social-providers/figma.d.mts +8 -2
- package/dist/social-providers/figma.mjs +8 -7
- package/dist/social-providers/github.d.mts +8 -2
- package/dist/social-providers/github.mjs +9 -8
- package/dist/social-providers/gitlab.d.mts +8 -2
- package/dist/social-providers/gitlab.mjs +8 -7
- package/dist/social-providers/google.d.mts +32 -4
- package/dist/social-providers/google.mjs +26 -29
- package/dist/social-providers/huggingface.d.mts +8 -2
- package/dist/social-providers/huggingface.mjs +11 -10
- package/dist/social-providers/index.d.mts +322 -75
- package/dist/social-providers/kakao.d.mts +8 -2
- package/dist/social-providers/kakao.mjs +11 -10
- package/dist/social-providers/kick.d.mts +8 -2
- package/dist/social-providers/kick.mjs +7 -6
- package/dist/social-providers/line.d.mts +11 -3
- package/dist/social-providers/line.mjs +14 -15
- package/dist/social-providers/linear.d.mts +8 -2
- package/dist/social-providers/linear.mjs +7 -6
- package/dist/social-providers/linkedin.d.mts +8 -2
- package/dist/social-providers/linkedin.mjs +12 -11
- package/dist/social-providers/microsoft-entra-id.d.mts +33 -7
- package/dist/social-providers/microsoft-entra-id.mjs +28 -38
- package/dist/social-providers/naver.d.mts +8 -2
- package/dist/social-providers/naver.mjs +7 -6
- package/dist/social-providers/notion.d.mts +8 -2
- package/dist/social-providers/notion.mjs +9 -6
- package/dist/social-providers/paybin.d.mts +8 -2
- package/dist/social-providers/paybin.mjs +12 -11
- package/dist/social-providers/paypal.d.mts +8 -3
- package/dist/social-providers/paypal.mjs +10 -14
- package/dist/social-providers/polar.d.mts +8 -2
- package/dist/social-providers/polar.mjs +11 -10
- package/dist/social-providers/railway.d.mts +8 -2
- package/dist/social-providers/railway.mjs +11 -10
- package/dist/social-providers/reddit.d.mts +8 -2
- package/dist/social-providers/reddit.mjs +11 -9
- package/dist/social-providers/roblox.d.mts +8 -2
- package/dist/social-providers/roblox.mjs +15 -5
- package/dist/social-providers/salesforce.d.mts +8 -2
- package/dist/social-providers/salesforce.mjs +11 -10
- package/dist/social-providers/slack.d.mts +8 -2
- package/dist/social-providers/slack.mjs +18 -15
- package/dist/social-providers/spotify.d.mts +8 -2
- package/dist/social-providers/spotify.mjs +7 -6
- package/dist/social-providers/tiktok.d.mts +8 -2
- package/dist/social-providers/tiktok.mjs +21 -5
- package/dist/social-providers/twitch.d.mts +8 -2
- package/dist/social-providers/twitch.mjs +7 -6
- package/dist/social-providers/twitter.d.mts +7 -2
- package/dist/social-providers/twitter.mjs +11 -10
- package/dist/social-providers/vercel.d.mts +8 -2
- package/dist/social-providers/vercel.mjs +7 -9
- package/dist/social-providers/vk.d.mts +8 -2
- package/dist/social-providers/vk.mjs +7 -6
- package/dist/social-providers/wechat.d.mts +8 -2
- package/dist/social-providers/wechat.mjs +16 -6
- package/dist/social-providers/zoom.d.mts +10 -3
- package/dist/social-providers/zoom.mjs +14 -15
- package/dist/types/context.d.mts +33 -11
- package/dist/types/index.d.mts +1 -1
- package/dist/types/init-options.d.mts +121 -6
- 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 +13 -12
- package/src/db/adapter/factory.ts +126 -0
- package/src/db/adapter/index.ts +32 -0
- 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 -0
- package/src/env/env-impl.ts +1 -2
- package/src/error/codes.ts +6 -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 +30 -8
- package/src/oauth2/index.ts +42 -10
- package/src/oauth2/oauth-provider.ts +161 -12
- package/src/oauth2/refresh-access-token.ts +52 -78
- package/src/oauth2/scopes.ts +118 -0
- package/src/oauth2/token-endpoint-auth.ts +221 -0
- package/src/oauth2/utils.ts +21 -5
- package/src/oauth2/validate-authorization-code.ts +55 -85
- package/src/oauth2/verify-id-token.ts +111 -0
- package/src/oauth2/verify.ts +82 -15
- package/src/social-providers/apple.ts +32 -45
- package/src/social-providers/atlassian.ts +20 -9
- package/src/social-providers/cognito.ts +51 -48
- package/src/social-providers/discord.ts +37 -22
- package/src/social-providers/dropbox.ts +20 -12
- package/src/social-providers/facebook.ts +108 -57
- package/src/social-providers/figma.ts +21 -10
- package/src/social-providers/github.ts +16 -10
- package/src/social-providers/gitlab.ts +16 -8
- package/src/social-providers/google.ts +67 -46
- package/src/social-providers/huggingface.ts +20 -9
- package/src/social-providers/kakao.ts +18 -9
- package/src/social-providers/kick.ts +20 -8
- package/src/social-providers/line.ts +39 -37
- package/src/social-providers/linear.ts +20 -7
- package/src/social-providers/linkedin.ts +16 -10
- package/src/social-providers/microsoft-entra-id.ts +66 -64
- package/src/social-providers/naver.ts +14 -7
- package/src/social-providers/notion.ts +20 -7
- package/src/social-providers/paybin.ts +16 -11
- package/src/social-providers/paypal.ts +12 -25
- package/src/social-providers/polar.ts +20 -9
- package/src/social-providers/railway.ts +20 -9
- package/src/social-providers/reddit.ts +22 -10
- package/src/social-providers/roblox.ts +31 -15
- package/src/social-providers/salesforce.ts +21 -10
- package/src/social-providers/slack.ts +31 -16
- package/src/social-providers/spotify.ts +20 -7
- package/src/social-providers/tiktok.ts +32 -13
- package/src/social-providers/twitch.ts +14 -9
- package/src/social-providers/twitter.ts +18 -8
- package/src/social-providers/vercel.ts +24 -11
- package/src/social-providers/vk.ts +20 -7
- package/src/social-providers/wechat.ts +28 -8
- package/src/social-providers/zoom.ts +28 -19
- package/src/types/context.ts +33 -12
- package/src/types/index.ts +7 -0
- package/src/types/init-options.ts +148 -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
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
|
+
import {
|
|
4
|
+
RESERVED_AUTHORIZATION_PARAMS_SET,
|
|
5
|
+
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
7
|
+
validateAuthorizationCode,
|
|
8
|
+
} from "../oauth2";
|
|
4
9
|
|
|
5
10
|
/**
|
|
6
11
|
* [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)
|
|
@@ -126,22 +131,36 @@ export interface TiktokOptions extends ProviderOptions {
|
|
|
126
131
|
clientKey: string;
|
|
127
132
|
}
|
|
128
133
|
|
|
134
|
+
const TIKTOK_DEFAULT_SCOPES = ["user.info.profile"];
|
|
135
|
+
|
|
129
136
|
export const tiktok = (options: TiktokOptions) => {
|
|
130
137
|
const tokenEndpoint = "https://open.tiktokapis.com/v2/oauth/token/";
|
|
131
138
|
return {
|
|
132
139
|
id: "tiktok",
|
|
133
140
|
name: "TikTok",
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
",",
|
|
141
|
-
)}&response_type=code&client_key=${options.clientKey}&redirect_uri=${encodeURIComponent(
|
|
142
|
-
options.redirectURI || redirectURI,
|
|
143
|
-
)}&state=${state}`,
|
|
141
|
+
callbackPath: "/callback/tiktok",
|
|
142
|
+
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
143
|
+
const requestedScopes = resolveRequestedScopes(
|
|
144
|
+
options,
|
|
145
|
+
TIKTOK_DEFAULT_SCOPES,
|
|
146
|
+
scopes,
|
|
144
147
|
);
|
|
148
|
+
// TikTok uses `client_key` instead of the standard `client_id`, so the
|
|
149
|
+
// shared createAuthorizationURL helper cannot be used directly.
|
|
150
|
+
const url = new URL("https://www.tiktok.com/v2/auth/authorize");
|
|
151
|
+
url.searchParams.set("scope", requestedScopes.join(","));
|
|
152
|
+
url.searchParams.set("response_type", "code");
|
|
153
|
+
url.searchParams.set("client_key", options.clientKey);
|
|
154
|
+
url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
|
|
155
|
+
url.searchParams.set("state", state);
|
|
156
|
+
if (additionalParams) {
|
|
157
|
+
for (const [key, value] of Object.entries(additionalParams)) {
|
|
158
|
+
if (RESERVED_AUTHORIZATION_PARAMS_SET.has(key)) continue;
|
|
159
|
+
if (key === "client_key") continue;
|
|
160
|
+
url.searchParams.set(key, value);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return { url, requestedScopes };
|
|
145
164
|
},
|
|
146
165
|
|
|
147
166
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
@@ -207,5 +226,5 @@ export const tiktok = (options: TiktokOptions) => {
|
|
|
207
226
|
};
|
|
208
227
|
},
|
|
209
228
|
options,
|
|
210
|
-
} satisfies
|
|
229
|
+
} satisfies UpstreamProvider<TiktokProfile, TiktokOptions>;
|
|
211
230
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { decodeJwt } from "jose";
|
|
2
2
|
import { logger } from "../env";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
4
4
|
import {
|
|
5
5
|
createAuthorizationURL,
|
|
6
6
|
refreshAccessToken,
|
|
7
|
+
resolveRequestedScopes,
|
|
7
8
|
validateAuthorizationCode,
|
|
8
9
|
} from "../oauth2";
|
|
9
10
|
|
|
@@ -37,23 +38,26 @@ export interface TwitchOptions extends ProviderOptions<TwitchProfile> {
|
|
|
37
38
|
clientId: string;
|
|
38
39
|
claims?: string[] | undefined;
|
|
39
40
|
}
|
|
41
|
+
const TWITCH_DEFAULT_SCOPES = ["user:read:email", "openid"];
|
|
42
|
+
|
|
40
43
|
export const twitch = (options: TwitchOptions) => {
|
|
41
44
|
const tokenEndpoint = "https://id.twitch.tv/oauth2/token";
|
|
42
45
|
return {
|
|
43
46
|
id: "twitch",
|
|
44
47
|
name: "Twitch",
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
callbackPath: "/callback/twitch",
|
|
49
|
+
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
50
|
+
const requestedScopes = resolveRequestedScopes(
|
|
51
|
+
options,
|
|
52
|
+
TWITCH_DEFAULT_SCOPES,
|
|
53
|
+
scopes,
|
|
54
|
+
);
|
|
51
55
|
return createAuthorizationURL({
|
|
52
56
|
id: "twitch",
|
|
53
57
|
redirectURI,
|
|
54
58
|
options,
|
|
55
59
|
authorizationEndpoint: "https://id.twitch.tv/oauth2/authorize",
|
|
56
|
-
scopes:
|
|
60
|
+
scopes: requestedScopes,
|
|
57
61
|
state,
|
|
58
62
|
claims: options.claims || [
|
|
59
63
|
"email",
|
|
@@ -61,6 +65,7 @@ export const twitch = (options: TwitchOptions) => {
|
|
|
61
65
|
"preferred_username",
|
|
62
66
|
"picture",
|
|
63
67
|
],
|
|
68
|
+
additionalParams,
|
|
64
69
|
});
|
|
65
70
|
},
|
|
66
71
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
@@ -108,5 +113,5 @@ export const twitch = (options: TwitchOptions) => {
|
|
|
108
113
|
};
|
|
109
114
|
},
|
|
110
115
|
options,
|
|
111
|
-
} satisfies
|
|
116
|
+
} satisfies UpstreamProvider<TwitchProfile>;
|
|
112
117
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
6
7
|
validateAuthorizationCode,
|
|
7
8
|
} from "../oauth2";
|
|
8
9
|
|
|
@@ -103,25 +104,34 @@ export interface TwitterOption extends ProviderOptions<TwitterProfile> {
|
|
|
103
104
|
clientId: string;
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
const TWITTER_DEFAULT_SCOPES = [
|
|
108
|
+
"users.read",
|
|
109
|
+
"tweet.read",
|
|
110
|
+
"offline.access",
|
|
111
|
+
"users.email",
|
|
112
|
+
];
|
|
113
|
+
|
|
106
114
|
export const twitter = (options: TwitterOption) => {
|
|
107
115
|
const tokenEndpoint = "https://api.x.com/2/oauth2/token";
|
|
108
116
|
return {
|
|
109
117
|
id: "twitter",
|
|
110
118
|
name: "Twitter",
|
|
119
|
+
callbackPath: "/callback/twitter",
|
|
111
120
|
createAuthorizationURL(data) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
const requestedScopes = resolveRequestedScopes(
|
|
122
|
+
options,
|
|
123
|
+
TWITTER_DEFAULT_SCOPES,
|
|
124
|
+
data.scopes,
|
|
125
|
+
);
|
|
117
126
|
return createAuthorizationURL({
|
|
118
127
|
id: "twitter",
|
|
119
128
|
options,
|
|
120
129
|
authorizationEndpoint: "https://x.com/i/oauth2/authorize",
|
|
121
|
-
scopes:
|
|
130
|
+
scopes: requestedScopes,
|
|
122
131
|
state: data.state,
|
|
123
132
|
codeVerifier: data.codeVerifier,
|
|
124
133
|
redirectURI: data.redirectURI,
|
|
134
|
+
additionalParams: data.additionalParams,
|
|
125
135
|
});
|
|
126
136
|
},
|
|
127
137
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
@@ -195,5 +205,5 @@ export const twitter = (options: TwitterOption) => {
|
|
|
195
205
|
};
|
|
196
206
|
},
|
|
197
207
|
options,
|
|
198
|
-
} satisfies
|
|
208
|
+
} satisfies UpstreamProvider<TwitterProfile>;
|
|
199
209
|
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
2
|
import { BetterAuthError } from "../error";
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
3
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
4
|
+
import {
|
|
5
|
+
createAuthorizationURL,
|
|
6
|
+
resolveRequestedScopes,
|
|
7
|
+
validateAuthorizationCode,
|
|
8
|
+
} from "../oauth2";
|
|
5
9
|
|
|
6
10
|
export interface VercelProfile {
|
|
7
11
|
sub: string;
|
|
@@ -16,30 +20,39 @@ export interface VercelOptions extends ProviderOptions<VercelProfile> {
|
|
|
16
20
|
clientId: string;
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
const VERCEL_DEFAULT_SCOPES: string[] = [];
|
|
24
|
+
|
|
19
25
|
export const vercel = (options: VercelOptions) => {
|
|
20
26
|
return {
|
|
21
27
|
id: "vercel",
|
|
22
28
|
name: "Vercel",
|
|
23
|
-
|
|
29
|
+
callbackPath: "/callback/vercel",
|
|
30
|
+
createAuthorizationURL({
|
|
31
|
+
state,
|
|
32
|
+
scopes,
|
|
33
|
+
codeVerifier,
|
|
34
|
+
redirectURI,
|
|
35
|
+
additionalParams,
|
|
36
|
+
}) {
|
|
24
37
|
if (!codeVerifier) {
|
|
25
38
|
throw new BetterAuthError("codeVerifier is required for Vercel");
|
|
26
39
|
}
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
41
|
+
const requestedScopes = resolveRequestedScopes(
|
|
42
|
+
options,
|
|
43
|
+
VERCEL_DEFAULT_SCOPES,
|
|
44
|
+
scopes,
|
|
45
|
+
);
|
|
34
46
|
|
|
35
47
|
return createAuthorizationURL({
|
|
36
48
|
id: "vercel",
|
|
37
49
|
options,
|
|
38
50
|
authorizationEndpoint: "https://vercel.com/oauth/authorize",
|
|
39
|
-
scopes:
|
|
51
|
+
scopes: requestedScopes,
|
|
40
52
|
state,
|
|
41
53
|
codeVerifier,
|
|
42
54
|
redirectURI,
|
|
55
|
+
additionalParams,
|
|
43
56
|
});
|
|
44
57
|
},
|
|
45
58
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
@@ -83,5 +96,5 @@ export const vercel = (options: VercelOptions) => {
|
|
|
83
96
|
};
|
|
84
97
|
},
|
|
85
98
|
options,
|
|
86
|
-
} satisfies
|
|
99
|
+
} satisfies UpstreamProvider<VercelProfile>;
|
|
87
100
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
6
7
|
validateAuthorizationCode,
|
|
7
8
|
} from "../oauth2";
|
|
8
9
|
|
|
@@ -25,25 +26,37 @@ export interface VkOption extends ProviderOptions {
|
|
|
25
26
|
scheme?: ("light" | "dark") | undefined;
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
const VK_DEFAULT_SCOPES = ["email", "phone"];
|
|
30
|
+
|
|
28
31
|
export const vk = (options: VkOption) => {
|
|
29
32
|
const tokenEndpoint = "https://id.vk.com/oauth2/auth";
|
|
30
33
|
return {
|
|
31
34
|
id: "vk",
|
|
32
35
|
name: "VK",
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
callbackPath: "/callback/vk",
|
|
37
|
+
createAuthorizationURL({
|
|
38
|
+
state,
|
|
39
|
+
scopes,
|
|
40
|
+
codeVerifier,
|
|
41
|
+
redirectURI,
|
|
42
|
+
additionalParams,
|
|
43
|
+
}) {
|
|
44
|
+
const requestedScopes = resolveRequestedScopes(
|
|
45
|
+
options,
|
|
46
|
+
VK_DEFAULT_SCOPES,
|
|
47
|
+
scopes,
|
|
48
|
+
);
|
|
37
49
|
const authorizationEndpoint = "https://id.vk.com/authorize";
|
|
38
50
|
|
|
39
51
|
return createAuthorizationURL({
|
|
40
52
|
id: "vk",
|
|
41
53
|
options,
|
|
42
54
|
authorizationEndpoint,
|
|
43
|
-
scopes:
|
|
55
|
+
scopes: requestedScopes,
|
|
44
56
|
state,
|
|
45
57
|
redirectURI,
|
|
46
58
|
codeVerifier,
|
|
59
|
+
additionalParams,
|
|
47
60
|
});
|
|
48
61
|
},
|
|
49
62
|
validateAuthorizationCode: async ({
|
|
@@ -121,5 +134,5 @@ export const vk = (options: VkOption) => {
|
|
|
121
134
|
};
|
|
122
135
|
},
|
|
123
136
|
options,
|
|
124
|
-
} satisfies
|
|
137
|
+
} satisfies UpstreamProvider<VkProfile>;
|
|
125
138
|
};
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
OAuth2Tokens,
|
|
4
|
+
ProviderOptions,
|
|
5
|
+
UpstreamProvider,
|
|
6
|
+
} from "../oauth2";
|
|
7
|
+
import {
|
|
8
|
+
RESERVED_AUTHORIZATION_PARAMS_SET,
|
|
9
|
+
resolveRequestedScopes,
|
|
10
|
+
} from "../oauth2";
|
|
3
11
|
|
|
4
12
|
/**
|
|
5
13
|
* WeChat user profile information
|
|
@@ -54,27 +62,39 @@ export interface WeChatOptions extends ProviderOptions<WeChatProfile> {
|
|
|
54
62
|
lang?: "cn" | "en";
|
|
55
63
|
}
|
|
56
64
|
|
|
65
|
+
const WECHAT_DEFAULT_SCOPES = ["snsapi_login"];
|
|
66
|
+
|
|
57
67
|
export const wechat = (options: WeChatOptions) => {
|
|
58
68
|
return {
|
|
59
69
|
id: "wechat",
|
|
60
70
|
name: "WeChat",
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
callbackPath: "/callback/wechat",
|
|
72
|
+
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
73
|
+
const requestedScopes = resolveRequestedScopes(
|
|
74
|
+
options,
|
|
75
|
+
WECHAT_DEFAULT_SCOPES,
|
|
76
|
+
scopes,
|
|
77
|
+
);
|
|
65
78
|
|
|
66
79
|
// WeChat uses non-standard OAuth2 parameters (appid instead of client_id)
|
|
67
80
|
// and requires a fragment (#wechat_redirect), so we construct the URL manually.
|
|
68
81
|
const url = new URL("https://open.weixin.qq.com/connect/qrconnect");
|
|
69
|
-
url.searchParams.set("scope",
|
|
82
|
+
url.searchParams.set("scope", requestedScopes.join(","));
|
|
70
83
|
url.searchParams.set("response_type", "code");
|
|
71
84
|
url.searchParams.set("appid", options.clientId);
|
|
72
85
|
url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
|
|
73
86
|
url.searchParams.set("state", state);
|
|
74
87
|
url.searchParams.set("lang", options.lang || "cn");
|
|
88
|
+
if (additionalParams) {
|
|
89
|
+
for (const [key, value] of Object.entries(additionalParams)) {
|
|
90
|
+
if (RESERVED_AUTHORIZATION_PARAMS_SET.has(key)) continue;
|
|
91
|
+
if (key === "appid") continue;
|
|
92
|
+
url.searchParams.set(key, value);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
75
95
|
url.hash = "wechat_redirect";
|
|
76
96
|
|
|
77
|
-
return url;
|
|
97
|
+
return { url, requestedScopes };
|
|
78
98
|
},
|
|
79
99
|
|
|
80
100
|
// WeChat uses non-standard token exchange (appid/secret instead of
|
|
@@ -209,5 +229,5 @@ export const wechat = (options: WeChatOptions) => {
|
|
|
209
229
|
};
|
|
210
230
|
},
|
|
211
231
|
options,
|
|
212
|
-
} satisfies
|
|
232
|
+
} satisfies UpstreamProvider<WeChatProfile, WeChatOptions>;
|
|
213
233
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
6
7
|
validateAuthorizationCode,
|
|
7
8
|
} from "../oauth2";
|
|
8
9
|
|
|
@@ -143,6 +144,8 @@ export interface ZoomOptions extends ProviderOptions<ZoomProfile> {
|
|
|
143
144
|
pkce?: boolean | undefined;
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
const ZOOM_DEFAULT_SCOPES: string[] = [];
|
|
148
|
+
|
|
146
149
|
export const zoom = (userOptions: ZoomOptions) => {
|
|
147
150
|
const options = {
|
|
148
151
|
pkce: true,
|
|
@@ -152,24 +155,30 @@ export const zoom = (userOptions: ZoomOptions) => {
|
|
|
152
155
|
return {
|
|
153
156
|
id: "zoom",
|
|
154
157
|
name: "Zoom",
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
callbackPath: "/callback/zoom",
|
|
159
|
+
createAuthorizationURL: ({
|
|
160
|
+
state,
|
|
161
|
+
scopes,
|
|
162
|
+
redirectURI,
|
|
163
|
+
codeVerifier,
|
|
164
|
+
additionalParams,
|
|
165
|
+
}) => {
|
|
166
|
+
const requestedScopes = resolveRequestedScopes(
|
|
167
|
+
options,
|
|
168
|
+
ZOOM_DEFAULT_SCOPES,
|
|
169
|
+
scopes,
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return createAuthorizationURL({
|
|
173
|
+
id: "zoom",
|
|
174
|
+
options,
|
|
175
|
+
authorizationEndpoint: "https://zoom.us/oauth/authorize",
|
|
176
|
+
scopes: requestedScopes,
|
|
160
177
|
state,
|
|
178
|
+
redirectURI,
|
|
179
|
+
codeVerifier: options.pkce ? codeVerifier : undefined,
|
|
180
|
+
additionalParams,
|
|
161
181
|
});
|
|
162
|
-
|
|
163
|
-
if (options.pkce) {
|
|
164
|
-
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
165
|
-
params.set("code_challenge_method", "S256");
|
|
166
|
-
params.set("code_challenge", codeChallenge);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const url = new URL("https://zoom.us/oauth/authorize");
|
|
170
|
-
url.search = params.toString();
|
|
171
|
-
|
|
172
|
-
return url;
|
|
173
182
|
},
|
|
174
183
|
validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
|
|
175
184
|
return validateAuthorizationCode({
|
|
@@ -226,5 +235,5 @@ export const zoom = (userOptions: ZoomOptions) => {
|
|
|
226
235
|
},
|
|
227
236
|
};
|
|
228
237
|
},
|
|
229
|
-
} satisfies
|
|
238
|
+
} satisfies UpstreamProvider<ZoomProfile>;
|
|
230
239
|
};
|
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>>(
|
|
@@ -158,7 +158,15 @@ export interface InternalAdapter<
|
|
|
158
158
|
*/
|
|
159
159
|
deleteAccount(id: string): Promise<void>;
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Delete every session belonging to a user.
|
|
163
|
+
*/
|
|
164
|
+
deleteUserSessions(userId: string): Promise<void>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Delete sessions by their session tokens.
|
|
168
|
+
*/
|
|
169
|
+
deleteSessions(sessionTokens: string[]): Promise<void>;
|
|
162
170
|
|
|
163
171
|
findOAuthUser(
|
|
164
172
|
email: string,
|
|
@@ -196,8 +204,6 @@ export interface InternalAdapter<
|
|
|
196
204
|
|
|
197
205
|
findAccounts(userId: string): Promise<Account[]>;
|
|
198
206
|
|
|
199
|
-
findAccount(accountId: string): Promise<Account | null>;
|
|
200
|
-
|
|
201
207
|
findAccountByProviderId(
|
|
202
208
|
accountId: string,
|
|
203
209
|
providerId: string,
|
|
@@ -216,6 +222,21 @@ export interface InternalAdapter<
|
|
|
216
222
|
|
|
217
223
|
deleteVerificationByIdentifier(identifier: string): Promise<void>;
|
|
218
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Atomically consume a single-use verification row by `identifier` and
|
|
227
|
+
* return it. Only the first concurrent caller receives the latest row;
|
|
228
|
+
* subsequent callers receive `null`. Consuming one row invalidates the
|
|
229
|
+
* whole identifier so stale rows cannot be replayed. Rows past their
|
|
230
|
+
* `expiresAt` are treated as already invalid: the row is deleted but
|
|
231
|
+
* `null` is returned, so callers do not need to gate on `expiresAt`
|
|
232
|
+
* themselves. Callers MUST gate any state change (issue session, mint
|
|
233
|
+
* token, change password) on a non-null result.
|
|
234
|
+
*
|
|
235
|
+
* Replaces the racy `findVerificationValue` + `deleteVerificationByIdentifier`
|
|
236
|
+
* pair at single-use credential consumption sites.
|
|
237
|
+
*/
|
|
238
|
+
consumeVerificationValue(identifier: string): Promise<Verification | null>;
|
|
239
|
+
|
|
219
240
|
updateVerificationByIdentifier(
|
|
220
241
|
identifier: string,
|
|
221
242
|
data: Partial<Verification>,
|
|
@@ -306,7 +327,7 @@ export type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> =
|
|
|
306
327
|
* - "cookie": Store state in an encrypted cookie (stateless)
|
|
307
328
|
* - "database": Store state in the database
|
|
308
329
|
*
|
|
309
|
-
* @default "cookie"
|
|
330
|
+
* @default "database" when `database` or `secondaryStorage` is configured, "cookie" otherwise
|
|
310
331
|
*/
|
|
311
332
|
storeStateStrategy: "database" | "cookie";
|
|
312
333
|
};
|
|
@@ -330,7 +351,7 @@ export type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> =
|
|
|
330
351
|
user: User & Record<string, any>;
|
|
331
352
|
} | null,
|
|
332
353
|
) => void;
|
|
333
|
-
socialProviders:
|
|
354
|
+
socialProviders: UpstreamProvider[];
|
|
334
355
|
authCookies: BetterAuthCookies;
|
|
335
356
|
logger: ReturnType<typeof createLogger>;
|
|
336
357
|
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,
|