@better-auth/core 1.7.0-beta.6 → 1.7.0-beta.8
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/get-tables.mjs +3 -3
- package/dist/db/schema/account.d.mts +1 -1
- package/dist/db/schema/account.mjs +1 -1
- package/dist/error/codes.d.mts +0 -5
- package/dist/error/codes.mjs +0 -5
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/create-authorization-url.d.mts +4 -5
- package/dist/oauth2/create-authorization-url.mjs +4 -5
- package/dist/oauth2/index.d.mts +3 -4
- package/dist/oauth2/index.mjs +2 -3
- package/dist/oauth2/oauth-provider.d.mts +44 -48
- package/dist/oauth2/refresh-access-token.mjs +17 -2
- package/dist/oauth2/utils.d.mts +6 -1
- package/dist/oauth2/utils.mjs +24 -2
- package/dist/oauth2/verify-id-token.d.mts +6 -5
- package/dist/oauth2/verify-id-token.mjs +2 -2
- package/dist/social-providers/apple.d.mts +3 -5
- package/dist/social-providers/apple.mjs +5 -5
- package/dist/social-providers/atlassian.d.mts +3 -5
- package/dist/social-providers/atlassian.mjs +4 -4
- package/dist/social-providers/cognito.d.mts +3 -5
- package/dist/social-providers/cognito.mjs +11 -18
- package/dist/social-providers/discord.d.mts +3 -5
- package/dist/social-providers/discord.mjs +6 -7
- package/dist/social-providers/dropbox.d.mts +3 -5
- package/dist/social-providers/dropbox.mjs +5 -5
- package/dist/social-providers/facebook.d.mts +3 -5
- package/dist/social-providers/facebook.mjs +5 -5
- package/dist/social-providers/figma.d.mts +3 -5
- package/dist/social-providers/figma.mjs +5 -5
- package/dist/social-providers/github.d.mts +3 -5
- package/dist/social-providers/github.mjs +4 -4
- package/dist/social-providers/gitlab.d.mts +3 -5
- package/dist/social-providers/gitlab.mjs +6 -6
- package/dist/social-providers/google.d.mts +10 -10
- package/dist/social-providers/google.mjs +12 -13
- package/dist/social-providers/huggingface.d.mts +3 -5
- package/dist/social-providers/huggingface.mjs +8 -8
- package/dist/social-providers/index.d.mts +105 -177
- package/dist/social-providers/kakao.d.mts +3 -5
- package/dist/social-providers/kakao.mjs +8 -8
- package/dist/social-providers/kick.d.mts +3 -5
- package/dist/social-providers/kick.mjs +4 -4
- package/dist/social-providers/line.d.mts +3 -5
- package/dist/social-providers/line.mjs +10 -10
- package/dist/social-providers/linear.d.mts +3 -5
- package/dist/social-providers/linear.mjs +4 -4
- package/dist/social-providers/linkedin.d.mts +3 -5
- package/dist/social-providers/linkedin.mjs +10 -10
- package/dist/social-providers/microsoft-entra-id.d.mts +3 -5
- package/dist/social-providers/microsoft-entra-id.mjs +10 -11
- package/dist/social-providers/naver.d.mts +3 -5
- package/dist/social-providers/naver.mjs +4 -4
- package/dist/social-providers/notion.d.mts +3 -5
- package/dist/social-providers/notion.mjs +4 -4
- package/dist/social-providers/paybin.d.mts +3 -5
- package/dist/social-providers/paybin.mjs +10 -10
- package/dist/social-providers/paypal.d.mts +3 -5
- package/dist/social-providers/paypal.mjs +2 -8
- package/dist/social-providers/polar.d.mts +3 -5
- package/dist/social-providers/polar.mjs +8 -8
- package/dist/social-providers/railway.d.mts +3 -5
- package/dist/social-providers/railway.mjs +9 -9
- package/dist/social-providers/reddit.d.mts +3 -5
- package/dist/social-providers/reddit.mjs +5 -5
- package/dist/social-providers/roblox.d.mts +3 -5
- package/dist/social-providers/roblox.mjs +5 -5
- package/dist/social-providers/salesforce.d.mts +3 -5
- package/dist/social-providers/salesforce.mjs +8 -8
- package/dist/social-providers/slack.d.mts +3 -5
- package/dist/social-providers/slack.mjs +9 -9
- package/dist/social-providers/spotify.d.mts +3 -5
- package/dist/social-providers/spotify.mjs +5 -5
- package/dist/social-providers/tiktok.d.mts +3 -5
- package/dist/social-providers/tiktok.mjs +5 -9
- package/dist/social-providers/twitch.d.mts +3 -5
- package/dist/social-providers/twitch.mjs +4 -4
- package/dist/social-providers/twitter.d.mts +3 -5
- package/dist/social-providers/twitter.mjs +9 -9
- package/dist/social-providers/vercel.d.mts +3 -5
- package/dist/social-providers/vercel.mjs +7 -4
- package/dist/social-providers/vk.d.mts +3 -5
- package/dist/social-providers/vk.mjs +5 -5
- package/dist/social-providers/wechat.d.mts +3 -5
- package/dist/social-providers/wechat.mjs +5 -9
- package/dist/social-providers/zoom.d.mts +3 -6
- package/dist/social-providers/zoom.mjs +9 -15
- package/dist/types/context.d.mts +6 -2
- package/dist/utils/host.d.mts +1 -1
- package/dist/utils/host.mjs +3 -0
- package/package.json +1 -1
- package/src/db/get-tables.ts +3 -8
- package/src/db/schema/account.ts +5 -14
- package/src/error/codes.ts +0 -5
- package/src/oauth2/create-authorization-url.ts +5 -1
- package/src/oauth2/index.ts +3 -12
- package/src/oauth2/oauth-provider.ts +46 -53
- package/src/oauth2/refresh-access-token.ts +30 -5
- package/src/oauth2/utils.ts +39 -1
- package/src/oauth2/verify-id-token.ts +9 -5
- package/src/social-providers/apple.ts +8 -13
- package/src/social-providers/atlassian.ts +8 -12
- package/src/social-providers/cognito.ts +11 -18
- package/src/social-providers/discord.ts +8 -19
- package/src/social-providers/dropbox.ts +7 -13
- package/src/social-providers/facebook.ts +9 -13
- package/src/social-providers/figma.ts +9 -13
- package/src/social-providers/github.ts +8 -12
- package/src/social-providers/gitlab.ts +8 -14
- package/src/social-providers/google.ts +23 -29
- package/src/social-providers/huggingface.ts +8 -12
- package/src/social-providers/kakao.ts +8 -16
- package/src/social-providers/kick.ts +7 -12
- package/src/social-providers/line.ts +10 -14
- package/src/social-providers/linear.ts +6 -12
- package/src/social-providers/linkedin.ts +10 -14
- package/src/social-providers/microsoft-entra-id.ts +8 -18
- package/src/social-providers/naver.ts +6 -12
- package/src/social-providers/notion.ts +6 -12
- package/src/social-providers/paybin.ts +11 -14
- package/src/social-providers/paypal.ts +8 -6
- package/src/social-providers/polar.ts +8 -12
- package/src/social-providers/railway.ts +9 -13
- package/src/social-providers/reddit.ts +7 -18
- package/src/social-providers/roblox.ts +7 -18
- package/src/social-providers/salesforce.ts +8 -12
- package/src/social-providers/slack.ts +9 -18
- package/src/social-providers/spotify.ts +7 -13
- package/src/social-providers/tiktok.ts +7 -13
- package/src/social-providers/twitch.ts +8 -12
- package/src/social-providers/twitter.ts +8 -17
- package/src/social-providers/vercel.ts +10 -16
- package/src/social-providers/vk.ts +7 -13
- package/src/social-providers/wechat.ts +8 -20
- package/src/social-providers/zoom.ts +6 -19
- package/src/types/context.ts +8 -2
- package/src/utils/host.ts +10 -1
- package/dist/oauth2/scopes.d.mts +0 -76
- package/dist/oauth2/scopes.mjs +0 -96
- package/src/oauth2/scopes.ts +0 -118
|
@@ -14,7 +14,6 @@ interface VercelOptions extends ProviderOptions<VercelProfile> {
|
|
|
14
14
|
declare const vercel: (options: VercelOptions) => {
|
|
15
15
|
id: "vercel";
|
|
16
16
|
name: string;
|
|
17
|
-
callbackPath: string;
|
|
18
17
|
createAuthorizationURL({
|
|
19
18
|
state,
|
|
20
19
|
scopes,
|
|
@@ -28,11 +27,9 @@ declare const vercel: (options: VercelOptions) => {
|
|
|
28
27
|
redirectURI: string;
|
|
29
28
|
display?: string | undefined;
|
|
30
29
|
loginHint?: string | undefined;
|
|
30
|
+
idTokenNonce?: string | undefined;
|
|
31
31
|
additionalParams?: Record<string, string> | undefined;
|
|
32
|
-
}): Promise<
|
|
33
|
-
url: URL;
|
|
34
|
-
requestedScopes: string[];
|
|
35
|
-
}>;
|
|
32
|
+
}): Promise<URL>;
|
|
36
33
|
validateAuthorizationCode: ({
|
|
37
34
|
code,
|
|
38
35
|
codeVerifier,
|
|
@@ -44,6 +41,7 @@ declare const vercel: (options: VercelOptions) => {
|
|
|
44
41
|
deviceId?: string | undefined;
|
|
45
42
|
}) => Promise<OAuth2Tokens>;
|
|
46
43
|
getUserInfo(token: OAuth2Tokens & {
|
|
44
|
+
expectedIdTokenNonce?: string | undefined;
|
|
47
45
|
user?: {
|
|
48
46
|
name?: {
|
|
49
47
|
firstName?: string;
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { BetterAuthError } from "../error/index.mjs";
|
|
2
|
-
import { resolveRequestedScopes } from "../oauth2/scopes.mjs";
|
|
3
2
|
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
4
3
|
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
5
4
|
import { betterFetch } from "@better-fetch/fetch";
|
|
6
5
|
//#region src/social-providers/vercel.ts
|
|
7
|
-
const VERCEL_DEFAULT_SCOPES = [];
|
|
8
6
|
const vercel = (options) => {
|
|
9
7
|
return {
|
|
10
8
|
id: "vercel",
|
|
11
9
|
name: "Vercel",
|
|
12
|
-
callbackPath: "/callback/vercel",
|
|
13
10
|
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
|
|
14
11
|
if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Vercel");
|
|
12
|
+
let _scopes = void 0;
|
|
13
|
+
if (options.scope !== void 0 || scopes !== void 0) {
|
|
14
|
+
_scopes = [];
|
|
15
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
16
|
+
if (scopes) _scopes.push(...scopes);
|
|
17
|
+
}
|
|
15
18
|
return createAuthorizationURL({
|
|
16
19
|
id: "vercel",
|
|
17
20
|
options,
|
|
18
21
|
authorizationEndpoint: "https://vercel.com/oauth/authorize",
|
|
19
|
-
scopes:
|
|
22
|
+
scopes: _scopes,
|
|
20
23
|
state,
|
|
21
24
|
codeVerifier,
|
|
22
25
|
redirectURI,
|
|
@@ -20,7 +20,6 @@ interface VkOption extends ProviderOptions {
|
|
|
20
20
|
declare const vk: (options: VkOption) => {
|
|
21
21
|
id: "vk";
|
|
22
22
|
name: string;
|
|
23
|
-
callbackPath: string;
|
|
24
23
|
createAuthorizationURL({
|
|
25
24
|
state,
|
|
26
25
|
scopes,
|
|
@@ -34,11 +33,9 @@ declare const vk: (options: VkOption) => {
|
|
|
34
33
|
redirectURI: string;
|
|
35
34
|
display?: string | undefined;
|
|
36
35
|
loginHint?: string | undefined;
|
|
36
|
+
idTokenNonce?: string | undefined;
|
|
37
37
|
additionalParams?: Record<string, string> | undefined;
|
|
38
|
-
}): Promise<
|
|
39
|
-
url: URL;
|
|
40
|
-
requestedScopes: string[];
|
|
41
|
-
}>;
|
|
38
|
+
}): Promise<URL>;
|
|
42
39
|
validateAuthorizationCode: ({
|
|
43
40
|
code,
|
|
44
41
|
codeVerifier,
|
|
@@ -52,6 +49,7 @@ declare const vk: (options: VkOption) => {
|
|
|
52
49
|
}) => Promise<OAuth2Tokens>;
|
|
53
50
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
54
51
|
getUserInfo(data: OAuth2Tokens & {
|
|
52
|
+
expectedIdTokenNonce?: string | undefined;
|
|
55
53
|
user?: {
|
|
56
54
|
name?: {
|
|
57
55
|
firstName?: string;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { resolveRequestedScopes } from "../oauth2/scopes.mjs";
|
|
2
1
|
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
3
2
|
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
4
3
|
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
5
4
|
import { betterFetch } from "@better-fetch/fetch";
|
|
6
5
|
//#region src/social-providers/vk.ts
|
|
7
|
-
const VK_DEFAULT_SCOPES = ["email", "phone"];
|
|
8
6
|
const vk = (options) => {
|
|
9
7
|
const tokenEndpoint = "https://id.vk.com/oauth2/auth";
|
|
10
8
|
return {
|
|
11
9
|
id: "vk",
|
|
12
10
|
name: "VK",
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
|
|
12
|
+
const _scopes = options.disableDefaultScope ? [] : ["email", "phone"];
|
|
13
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
14
|
+
if (scopes) _scopes.push(...scopes);
|
|
15
15
|
return createAuthorizationURL({
|
|
16
16
|
id: "vk",
|
|
17
17
|
options,
|
|
18
18
|
authorizationEndpoint: "https://id.vk.com/authorize",
|
|
19
|
-
scopes:
|
|
19
|
+
scopes: _scopes,
|
|
20
20
|
state,
|
|
21
21
|
redirectURI,
|
|
22
22
|
codeVerifier,
|
|
@@ -53,7 +53,6 @@ interface WeChatOptions extends ProviderOptions<WeChatProfile> {
|
|
|
53
53
|
declare const wechat: (options: WeChatOptions) => {
|
|
54
54
|
id: "wechat";
|
|
55
55
|
name: string;
|
|
56
|
-
callbackPath: string;
|
|
57
56
|
createAuthorizationURL({
|
|
58
57
|
state,
|
|
59
58
|
scopes,
|
|
@@ -66,11 +65,9 @@ declare const wechat: (options: WeChatOptions) => {
|
|
|
66
65
|
redirectURI: string;
|
|
67
66
|
display?: string | undefined;
|
|
68
67
|
loginHint?: string | undefined;
|
|
68
|
+
idTokenNonce?: string | undefined;
|
|
69
69
|
additionalParams?: Record<string, string> | undefined;
|
|
70
|
-
}):
|
|
71
|
-
url: URL;
|
|
72
|
-
requestedScopes: string[];
|
|
73
|
-
};
|
|
70
|
+
}): URL;
|
|
74
71
|
validateAuthorizationCode: ({
|
|
75
72
|
code
|
|
76
73
|
}: {
|
|
@@ -95,6 +92,7 @@ declare const wechat: (options: WeChatOptions) => {
|
|
|
95
92
|
scopes: string[];
|
|
96
93
|
}>);
|
|
97
94
|
getUserInfo(token: OAuth2Tokens & {
|
|
95
|
+
expectedIdTokenNonce?: string | undefined;
|
|
98
96
|
user?: {
|
|
99
97
|
name?: {
|
|
100
98
|
firstName?: string;
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { resolveRequestedScopes } from "../oauth2/scopes.mjs";
|
|
2
1
|
import { RESERVED_AUTHORIZATION_PARAMS_SET } from "../oauth2/create-authorization-url.mjs";
|
|
3
2
|
import { betterFetch } from "@better-fetch/fetch";
|
|
4
3
|
//#region src/social-providers/wechat.ts
|
|
5
|
-
const WECHAT_DEFAULT_SCOPES = ["snsapi_login"];
|
|
6
4
|
const wechat = (options) => {
|
|
7
5
|
return {
|
|
8
6
|
id: "wechat",
|
|
9
7
|
name: "WeChat",
|
|
10
|
-
callbackPath: "/callback/wechat",
|
|
11
8
|
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
12
|
-
const
|
|
9
|
+
const _scopes = options.disableDefaultScope ? [] : ["snsapi_login"];
|
|
10
|
+
options.scope && _scopes.push(...options.scope);
|
|
11
|
+
scopes && _scopes.push(...scopes);
|
|
13
12
|
const url = new URL("https://open.weixin.qq.com/connect/qrconnect");
|
|
14
|
-
url.searchParams.set("scope",
|
|
13
|
+
url.searchParams.set("scope", _scopes.join(","));
|
|
15
14
|
url.searchParams.set("response_type", "code");
|
|
16
15
|
url.searchParams.set("appid", options.clientId);
|
|
17
16
|
url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
|
|
@@ -23,10 +22,7 @@ const wechat = (options) => {
|
|
|
23
22
|
url.searchParams.set(key, value);
|
|
24
23
|
}
|
|
25
24
|
url.hash = "wechat_redirect";
|
|
26
|
-
return
|
|
27
|
-
url,
|
|
28
|
-
requestedScopes
|
|
29
|
-
};
|
|
25
|
+
return url;
|
|
30
26
|
},
|
|
31
27
|
validateAuthorizationCode: async ({ code }) => {
|
|
32
28
|
const { data: tokenData, error } = await betterFetch("https://api.weixin.qq.com/sns/oauth2/access_token?" + new URLSearchParams({
|
|
@@ -116,10 +116,8 @@ interface ZoomOptions extends ProviderOptions<ZoomProfile> {
|
|
|
116
116
|
declare const zoom: (userOptions: ZoomOptions) => {
|
|
117
117
|
id: "zoom";
|
|
118
118
|
name: string;
|
|
119
|
-
callbackPath: string;
|
|
120
119
|
createAuthorizationURL: ({
|
|
121
120
|
state,
|
|
122
|
-
scopes,
|
|
123
121
|
redirectURI,
|
|
124
122
|
codeVerifier,
|
|
125
123
|
additionalParams
|
|
@@ -130,11 +128,9 @@ declare const zoom: (userOptions: ZoomOptions) => {
|
|
|
130
128
|
redirectURI: string;
|
|
131
129
|
display?: string | undefined;
|
|
132
130
|
loginHint?: string | undefined;
|
|
131
|
+
idTokenNonce?: string | undefined;
|
|
133
132
|
additionalParams?: Record<string, string> | undefined;
|
|
134
|
-
}) => Promise<
|
|
135
|
-
url: URL;
|
|
136
|
-
requestedScopes: string[];
|
|
137
|
-
}>;
|
|
133
|
+
}) => Promise<URL>;
|
|
138
134
|
validateAuthorizationCode: ({
|
|
139
135
|
code,
|
|
140
136
|
redirectURI,
|
|
@@ -147,6 +143,7 @@ declare const zoom: (userOptions: ZoomOptions) => {
|
|
|
147
143
|
}) => Promise<OAuth2Tokens>;
|
|
148
144
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
149
145
|
getUserInfo(token: OAuth2Tokens & {
|
|
146
|
+
expectedIdTokenNonce?: string | undefined;
|
|
150
147
|
user?: {
|
|
151
148
|
name?: {
|
|
152
149
|
firstName?: string;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { resolveRequestedScopes } from "../oauth2/scopes.mjs";
|
|
2
1
|
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
3
2
|
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
4
3
|
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
5
4
|
import { betterFetch } from "@better-fetch/fetch";
|
|
6
5
|
//#region src/social-providers/zoom.ts
|
|
7
|
-
const ZOOM_DEFAULT_SCOPES = [];
|
|
8
6
|
const zoom = (userOptions) => {
|
|
9
7
|
const options = {
|
|
10
8
|
pkce: true,
|
|
@@ -13,19 +11,15 @@ const zoom = (userOptions) => {
|
|
|
13
11
|
return {
|
|
14
12
|
id: "zoom",
|
|
15
13
|
name: "Zoom",
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
codeVerifier: options.pkce ? codeVerifier : void 0,
|
|
26
|
-
additionalParams
|
|
27
|
-
});
|
|
28
|
-
},
|
|
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
|
+
}),
|
|
29
23
|
validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
|
|
30
24
|
return validateAuthorizationCode({
|
|
31
25
|
code,
|
package/dist/types/context.d.mts
CHANGED
|
@@ -10,7 +10,7 @@ import { BetterAuthOptions, BetterAuthRateLimitOptions, UserProvisioningSource }
|
|
|
10
10
|
import { Account } from "../db/schema/account.mjs";
|
|
11
11
|
import { BetterAuthCookie, BetterAuthCookies } from "./cookie.mjs";
|
|
12
12
|
import { SecretConfig } from "./secret.mjs";
|
|
13
|
-
import {
|
|
13
|
+
import { OAuthProvider } from "../oauth2/oauth-provider.mjs";
|
|
14
14
|
import { CookieOptions, EndpointContext } from "better-call";
|
|
15
15
|
|
|
16
16
|
//#region src/types/context.d.ts
|
|
@@ -54,6 +54,10 @@ type GenericEndpointContext<Options extends BetterAuthOptions = BetterAuthOption
|
|
|
54
54
|
context: AuthContext<Options>;
|
|
55
55
|
};
|
|
56
56
|
interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions> {
|
|
57
|
+
createOAuthUser(user: Omit<User, "id" | "createdAt" | "updatedAt">, account: Omit<Account, "userId" | "id" | "createdAt" | "updatedAt"> & Partial<Account>): Promise<{
|
|
58
|
+
user: User;
|
|
59
|
+
account: Account;
|
|
60
|
+
}>;
|
|
57
61
|
createUser<T extends Record<string, any>>(user: Omit<User, "id" | "createdAt" | "updatedAt" | "emailVerified"> & Partial<User> & Record<string, any>,
|
|
58
62
|
/**
|
|
59
63
|
* Provisioning source. The creation seam adds `action: "create-user"` and
|
|
@@ -232,7 +236,7 @@ type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> = Plugin
|
|
|
232
236
|
session: Session & Record<string, any>;
|
|
233
237
|
user: User & Record<string, any>;
|
|
234
238
|
} | null) => void;
|
|
235
|
-
socialProviders:
|
|
239
|
+
socialProviders: OAuthProvider[];
|
|
236
240
|
authCookies: BetterAuthCookies;
|
|
237
241
|
logger: ReturnType<typeof createLogger>;
|
|
238
242
|
rateLimit: {
|
package/dist/utils/host.d.mts
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* The semantic kind of a host, derived from RFC 6890 special-purpose registries
|
|
27
27
|
* plus a few domain-name categories (localhost, cloud metadata FQDNs).
|
|
28
28
|
*/
|
|
29
|
-
type HostKind = /** IPv4 `127.0.0.0/8` or IPv6 `::1`. */"loopback" /** DNS name `localhost` or RFC 6761 `.localhost` TLD. */ | "localhost" /** IPv4 `0.0.0.0` or IPv6 `::` — "this host on this network", not loopback. */ | "unspecified" /** RFC 1918 `10/8`, `172.16/12`, `192.168/16`, or IPv6 ULA `fc00::/7`. */ | "private" /** IPv4 `169.254/16` or IPv6 `fe80::/10`. Includes AWS IMDS `169.254.169.254`. */ | "linkLocal" /** RFC 6598 carrier-grade NAT `100.64.0.0/10`. */ | "sharedAddressSpace" /** RFC 5737 `192.0.2/24`, `198.51.100/24`, `203.0.113/24`, or RFC 3849 `2001:db8::/32`. */ | "documentation" /** RFC 2544 `198.18.0.0/15`. */ | "benchmarking" /** IPv4 `224.0.0.0/4` or IPv6 `ff00::/8`. */ | "multicast" /** IPv4 limited broadcast `255.255.255.255`. */ | "broadcast" /** Other RFC 6890 special-purpose ranges (0/8, 192.0.0/24, 240/4, 2001::/32, etc.). */ | "reserved" /** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */ | "cloudMetadata" /** Any host not matching a special-purpose range above. */ | "public";
|
|
29
|
+
type HostKind = /** IPv4 `127.0.0.0/8` or IPv6 `::1`. */"loopback" /** DNS name `localhost` or RFC 6761 `.localhost` TLD. */ | "localhost" /** IPv4 `0.0.0.0` or IPv6 `::` — "this host on this network", not loopback. */ | "unspecified" /** RFC 1918 `10/8`, `172.16/12`, `192.168/16`, or IPv6 ULA `fc00::/7`. */ | "private" /** IPv4 `169.254/16` or IPv6 `fe80::/10`. Includes AWS IMDS `169.254.169.254`. */ | "linkLocal" /** RFC 6598 carrier-grade NAT `100.64.0.0/10`. */ | "sharedAddressSpace" /** RFC 5737 `192.0.2/24`, `198.51.100/24`, `203.0.113/24`, or RFC 3849 `2001:db8::/32`. */ | "documentation" /** RFC 2544 `198.18.0.0/15`. */ | "benchmarking" /** IPv4 `224.0.0.0/4` or IPv6 `ff00::/8`. */ | "multicast" /** IPv4 limited broadcast `255.255.255.255`. */ | "broadcast" /** Other RFC 6890 special-purpose ranges (0.0.0.0/8, 192.0.0.0/24, 192.88.99.0/24, 240.0.0.0/4, 2001::/32, etc.). */ | "reserved" /** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */ | "cloudMetadata" /** Any host not matching a special-purpose range above. */ | "public";
|
|
30
30
|
/**
|
|
31
31
|
* The syntactic form of the input host: an IPv4 literal, an IPv6 literal, or
|
|
32
32
|
* a domain name. IPv4-mapped IPv6 (`::ffff:192.0.2.1`) is reported as `ipv4`
|
package/dist/utils/host.mjs
CHANGED
|
@@ -86,6 +86,7 @@ function classifyIPv4(ip) {
|
|
|
86
86
|
if (inIPv4Range(n, ipv4ToUint32("224.0.0.0"), 4)) return "multicast";
|
|
87
87
|
if (inIPv4Range(n, ipv4ToUint32("0.0.0.0"), 8)) return "reserved";
|
|
88
88
|
if (inIPv4Range(n, ipv4ToUint32("192.0.0.0"), 24)) return "reserved";
|
|
89
|
+
if (inIPv4Range(n, ipv4ToUint32("192.88.99.0"), 24)) return "reserved";
|
|
89
90
|
if (inIPv4Range(n, ipv4ToUint32("240.0.0.0"), 4)) return "reserved";
|
|
90
91
|
return "public";
|
|
91
92
|
}
|
|
@@ -124,6 +125,7 @@ function classifyIPv6(expanded) {
|
|
|
124
125
|
const secondByte = Number.parseInt(expanded.slice(2, 4), 16);
|
|
125
126
|
if (firstByte === 255) return "multicast";
|
|
126
127
|
if (firstByte === 254 && (secondByte & 192) === 128) return "linkLocal";
|
|
128
|
+
if (firstByte === 254 && (secondByte & 192) === 192) return "reserved";
|
|
127
129
|
if ((firstByte & 254) === 252) return "private";
|
|
128
130
|
if (expanded.startsWith("2001:0db8:")) return "documentation";
|
|
129
131
|
if (expanded.startsWith("2001:0002:0000:")) return "benchmarking";
|
|
@@ -146,6 +148,7 @@ function classifyIPv6(expanded) {
|
|
|
146
148
|
if (expanded.startsWith("0100:0000:0000:0000:")) return "reserved";
|
|
147
149
|
if (expanded.startsWith("3fff:0")) return "documentation";
|
|
148
150
|
if (expanded.startsWith("5f00:")) return "reserved";
|
|
151
|
+
if (expanded.startsWith("0000:0000:0000:0000:0000:0000:")) return "reserved";
|
|
149
152
|
return "public";
|
|
150
153
|
}
|
|
151
154
|
/**
|
package/package.json
CHANGED
package/src/db/get-tables.ts
CHANGED
|
@@ -261,15 +261,10 @@ export const getAuthTables = (
|
|
|
261
261
|
options.account?.fields?.refreshTokenExpiresAt ||
|
|
262
262
|
"refreshTokenExpiresAt",
|
|
263
263
|
},
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
// value. Upgrading installs need a manual data migration (split
|
|
267
|
-
// legacy `scope` on comma/space, trim, drop empties, dedupe). Order
|
|
268
|
-
// is insignificant per RFC 6749 §3.3.
|
|
269
|
-
grantedScopes: {
|
|
270
|
-
type: "string[]",
|
|
264
|
+
scope: {
|
|
265
|
+
type: "string",
|
|
271
266
|
required: false,
|
|
272
|
-
fieldName: options.account?.fields?.
|
|
267
|
+
fieldName: options.account?.fields?.scope || "scope",
|
|
273
268
|
},
|
|
274
269
|
password: {
|
|
275
270
|
type: "string",
|
package/src/db/schema/account.ts
CHANGED
|
@@ -23,21 +23,12 @@ export const accountSchema = coreSchema.extend({
|
|
|
23
23
|
*/
|
|
24
24
|
refreshTokenExpiresAt: z.date().nullish(),
|
|
25
25
|
/**
|
|
26
|
-
* The scopes the user has granted
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* Renamed from the legacy comma-joined `scope` string. Breaking, with no
|
|
32
|
-
* automatic data migration (and no read-time shim): the migration generator
|
|
33
|
-
* only adds the new `grantedScopes` column, so legacy accounts read as empty
|
|
34
|
-
* here until an upgrade backfills `grantedScopes` from the old `scope` values
|
|
35
|
-
* (split on comma/space, trim, drop empties, dedupe). See the release
|
|
36
|
-
* changeset for the backfill.
|
|
37
|
-
*
|
|
38
|
-
* @see https://www.rfc-editor.org/rfc/rfc6749#section-3.3
|
|
26
|
+
* The set of OAuth scopes the user has granted to this account, stored
|
|
27
|
+
* as a comma-separated list. Represents the accumulated grant rather
|
|
28
|
+
* than the latest token's `scope` claim, since per RFC 6749 Section 1.5 a
|
|
29
|
+
* token's scope may be narrower than the user's grant.
|
|
39
30
|
*/
|
|
40
|
-
|
|
31
|
+
scope: z.string().nullish(),
|
|
41
32
|
/**
|
|
42
33
|
* Password is only stored in the credential provider
|
|
43
34
|
*/
|
package/src/error/codes.ts
CHANGED
|
@@ -29,11 +29,6 @@ export const BASE_ERROR_CODES = defineErrorCodes({
|
|
|
29
29
|
TOKEN_EXPIRED: "Token expired",
|
|
30
30
|
ID_TOKEN_NOT_SUPPORTED: "id_token not supported",
|
|
31
31
|
FAILED_TO_GET_USER_INFO: "Failed to get user info",
|
|
32
|
-
PROVIDER_NOT_SUPPORTED: "Provider not supported",
|
|
33
|
-
TOKEN_REFRESH_NOT_SUPPORTED: "Token refresh not supported",
|
|
34
|
-
REFRESH_TOKEN_NOT_FOUND: "Refresh token not found",
|
|
35
|
-
FAILED_TO_GET_ACCESS_TOKEN: "Failed to get a valid access token",
|
|
36
|
-
FAILED_TO_REFRESH_ACCESS_TOKEN: "Failed to refresh access token",
|
|
37
32
|
USER_EMAIL_NOT_FOUND: "User email not found",
|
|
38
33
|
EMAIL_NOT_VERIFIED: "Email not verified",
|
|
39
34
|
PASSWORD_TOO_SHORT: "Password too short",
|
|
@@ -15,6 +15,7 @@ export const RESERVED_AUTHORIZATION_PARAMS = [
|
|
|
15
15
|
"response_type",
|
|
16
16
|
"code_challenge",
|
|
17
17
|
"code_challenge_method",
|
|
18
|
+
"nonce",
|
|
18
19
|
"scope",
|
|
19
20
|
] as const;
|
|
20
21
|
|
|
@@ -37,6 +38,7 @@ export async function createAuthorizationURL({
|
|
|
37
38
|
responseType,
|
|
38
39
|
display,
|
|
39
40
|
loginHint,
|
|
41
|
+
nonce,
|
|
40
42
|
hd,
|
|
41
43
|
responseMode,
|
|
42
44
|
additionalParams,
|
|
@@ -56,6 +58,7 @@ export async function createAuthorizationURL({
|
|
|
56
58
|
responseType?: string | undefined;
|
|
57
59
|
display?: string | undefined;
|
|
58
60
|
loginHint?: string | undefined;
|
|
61
|
+
nonce?: string | undefined;
|
|
59
62
|
hd?: string | undefined;
|
|
60
63
|
responseMode?: string | undefined;
|
|
61
64
|
additionalParams?: Record<string, string> | undefined;
|
|
@@ -77,6 +80,7 @@ export async function createAuthorizationURL({
|
|
|
77
80
|
duration && url.searchParams.set("duration", duration);
|
|
78
81
|
display && url.searchParams.set("display", display);
|
|
79
82
|
loginHint && url.searchParams.set("login_hint", loginHint);
|
|
83
|
+
nonce && url.searchParams.set("nonce", nonce);
|
|
80
84
|
prompt && url.searchParams.set("prompt", prompt);
|
|
81
85
|
hd && url.searchParams.set("hd", hd);
|
|
82
86
|
accessType && url.searchParams.set("access_type", accessType);
|
|
@@ -107,5 +111,5 @@ export async function createAuthorizationURL({
|
|
|
107
111
|
url.searchParams.set(key, value);
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
|
-
return
|
|
114
|
+
return url;
|
|
111
115
|
}
|
package/src/oauth2/index.ts
CHANGED
|
@@ -63,27 +63,17 @@ export {
|
|
|
63
63
|
verifyDpopProof,
|
|
64
64
|
} from "./dpop";
|
|
65
65
|
export type {
|
|
66
|
-
AuthorizationURLResult,
|
|
67
|
-
GrantAuthority,
|
|
68
66
|
OAuth2Tokens,
|
|
69
67
|
OAuth2UserInfo,
|
|
70
68
|
OAuthIdTokenConfig,
|
|
71
|
-
|
|
69
|
+
OAuthProvider,
|
|
70
|
+
OAuthRefreshContext,
|
|
72
71
|
ProviderOptions,
|
|
73
|
-
UpstreamProvider,
|
|
74
72
|
} from "./oauth-provider";
|
|
75
73
|
export {
|
|
76
74
|
refreshAccessToken,
|
|
77
75
|
refreshAccessTokenRequest,
|
|
78
76
|
} from "./refresh-access-token";
|
|
79
|
-
export {
|
|
80
|
-
includesGrantedScope,
|
|
81
|
-
normalizeScopes,
|
|
82
|
-
parseScopeField,
|
|
83
|
-
readGrantedScopes,
|
|
84
|
-
resolveRequestedScopes,
|
|
85
|
-
unionGrantedScopes,
|
|
86
|
-
} from "./scopes";
|
|
87
77
|
export type {
|
|
88
78
|
TokenEndpointAuth,
|
|
89
79
|
TokenEndpointAuthMethod,
|
|
@@ -94,6 +84,7 @@ export {
|
|
|
94
84
|
generateCodeChallenge,
|
|
95
85
|
getOAuth2Tokens,
|
|
96
86
|
getPrimaryClientId,
|
|
87
|
+
mergeScopes,
|
|
97
88
|
} from "./utils";
|
|
98
89
|
export {
|
|
99
90
|
authorizationCodeRequest,
|
|
@@ -78,63 +78,33 @@ export type OAuth2UserInfo = {
|
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
81
|
+
* Request metadata available to provider refresh hooks.
|
|
82
82
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* callback can fall back to the request when the provider omits `scope` from
|
|
87
|
-
* its token response (RFC 6749 §5.1).
|
|
83
|
+
* The refresh flow may be triggered by endpoints such as `getAccessToken` or
|
|
84
|
+
* `refreshToken`; this context gives provider hooks access to the triggering
|
|
85
|
+
* request without exposing the full endpoint implementation surface.
|
|
88
86
|
*/
|
|
89
|
-
export interface
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
export interface OAuthRefreshContext {
|
|
88
|
+
headers?: Headers | undefined;
|
|
89
|
+
request?: Request | undefined;
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
|
|
95
|
-
* How much an RP trusts a provider's echoed token-response `scope` when
|
|
96
|
-
* persisting `account.grantedScopes`.
|
|
97
|
-
*
|
|
98
|
-
* - `"full-grant"`: the echo is the user's complete current grant, so the seam
|
|
99
|
-
* replaces the stored grant with it. This is the only path that may narrow
|
|
100
|
-
* the grant. Declare it only for providers whose token response reports the
|
|
101
|
-
* full combined grant, e.g. Google with `include_granted_scopes`.
|
|
102
|
-
* - `"projection"`: the echo is this request's subset, so the seam unions it
|
|
103
|
-
* onto the stored grant. The safe default for every provider.
|
|
104
|
-
* - `"absent-echo"`: the provider omitted `scope`, so the grant equals what was
|
|
105
|
-
* requested (RFC 6749 §5.1) and the seam unions the requested set. Resolved
|
|
106
|
-
* at runtime by the persistence seam, never declared by a provider.
|
|
107
|
-
*
|
|
108
|
-
* @see https://www.rfc-editor.org/rfc/rfc6749#section-5.1
|
|
109
|
-
*/
|
|
110
|
-
export type GrantAuthority = "full-grant" | "projection" | "absent-echo";
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* The authority a provider may declare for its own echoed scope. `"absent-echo"`
|
|
114
|
-
* is excluded because it is a runtime condition (an omitted echo), not a
|
|
115
|
-
* provider trait.
|
|
116
|
-
*/
|
|
117
|
-
export type ProviderGrantAuthority = Exclude<GrantAuthority, "absent-echo">;
|
|
118
|
-
|
|
119
|
-
export interface UpstreamProvider<
|
|
92
|
+
export interface OAuthProvider<
|
|
120
93
|
T extends Record<string, any> = Record<string, any>,
|
|
121
94
|
O extends Record<string, any> = Partial<ProviderOptions>,
|
|
122
95
|
> {
|
|
123
96
|
id: LiteralString;
|
|
124
97
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
|
|
128
|
-
callbackPath: string;
|
|
129
|
-
/**
|
|
130
|
-
* How the persistence seam treats this provider's echoed token-response
|
|
131
|
-
* `scope`. Declare `"full-grant"` only when the echo is the user's complete
|
|
132
|
-
* current grant (e.g. Google with `include_granted_scopes`); otherwise the
|
|
133
|
-
* echo is unioned onto the stored grant.
|
|
98
|
+
* Optional path under the resolved per-request `baseURL` where this
|
|
99
|
+
* provider's OAuth callback handler is mounted. Providers that use the
|
|
100
|
+
* shared `/callback/<id>` route can omit this.
|
|
134
101
|
*
|
|
135
|
-
*
|
|
102
|
+
* Custom paths must start with `/`.
|
|
103
|
+
*
|
|
104
|
+
* Endpoints compose `redirectURI = ctx.context.baseURL + callbackPath` per
|
|
105
|
+
* request, so the provider must not hardcode an origin or `baseURL` here.
|
|
136
106
|
*/
|
|
137
|
-
|
|
107
|
+
callbackPath?: string | undefined;
|
|
138
108
|
createAuthorizationURL: (data: {
|
|
139
109
|
state: string;
|
|
140
110
|
codeVerifier: string;
|
|
@@ -142,6 +112,12 @@ export interface UpstreamProvider<
|
|
|
142
112
|
redirectURI: string;
|
|
143
113
|
display?: string | undefined;
|
|
144
114
|
loginHint?: string | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* OIDC nonce generated by the redirect initiator and persisted in OAuth
|
|
117
|
+
* state. Providers that set `requiresIdTokenNonce` must forward this to
|
|
118
|
+
* the authorization URL as the `nonce` parameter.
|
|
119
|
+
*/
|
|
120
|
+
idTokenNonce?: string | undefined;
|
|
145
121
|
/**
|
|
146
122
|
* Extra query parameters to append to the authorization URL.
|
|
147
123
|
* Providers forward these to the shared `createAuthorizationURL` helper,
|
|
@@ -149,7 +125,7 @@ export interface UpstreamProvider<
|
|
|
149
125
|
* before applying them.
|
|
150
126
|
*/
|
|
151
127
|
additionalParams?: Record<string, string> | undefined;
|
|
152
|
-
}) => Awaitable<
|
|
128
|
+
}) => Awaitable<URL>;
|
|
153
129
|
name: string;
|
|
154
130
|
validateAuthorizationCode: (data: {
|
|
155
131
|
code: string;
|
|
@@ -159,6 +135,12 @@ export interface UpstreamProvider<
|
|
|
159
135
|
}) => Promise<OAuth2Tokens | null>;
|
|
160
136
|
getUserInfo: (
|
|
161
137
|
token: OAuth2Tokens & {
|
|
138
|
+
/**
|
|
139
|
+
* OIDC nonce recovered from OAuth state. Providers that required an
|
|
140
|
+
* ID-token nonce must pass this to `verifyProviderIdToken` before
|
|
141
|
+
* trusting ID-token claims.
|
|
142
|
+
*/
|
|
143
|
+
expectedIdTokenNonce?: string | undefined;
|
|
162
144
|
/**
|
|
163
145
|
* The user object from the provider
|
|
164
146
|
* This is only available for some providers like Apple
|
|
@@ -178,11 +160,19 @@ export interface UpstreamProvider<
|
|
|
178
160
|
data: T;
|
|
179
161
|
} | null>;
|
|
180
162
|
/**
|
|
181
|
-
* Custom function to refresh a token
|
|
163
|
+
* Custom function to refresh a token.
|
|
164
|
+
*
|
|
165
|
+
* Receives request metadata from the endpoint that triggered the refresh.
|
|
166
|
+
* Providers that don't need request-scoped data can ignore the second
|
|
167
|
+
* argument.
|
|
182
168
|
*/
|
|
183
169
|
refreshAccessToken?:
|
|
184
|
-
| ((
|
|
170
|
+
| ((
|
|
171
|
+
refreshToken: string,
|
|
172
|
+
ctx?: OAuthRefreshContext,
|
|
173
|
+
) => Promise<OAuth2Tokens>)
|
|
185
174
|
| undefined;
|
|
175
|
+
revokeToken?: ((token: string) => Promise<void>) | undefined;
|
|
186
176
|
/**
|
|
187
177
|
* Declarative id_token verification config consumed by the shared
|
|
188
178
|
* `verifyProviderIdToken` verifier. Providers set this instead of implementing a boolean
|
|
@@ -195,6 +185,13 @@ export interface UpstreamProvider<
|
|
|
195
185
|
* against this value to prevent authorization server mix-up attacks.
|
|
196
186
|
*/
|
|
197
187
|
issuer?: string | undefined;
|
|
188
|
+
/**
|
|
189
|
+
* Require shared OAuth redirect routes to bind ID-token verification to an
|
|
190
|
+
* authorization request nonce. When true, routes generate `idTokenNonce`,
|
|
191
|
+
* pass it to `createAuthorizationURL`, persist it in state, and provide it
|
|
192
|
+
* back to `getUserInfo` as `expectedIdTokenNonce`.
|
|
193
|
+
*/
|
|
194
|
+
requiresIdTokenNonce?: boolean | undefined;
|
|
198
195
|
/**
|
|
199
196
|
* Disable implicit sign up for new users. When set to true for the provider,
|
|
200
197
|
* sign-in need to be called with with requestSignUp as true to create new users.
|
|
@@ -283,10 +280,6 @@ export type ProviderOptions<Profile extends Record<string, any> = any> = {
|
|
|
283
280
|
emailVerified: boolean;
|
|
284
281
|
[key: string]: any;
|
|
285
282
|
};
|
|
286
|
-
// TODO: type as `Profile` once provider getUserInfo paths that return a
|
|
287
|
-
// narrower data shape than their declared profile are reconciled; today
|
|
288
|
-
// `any` is load-bearing for those (e.g. facebook) and tightening it ripples
|
|
289
|
-
// across ~10 providers, out of scope for the grant refactor.
|
|
290
283
|
data: any;
|
|
291
284
|
} | null>)
|
|
292
285
|
| undefined;
|