@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,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
|
|
|
@@ -101,24 +102,32 @@ export interface KakaoOptions extends ProviderOptions<KakaoProfile> {
|
|
|
101
102
|
clientId: string;
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
const KAKAO_DEFAULT_SCOPES = [
|
|
106
|
+
"account_email",
|
|
107
|
+
"profile_image",
|
|
108
|
+
"profile_nickname",
|
|
109
|
+
];
|
|
110
|
+
|
|
104
111
|
export const kakao = (options: KakaoOptions) => {
|
|
105
112
|
const tokenEndpoint = "https://kauth.kakao.com/oauth/token";
|
|
106
113
|
return {
|
|
107
114
|
id: "kakao",
|
|
108
115
|
name: "Kakao",
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
callbackPath: "/callback/kakao",
|
|
117
|
+
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
118
|
+
const requestedScopes = resolveRequestedScopes(
|
|
119
|
+
options,
|
|
120
|
+
KAKAO_DEFAULT_SCOPES,
|
|
121
|
+
scopes,
|
|
122
|
+
);
|
|
115
123
|
return createAuthorizationURL({
|
|
116
124
|
id: "kakao",
|
|
117
125
|
options,
|
|
118
126
|
authorizationEndpoint: "https://kauth.kakao.com/oauth/authorize",
|
|
119
|
-
scopes:
|
|
127
|
+
scopes: requestedScopes,
|
|
120
128
|
state,
|
|
121
129
|
redirectURI,
|
|
130
|
+
additionalParams,
|
|
122
131
|
});
|
|
123
132
|
},
|
|
124
133
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
@@ -175,5 +184,5 @@ export const kakao = (options: KakaoOptions) => {
|
|
|
175
184
|
};
|
|
176
185
|
},
|
|
177
186
|
options,
|
|
178
|
-
} satisfies
|
|
187
|
+
} satisfies UpstreamProvider<KakaoProfile>;
|
|
179
188
|
};
|
|
@@ -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
|
|
|
@@ -29,23 +30,34 @@ export interface KickOptions extends ProviderOptions<KickProfile> {
|
|
|
29
30
|
clientId: string;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
const KICK_DEFAULT_SCOPES = ["user:read"];
|
|
34
|
+
|
|
32
35
|
export const kick = (options: KickOptions) => {
|
|
33
36
|
return {
|
|
34
37
|
id: "kick",
|
|
35
38
|
name: "Kick",
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
callbackPath: "/callback/kick",
|
|
40
|
+
createAuthorizationURL({
|
|
41
|
+
state,
|
|
42
|
+
scopes,
|
|
43
|
+
redirectURI,
|
|
44
|
+
codeVerifier,
|
|
45
|
+
additionalParams,
|
|
46
|
+
}) {
|
|
47
|
+
const requestedScopes = resolveRequestedScopes(
|
|
48
|
+
options,
|
|
49
|
+
KICK_DEFAULT_SCOPES,
|
|
50
|
+
scopes,
|
|
51
|
+
);
|
|
41
52
|
return createAuthorizationURL({
|
|
42
53
|
id: "kick",
|
|
43
54
|
redirectURI,
|
|
44
55
|
options,
|
|
45
56
|
authorizationEndpoint: "https://id.kick.com/oauth/authorize",
|
|
46
|
-
scopes:
|
|
57
|
+
scopes: requestedScopes,
|
|
47
58
|
codeVerifier,
|
|
48
59
|
state,
|
|
60
|
+
additionalParams,
|
|
49
61
|
});
|
|
50
62
|
},
|
|
51
63
|
async validateAuthorizationCode({ code, redirectURI, codeVerifier }) {
|
|
@@ -105,5 +117,5 @@ export const kick = (options: KickOptions) => {
|
|
|
105
117
|
};
|
|
106
118
|
},
|
|
107
119
|
options,
|
|
108
|
-
} satisfies
|
|
120
|
+
} satisfies UpstreamProvider<KickProfile>;
|
|
109
121
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
2
|
import { decodeJwt } from "jose";
|
|
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
|
|
|
@@ -32,6 +33,8 @@ export interface LineOptions
|
|
|
32
33
|
clientId: string;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
const LINE_DEFAULT_SCOPES = ["openid", "profile", "email"];
|
|
37
|
+
|
|
35
38
|
/**
|
|
36
39
|
* LINE Login v2.1
|
|
37
40
|
* - Authorization endpoint: https://access.line.me/oauth2/v2.1/authorize
|
|
@@ -50,27 +53,30 @@ export const line = (options: LineOptions) => {
|
|
|
50
53
|
return {
|
|
51
54
|
id: "line",
|
|
52
55
|
name: "LINE",
|
|
53
|
-
|
|
56
|
+
callbackPath: "/callback/line",
|
|
57
|
+
createAuthorizationURL({
|
|
54
58
|
state,
|
|
55
59
|
scopes,
|
|
56
60
|
codeVerifier,
|
|
57
61
|
redirectURI,
|
|
58
62
|
loginHint,
|
|
63
|
+
additionalParams,
|
|
59
64
|
}) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return
|
|
65
|
+
const requestedScopes = resolveRequestedScopes(
|
|
66
|
+
options,
|
|
67
|
+
LINE_DEFAULT_SCOPES,
|
|
68
|
+
scopes,
|
|
69
|
+
);
|
|
70
|
+
return createAuthorizationURL({
|
|
66
71
|
id: "line",
|
|
67
72
|
options,
|
|
68
73
|
authorizationEndpoint,
|
|
69
|
-
scopes:
|
|
74
|
+
scopes: requestedScopes,
|
|
70
75
|
state,
|
|
71
76
|
codeVerifier,
|
|
72
77
|
redirectURI,
|
|
73
78
|
loginHint,
|
|
79
|
+
additionalParams,
|
|
74
80
|
});
|
|
75
81
|
},
|
|
76
82
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
@@ -94,34 +100,30 @@ export const line = (options: LineOptions) => {
|
|
|
94
100
|
tokenEndpoint,
|
|
95
101
|
});
|
|
96
102
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
method: "POST",
|
|
112
|
-
headers: {
|
|
113
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
103
|
+
idToken: {
|
|
104
|
+
verify: async (token, nonce) => {
|
|
105
|
+
const body = new URLSearchParams();
|
|
106
|
+
body.set("id_token", token);
|
|
107
|
+
body.set("client_id", options.clientId);
|
|
108
|
+
if (nonce) body.set("nonce", nonce);
|
|
109
|
+
const { data, error } = await betterFetch<LineIdTokenPayload>(
|
|
110
|
+
verifyIdTokenEndpoint,
|
|
111
|
+
{
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: {
|
|
114
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
115
|
+
},
|
|
116
|
+
body,
|
|
114
117
|
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return true;
|
|
118
|
+
);
|
|
119
|
+
if (error || !data) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
// aud must match clientId; nonce (if provided) must also match nonce
|
|
123
|
+
if (data.aud !== options.clientId) return false;
|
|
124
|
+
if (data.nonce && data.nonce !== nonce) return false;
|
|
125
|
+
return true;
|
|
126
|
+
},
|
|
125
127
|
},
|
|
126
128
|
async getUserInfo(token) {
|
|
127
129
|
if (options.getUserInfo) {
|
|
@@ -165,5 +167,5 @@ export const line = (options: LineOptions) => {
|
|
|
165
167
|
};
|
|
166
168
|
},
|
|
167
169
|
options,
|
|
168
|
-
} satisfies
|
|
170
|
+
} satisfies UpstreamProvider<LineUserInfo | LineIdTokenPayload, LineOptions>;
|
|
169
171
|
};
|
|
@@ -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
|
|
|
@@ -26,23 +27,35 @@ export interface LinearOptions extends ProviderOptions<LinearUser> {
|
|
|
26
27
|
clientId: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
const LINEAR_DEFAULT_SCOPES = ["read"];
|
|
31
|
+
|
|
29
32
|
export const linear = (options: LinearOptions) => {
|
|
30
33
|
const tokenEndpoint = "https://api.linear.app/oauth/token";
|
|
31
34
|
return {
|
|
32
35
|
id: "linear",
|
|
33
36
|
name: "Linear",
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
callbackPath: "/callback/linear",
|
|
38
|
+
createAuthorizationURL({
|
|
39
|
+
state,
|
|
40
|
+
scopes,
|
|
41
|
+
loginHint,
|
|
42
|
+
redirectURI,
|
|
43
|
+
additionalParams,
|
|
44
|
+
}) {
|
|
45
|
+
const requestedScopes = resolveRequestedScopes(
|
|
46
|
+
options,
|
|
47
|
+
LINEAR_DEFAULT_SCOPES,
|
|
48
|
+
scopes,
|
|
49
|
+
);
|
|
38
50
|
return createAuthorizationURL({
|
|
39
51
|
id: "linear",
|
|
40
52
|
options,
|
|
41
53
|
authorizationEndpoint: "https://linear.app/oauth/authorize",
|
|
42
|
-
scopes:
|
|
54
|
+
scopes: requestedScopes,
|
|
43
55
|
state,
|
|
44
56
|
redirectURI,
|
|
45
57
|
loginHint,
|
|
58
|
+
additionalParams,
|
|
46
59
|
});
|
|
47
60
|
},
|
|
48
61
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
@@ -117,5 +130,5 @@ export const linear = (options: LinearOptions) => {
|
|
|
117
130
|
};
|
|
118
131
|
},
|
|
119
132
|
options,
|
|
120
|
-
} satisfies
|
|
133
|
+
} satisfies UpstreamProvider<LinearUser>;
|
|
121
134
|
};
|
|
@@ -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
|
|
|
@@ -24,6 +25,8 @@ export interface LinkedInOptions extends ProviderOptions<LinkedInProfile> {
|
|
|
24
25
|
clientId: string;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
const LINKEDIN_DEFAULT_SCOPES = ["profile", "email", "openid"];
|
|
29
|
+
|
|
27
30
|
export const linkedin = (options: LinkedInOptions) => {
|
|
28
31
|
const authorizationEndpoint =
|
|
29
32
|
"https://www.linkedin.com/oauth/v2/authorization";
|
|
@@ -32,25 +35,28 @@ export const linkedin = (options: LinkedInOptions) => {
|
|
|
32
35
|
return {
|
|
33
36
|
id: "linkedin",
|
|
34
37
|
name: "Linkedin",
|
|
35
|
-
|
|
38
|
+
callbackPath: "/callback/linkedin",
|
|
39
|
+
createAuthorizationURL: ({
|
|
36
40
|
state,
|
|
37
41
|
scopes,
|
|
38
42
|
redirectURI,
|
|
39
43
|
loginHint,
|
|
44
|
+
additionalParams,
|
|
40
45
|
}) => {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return
|
|
46
|
+
const requestedScopes = resolveRequestedScopes(
|
|
47
|
+
options,
|
|
48
|
+
LINKEDIN_DEFAULT_SCOPES,
|
|
49
|
+
scopes,
|
|
50
|
+
);
|
|
51
|
+
return createAuthorizationURL({
|
|
47
52
|
id: "linkedin",
|
|
48
53
|
options,
|
|
49
54
|
authorizationEndpoint,
|
|
50
|
-
scopes:
|
|
55
|
+
scopes: requestedScopes,
|
|
51
56
|
state,
|
|
52
57
|
loginHint,
|
|
53
58
|
redirectURI,
|
|
59
|
+
additionalParams,
|
|
54
60
|
});
|
|
55
61
|
},
|
|
56
62
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
@@ -106,5 +112,5 @@ export const linkedin = (options: LinkedInOptions) => {
|
|
|
106
112
|
};
|
|
107
113
|
},
|
|
108
114
|
options,
|
|
109
|
-
} satisfies
|
|
115
|
+
} satisfies UpstreamProvider<LinkedInProfile>;
|
|
110
116
|
};
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { base64 } from "@better-auth/utils/base64";
|
|
2
2
|
import { betterFetch } from "@better-fetch/fetch";
|
|
3
|
-
import { decodeJwt,
|
|
3
|
+
import { decodeJwt, importJWK } from "jose";
|
|
4
4
|
import { logger } from "../env";
|
|
5
5
|
import { APIError, BetterAuthError } from "../error";
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
ClientAssertionGetter,
|
|
8
|
+
ProviderOptions,
|
|
9
|
+
TokenEndpointAuth,
|
|
10
|
+
UpstreamProvider,
|
|
11
|
+
} from "../oauth2";
|
|
7
12
|
import {
|
|
8
13
|
createAuthorizationURL,
|
|
9
14
|
getPrimaryClientId,
|
|
10
15
|
refreshAccessToken,
|
|
16
|
+
resolveRequestedScopes,
|
|
11
17
|
validateAuthorizationCode,
|
|
12
18
|
} from "../oauth2";
|
|
13
19
|
|
|
@@ -122,33 +128,60 @@ export interface MicrosoftOptions
|
|
|
122
128
|
* The tenant ID of the Microsoft account
|
|
123
129
|
* @default "common"
|
|
124
130
|
*/
|
|
125
|
-
tenantId?: string
|
|
131
|
+
tenantId?: string;
|
|
126
132
|
/**
|
|
127
133
|
* The authentication authority URL. Use the default "https://login.microsoftonline.com" for standard Entra ID or "https://<tenant-id>.ciamlogin.com" for CIAM scenarios.
|
|
128
134
|
* @default "https://login.microsoftonline.com"
|
|
129
135
|
*/
|
|
130
|
-
authority?: string
|
|
136
|
+
authority?: string;
|
|
137
|
+
/**
|
|
138
|
+
* Function that returns a JWT client assertion for token endpoint authentication.
|
|
139
|
+
*
|
|
140
|
+
* Use this instead of `clientSecret` when your Microsoft Entra ID app is
|
|
141
|
+
* configured for client authentication with assertions (private_key_jwt or
|
|
142
|
+
* workload identity federation).
|
|
143
|
+
*/
|
|
144
|
+
clientAssertion?: ClientAssertionGetter;
|
|
131
145
|
/**
|
|
132
146
|
* The size of the profile photo
|
|
133
147
|
* @default 48
|
|
134
148
|
*/
|
|
135
|
-
profilePhotoSize?:
|
|
136
|
-
| (48 | 64 | 96 | 120 | 240 | 360 | 432 | 504 | 648)
|
|
137
|
-
| undefined;
|
|
149
|
+
profilePhotoSize?: 48 | 64 | 96 | 120 | 240 | 360 | 432 | 504 | 648;
|
|
138
150
|
/**
|
|
139
151
|
* Disable profile photo
|
|
140
152
|
*/
|
|
141
|
-
disableProfilePhoto?: boolean
|
|
153
|
+
disableProfilePhoto?: boolean;
|
|
142
154
|
}
|
|
143
155
|
|
|
156
|
+
const MICROSOFT_ENTRA_ID_DEFAULT_SCOPES = [
|
|
157
|
+
"openid",
|
|
158
|
+
"profile",
|
|
159
|
+
"email",
|
|
160
|
+
"User.Read",
|
|
161
|
+
"offline_access",
|
|
162
|
+
];
|
|
163
|
+
|
|
144
164
|
export const microsoft = (options: MicrosoftOptions) => {
|
|
145
165
|
const tenant = options.tenantId || "common";
|
|
146
166
|
const authority = options.authority || "https://login.microsoftonline.com";
|
|
147
167
|
const authorizationEndpoint = `${authority}/${tenant}/oauth2/v2.0/authorize`;
|
|
148
168
|
const tokenEndpoint = `${authority}/${tenant}/oauth2/v2.0/token`;
|
|
169
|
+
if (options.clientSecret && options.clientAssertion) {
|
|
170
|
+
throw new BetterAuthError(
|
|
171
|
+
"Microsoft Entra ID clientAssertion cannot be combined with clientSecret",
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const tokenEndpointAuth: TokenEndpointAuth | undefined =
|
|
175
|
+
options.clientAssertion
|
|
176
|
+
? {
|
|
177
|
+
method: "private_key_jwt",
|
|
178
|
+
getClientAssertion: options.clientAssertion,
|
|
179
|
+
}
|
|
180
|
+
: undefined;
|
|
149
181
|
return {
|
|
150
182
|
id: "microsoft",
|
|
151
183
|
name: "Microsoft EntraID",
|
|
184
|
+
callbackPath: "/callback/microsoft",
|
|
152
185
|
createAuthorizationURL(data) {
|
|
153
186
|
// Microsoft Entra supports public clients (SPA / native apps with
|
|
154
187
|
// PKCE only), so clientSecret is intentionally not required here.
|
|
@@ -159,21 +192,22 @@ export const microsoft = (options: MicrosoftOptions) => {
|
|
|
159
192
|
);
|
|
160
193
|
throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
|
|
161
194
|
}
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
195
|
+
const requestedScopes = resolveRequestedScopes(
|
|
196
|
+
options,
|
|
197
|
+
MICROSOFT_ENTRA_ID_DEFAULT_SCOPES,
|
|
198
|
+
data.scopes,
|
|
199
|
+
);
|
|
167
200
|
return createAuthorizationURL({
|
|
168
201
|
id: "microsoft",
|
|
169
202
|
options,
|
|
170
203
|
authorizationEndpoint,
|
|
171
204
|
state: data.state,
|
|
172
205
|
codeVerifier: data.codeVerifier,
|
|
173
|
-
scopes,
|
|
206
|
+
scopes: requestedScopes,
|
|
174
207
|
redirectURI: data.redirectURI,
|
|
175
208
|
prompt: options.prompt,
|
|
176
209
|
loginHint: data.loginHint,
|
|
210
|
+
additionalParams: data.additionalParams,
|
|
177
211
|
});
|
|
178
212
|
},
|
|
179
213
|
validateAuthorizationCode({ code, codeVerifier, redirectURI }) {
|
|
@@ -183,57 +217,24 @@ export const microsoft = (options: MicrosoftOptions) => {
|
|
|
183
217
|
redirectURI,
|
|
184
218
|
options,
|
|
185
219
|
tokenEndpoint,
|
|
220
|
+
tokenEndpointAuth,
|
|
186
221
|
});
|
|
187
222
|
},
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
audience: string | string[];
|
|
204
|
-
maxTokenAge: string;
|
|
205
|
-
issuer?: string;
|
|
206
|
-
} = {
|
|
207
|
-
algorithms: [jwtAlg],
|
|
208
|
-
audience: options.clientId,
|
|
209
|
-
maxTokenAge: "1h",
|
|
210
|
-
};
|
|
211
|
-
/**
|
|
212
|
-
* Issuer varies per user's tenant for multi-tenant endpoints, so only validate for specific tenants.
|
|
213
|
-
* @see https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols#endpoints
|
|
214
|
-
*/
|
|
215
|
-
if (
|
|
216
|
-
tenant !== "common" &&
|
|
217
|
-
tenant !== "organizations" &&
|
|
218
|
-
tenant !== "consumers"
|
|
219
|
-
) {
|
|
220
|
-
verifyOptions.issuer = `${authority}/${tenant}/v2.0`;
|
|
221
|
-
}
|
|
222
|
-
const { payload: jwtClaims } = await jwtVerify(
|
|
223
|
-
token,
|
|
224
|
-
publicKey,
|
|
225
|
-
verifyOptions,
|
|
226
|
-
);
|
|
227
|
-
|
|
228
|
-
if (nonce && jwtClaims.nonce !== nonce) {
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return true;
|
|
233
|
-
} catch (error) {
|
|
234
|
-
logger.error("Failed to verify ID token:", error);
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
223
|
+
idToken: {
|
|
224
|
+
jwks: (header) => getMicrosoftPublicKey(header.kid!, tenant, authority),
|
|
225
|
+
audience: options.clientId,
|
|
226
|
+
maxTokenAge: "1h",
|
|
227
|
+
/**
|
|
228
|
+
* Issuer varies per tenant for multi-tenant endpoints, so only validate it for
|
|
229
|
+
* specific tenants.
|
|
230
|
+
* @see https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols#endpoints
|
|
231
|
+
*/
|
|
232
|
+
issuer:
|
|
233
|
+
tenant !== "common" &&
|
|
234
|
+
tenant !== "organizations" &&
|
|
235
|
+
tenant !== "consumers"
|
|
236
|
+
? `${authority}/${tenant}/v2.0`
|
|
237
|
+
: undefined,
|
|
237
238
|
},
|
|
238
239
|
async getUserInfo(token) {
|
|
239
240
|
if (options.getUserInfo) {
|
|
@@ -313,10 +314,11 @@ export const microsoft = (options: MicrosoftOptions) => {
|
|
|
313
314
|
scope: scopes.join(" "), // Include the scopes in request to microsoft
|
|
314
315
|
},
|
|
315
316
|
tokenEndpoint,
|
|
317
|
+
tokenEndpointAuth,
|
|
316
318
|
});
|
|
317
319
|
},
|
|
318
320
|
options,
|
|
319
|
-
} satisfies
|
|
321
|
+
} satisfies UpstreamProvider;
|
|
320
322
|
};
|
|
321
323
|
|
|
322
324
|
export const getMicrosoftPublicKey = async (
|
|
@@ -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
|
|
|
@@ -39,22 +40,28 @@ export interface NaverOptions extends ProviderOptions<NaverProfile> {
|
|
|
39
40
|
clientId: string;
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
const NAVER_DEFAULT_SCOPES = ["profile", "email"];
|
|
44
|
+
|
|
42
45
|
export const naver = (options: NaverOptions) => {
|
|
43
46
|
const tokenEndpoint = "https://nid.naver.com/oauth2.0/token";
|
|
44
47
|
return {
|
|
45
48
|
id: "naver",
|
|
46
49
|
name: "Naver",
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
callbackPath: "/callback/naver",
|
|
51
|
+
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
52
|
+
const requestedScopes = resolveRequestedScopes(
|
|
53
|
+
options,
|
|
54
|
+
NAVER_DEFAULT_SCOPES,
|
|
55
|
+
scopes,
|
|
56
|
+
);
|
|
51
57
|
return createAuthorizationURL({
|
|
52
58
|
id: "naver",
|
|
53
59
|
options,
|
|
54
60
|
authorizationEndpoint: "https://nid.naver.com/oauth2.0/authorize",
|
|
55
|
-
scopes:
|
|
61
|
+
scopes: requestedScopes,
|
|
56
62
|
state,
|
|
57
63
|
redirectURI,
|
|
64
|
+
additionalParams,
|
|
58
65
|
});
|
|
59
66
|
},
|
|
60
67
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
@@ -109,5 +116,5 @@ export const naver = (options: NaverOptions) => {
|
|
|
109
116
|
};
|
|
110
117
|
},
|
|
111
118
|
options,
|
|
112
|
-
} satisfies
|
|
119
|
+
} satisfies UpstreamProvider<NaverProfile>;
|
|
113
120
|
};
|
|
@@ -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
|
|
|
@@ -23,24 +24,36 @@ export interface NotionOptions extends ProviderOptions<NotionProfile> {
|
|
|
23
24
|
clientId: string;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
const NOTION_DEFAULT_SCOPES: string[] = [];
|
|
28
|
+
|
|
26
29
|
export const notion = (options: NotionOptions) => {
|
|
27
30
|
const tokenEndpoint = "https://api.notion.com/v1/oauth/token";
|
|
28
31
|
return {
|
|
29
32
|
id: "notion",
|
|
30
33
|
name: "Notion",
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
callbackPath: "/callback/notion",
|
|
35
|
+
createAuthorizationURL({
|
|
36
|
+
state,
|
|
37
|
+
scopes,
|
|
38
|
+
loginHint,
|
|
39
|
+
redirectURI,
|
|
40
|
+
additionalParams,
|
|
41
|
+
}) {
|
|
42
|
+
const requestedScopes = resolveRequestedScopes(
|
|
43
|
+
options,
|
|
44
|
+
NOTION_DEFAULT_SCOPES,
|
|
45
|
+
scopes,
|
|
46
|
+
);
|
|
35
47
|
return createAuthorizationURL({
|
|
36
48
|
id: "notion",
|
|
37
49
|
options,
|
|
38
50
|
authorizationEndpoint: "https://api.notion.com/v1/oauth/authorize",
|
|
39
|
-
scopes:
|
|
51
|
+
scopes: requestedScopes,
|
|
40
52
|
state,
|
|
41
53
|
redirectURI,
|
|
42
54
|
loginHint,
|
|
43
55
|
additionalParams: {
|
|
56
|
+
...(additionalParams ?? {}),
|
|
44
57
|
owner: "user",
|
|
45
58
|
},
|
|
46
59
|
});
|
|
@@ -104,5 +117,5 @@ export const notion = (options: NotionOptions) => {
|
|
|
104
117
|
};
|
|
105
118
|
},
|
|
106
119
|
options,
|
|
107
|
-
} satisfies
|
|
120
|
+
} satisfies UpstreamProvider<NotionProfile>;
|
|
108
121
|
};
|