@better-auth/core 1.7.1 → 1.7.3
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 -0
- package/dist/context/endpoint-context.d.mts +19 -5
- package/dist/context/endpoint-context.mjs +35 -16
- package/dist/context/global.mjs +5 -2
- package/dist/context/index.d.mts +2 -2
- package/dist/context/index.mjs +2 -2
- package/dist/context/transaction.mjs +3 -0
- package/dist/db/adapter/atomic-fallback.mjs +134 -0
- package/dist/db/adapter/factory.mjs +22 -4
- package/dist/db/adapter/index.d.mts +15 -11
- package/dist/db/get-tables.mjs +1 -9
- package/dist/db/index.d.mts +2 -2
- package/dist/db/index.mjs +2 -2
- package/dist/db/internal.d.mts +3 -1
- package/dist/db/internal.mjs +3 -1
- package/dist/db/schema/account.d.mts +2 -13
- package/dist/db/schema/account.mjs +1 -19
- package/dist/db/schema-check.d.mts +48 -0
- package/dist/db/schema-check.mjs +80 -0
- package/dist/db/schema-diff.d.mts +104 -0
- package/dist/db/schema-diff.mjs +154 -0
- package/dist/env/logger.mjs +16 -1
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/index.d.mts +2 -2
- package/dist/oauth2/oauth-provider.d.mts +0 -10
- package/dist/oauth2/token-endpoint-auth.d.mts +26 -2
- package/dist/oauth2/token-endpoint-auth.mjs +11 -0
- package/dist/social-providers/apple.d.mts +0 -1
- package/dist/social-providers/apple.mjs +0 -1
- package/dist/social-providers/cloudflare.d.mts +132 -0
- package/dist/social-providers/cloudflare.mjs +85 -0
- package/dist/social-providers/cognito.d.mts +0 -1
- package/dist/social-providers/cognito.mjs +0 -1
- package/dist/social-providers/facebook.d.mts +0 -1
- package/dist/social-providers/facebook.mjs +0 -1
- package/dist/social-providers/google.d.mts +0 -1
- package/dist/social-providers/google.mjs +0 -1
- package/dist/social-providers/index.d.mts +53 -21
- package/dist/social-providers/index.mjs +3 -1
- package/dist/social-providers/line.d.mts +0 -1
- package/dist/social-providers/line.mjs +0 -1
- package/dist/social-providers/microsoft-entra-id.d.mts +0 -3
- package/dist/social-providers/microsoft-entra-id.mjs +0 -1
- package/dist/social-providers/paybin.d.mts +0 -1
- package/dist/social-providers/paybin.mjs +0 -1
- package/dist/social-providers/paypal.d.mts +3 -11
- package/dist/social-providers/paypal.mjs +20 -47
- package/dist/social-providers/reddit.mjs +22 -23
- package/dist/social-providers/roblox.mjs +5 -1
- package/dist/social-providers/tiktok.d.mts +1 -0
- package/dist/social-providers/tiktok.mjs +19 -10
- package/dist/social-providers/twitter.mjs +5 -1
- package/dist/social-providers/wechat.mjs +6 -1
- package/dist/types/context.d.mts +11 -0
- package/dist/types/init-options.d.mts +11 -0
- package/dist/utils/ip.mjs +11 -9
- package/dist/utils/url.d.mts +10 -1
- package/dist/utils/url.mjs +21 -1
- package/package.json +3 -3
- package/src/context/endpoint-context.ts +46 -21
- package/src/context/global.ts +7 -0
- package/src/context/index.ts +2 -0
- package/src/context/transaction.ts +5 -0
- package/src/db/adapter/atomic-fallback.ts +237 -0
- package/src/db/adapter/factory.ts +33 -17
- package/src/db/adapter/index.ts +15 -11
- package/src/db/get-tables.ts +1 -14
- package/src/db/index.ts +0 -2
- package/src/db/internal.ts +19 -0
- package/src/db/schema/account.ts +3 -22
- package/src/db/schema/user.ts +1 -1
- package/src/db/schema-check.ts +107 -0
- package/src/db/schema-diff.ts +270 -0
- package/src/env/logger.ts +22 -1
- package/src/oauth2/index.ts +2 -0
- package/src/oauth2/oauth-provider.ts +0 -10
- package/src/oauth2/token-endpoint-auth.ts +39 -6
- package/src/social-providers/apple.ts +0 -1
- package/src/social-providers/cloudflare.ts +221 -0
- package/src/social-providers/cognito.ts +0 -1
- package/src/social-providers/facebook.ts +0 -1
- package/src/social-providers/google.ts +0 -1
- package/src/social-providers/index.ts +3 -0
- package/src/social-providers/line.ts +0 -1
- package/src/social-providers/microsoft-entra-id.ts +0 -1
- package/src/social-providers/paybin.ts +0 -1
- package/src/social-providers/paypal.ts +30 -71
- package/src/social-providers/reddit.ts +34 -37
- package/src/social-providers/roblox.ts +5 -3
- package/src/social-providers/tiktok.ts +25 -14
- package/src/social-providers/twitter.ts +8 -2
- package/src/social-providers/wechat.ts +6 -6
- package/src/types/context.ts +11 -0
- package/src/types/init-options.ts +11 -0
- package/src/utils/ip.ts +13 -9
- package/src/utils/url.ts +43 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { logger } from "../env/logger.mjs";
|
|
2
|
+
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
3
|
+
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
4
|
+
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
5
|
+
import { betterFetch } from "@better-fetch/fetch";
|
|
6
|
+
//#region src/social-providers/cloudflare.ts
|
|
7
|
+
const authorizationEndpoint = "https://dash.cloudflare.com/oauth2/auth";
|
|
8
|
+
const tokenEndpoint = "https://dash.cloudflare.com/oauth2/token";
|
|
9
|
+
/**
|
|
10
|
+
* Cloudflare's OIDC `userinfo` endpoint only returns the `sub` claim, so it
|
|
11
|
+
* cannot be used to build a user. The user's profile (email, name, ...) is
|
|
12
|
+
* read from the Cloudflare API `/user` endpoint instead, which the access
|
|
13
|
+
* token can call when the `user-details.read` scope is granted.
|
|
14
|
+
*/
|
|
15
|
+
const userEndpoint = "https://api.cloudflare.com/client/v4/user";
|
|
16
|
+
const getTokenEndpointAuth = (options) => {
|
|
17
|
+
const defaultMethod = options.clientSecret ? "client_secret_basic" : "none";
|
|
18
|
+
return { method: options.tokenEndpointAuthMethod ?? defaultMethod };
|
|
19
|
+
};
|
|
20
|
+
const cloudflare = (options) => {
|
|
21
|
+
return {
|
|
22
|
+
id: "cloudflare",
|
|
23
|
+
name: "Cloudflare",
|
|
24
|
+
accountSubject: ({ profile }) => profile.id,
|
|
25
|
+
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
26
|
+
const _scopes = options.disableDefaultScope ? [] : ["user-details.read"];
|
|
27
|
+
if (options.scope?.length) _scopes.push(...options.scope);
|
|
28
|
+
if (scopes?.length) _scopes.push(...scopes);
|
|
29
|
+
return createAuthorizationURL({
|
|
30
|
+
id: "cloudflare",
|
|
31
|
+
options,
|
|
32
|
+
authorizationEndpoint,
|
|
33
|
+
scopes: _scopes.length ? [...new Set(_scopes)] : void 0,
|
|
34
|
+
state,
|
|
35
|
+
codeVerifier,
|
|
36
|
+
redirectURI
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
40
|
+
return validateAuthorizationCode({
|
|
41
|
+
code,
|
|
42
|
+
codeVerifier,
|
|
43
|
+
redirectURI,
|
|
44
|
+
options,
|
|
45
|
+
tokenEndpoint,
|
|
46
|
+
tokenEndpointAuth: getTokenEndpointAuth(options)
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
|
|
50
|
+
return refreshAccessToken({
|
|
51
|
+
refreshToken,
|
|
52
|
+
options: {
|
|
53
|
+
clientId: options.clientId,
|
|
54
|
+
clientKey: options.clientKey,
|
|
55
|
+
clientSecret: options.clientSecret
|
|
56
|
+
},
|
|
57
|
+
tokenEndpoint,
|
|
58
|
+
tokenEndpointAuth: getTokenEndpointAuth(options)
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
async getUserInfo(token) {
|
|
62
|
+
if (options.getUserInfo) return options.getUserInfo(token);
|
|
63
|
+
const { data, error } = await betterFetch(userEndpoint, { headers: { authorization: `Bearer ${token.accessToken}` } });
|
|
64
|
+
if (error || !data?.success || !data.result) {
|
|
65
|
+
logger.error("Failed to fetch user info from Cloudflare:", error ?? data?.errors);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const profile = data.result;
|
|
69
|
+
const name = [profile.first_name, profile.last_name].filter(Boolean).join(" ") || profile.email;
|
|
70
|
+
const userMap = await options.mapProfileToUser?.(profile);
|
|
71
|
+
return {
|
|
72
|
+
user: {
|
|
73
|
+
name,
|
|
74
|
+
email: profile.email,
|
|
75
|
+
emailVerified: false,
|
|
76
|
+
...userMap
|
|
77
|
+
},
|
|
78
|
+
data: profile
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
options
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
export { cloudflare };
|
|
@@ -20,7 +20,6 @@ const cognito = (options) => {
|
|
|
20
20
|
id: "cognito",
|
|
21
21
|
name: "Cognito",
|
|
22
22
|
accountSubject: ({ profile }) => profile.sub,
|
|
23
|
-
accountIssuer: `https://cognito-idp.${options.region}.amazonaws.com/${options.userPoolId}`,
|
|
24
23
|
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
|
|
25
24
|
if (!getPrimaryClientId(options.clientId)) {
|
|
26
25
|
logger.error("ClientId is required for Amazon Cognito. Make sure to provide them in the options.");
|
|
@@ -40,7 +40,6 @@ const facebook = (options) => {
|
|
|
40
40
|
id: "facebook",
|
|
41
41
|
name: "Facebook",
|
|
42
42
|
accountSubject: ({ profile }) => "sub" in profile ? profile.sub : profile.id,
|
|
43
|
-
accountIssuer: "https://www.facebook.com",
|
|
44
43
|
async createAuthorizationURL({ state, scopes, redirectURI, loginHint, additionalParams }) {
|
|
45
44
|
if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
|
|
46
45
|
logger.error("Client ID and client secret are required for Facebook. Make sure to provide them in the options.");
|
|
@@ -54,7 +54,6 @@ const google = (options) => {
|
|
|
54
54
|
id: "google",
|
|
55
55
|
name: "Google",
|
|
56
56
|
accountSubject: ({ profile }) => profile.sub,
|
|
57
|
-
accountIssuer: "https://accounts.google.com",
|
|
58
57
|
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, display, additionalParams }) {
|
|
59
58
|
if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
|
|
60
59
|
logger.error("Client Id and Client Secret is required for Google. Make sure to provide them in the options.");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AppleNonConformUser, AppleOptions, AppleProfile, apple, getApplePublicKey } from "./apple.mjs";
|
|
2
2
|
import { AtlassianOptions, AtlassianProfile, atlassian } from "./atlassian.mjs";
|
|
3
|
+
import { CloudflareOptions, CloudflareProfile, cloudflare } from "./cloudflare.mjs";
|
|
3
4
|
import { CognitoOptions, CognitoProfile, cognito, getCognitoPublicKey } from "./cognito.mjs";
|
|
4
5
|
import { DiscordOptions, DiscordProfile, discord } from "./discord.mjs";
|
|
5
6
|
import { FacebookGraphProfile, FacebookLimitedLoginProfile, FacebookOptions, FacebookProfile, facebook } from "./facebook.mjs";
|
|
@@ -46,7 +47,6 @@ declare const socialProviders: {
|
|
|
46
47
|
accountSubject: ({
|
|
47
48
|
profile
|
|
48
49
|
}: OAuthAccountKeyContext<AppleProfile>) => string;
|
|
49
|
-
accountIssuer: string;
|
|
50
50
|
createAuthorizationURL({
|
|
51
51
|
state,
|
|
52
52
|
scopes,
|
|
@@ -158,13 +158,59 @@ declare const socialProviders: {
|
|
|
158
158
|
} | null>;
|
|
159
159
|
options: AtlassianOptions;
|
|
160
160
|
};
|
|
161
|
+
cloudflare: (options: CloudflareOptions) => {
|
|
162
|
+
id: "cloudflare";
|
|
163
|
+
name: string;
|
|
164
|
+
accountSubject: ({
|
|
165
|
+
profile
|
|
166
|
+
}: OAuthAccountKeyContext<CloudflareProfile>) => string;
|
|
167
|
+
createAuthorizationURL({
|
|
168
|
+
state,
|
|
169
|
+
scopes,
|
|
170
|
+
codeVerifier,
|
|
171
|
+
redirectURI
|
|
172
|
+
}: {
|
|
173
|
+
state: string;
|
|
174
|
+
codeVerifier: string;
|
|
175
|
+
scopes?: string[] | undefined;
|
|
176
|
+
redirectURI: string;
|
|
177
|
+
display?: string | undefined;
|
|
178
|
+
loginHint?: string | undefined;
|
|
179
|
+
idTokenNonce?: string | undefined;
|
|
180
|
+
additionalParams?: Record<string, string> | undefined;
|
|
181
|
+
}): Promise<URL>;
|
|
182
|
+
validateAuthorizationCode: ({
|
|
183
|
+
code,
|
|
184
|
+
codeVerifier,
|
|
185
|
+
redirectURI
|
|
186
|
+
}: {
|
|
187
|
+
code: string;
|
|
188
|
+
redirectURI: string;
|
|
189
|
+
codeVerifier?: string | undefined;
|
|
190
|
+
deviceId?: string | undefined;
|
|
191
|
+
}) => Promise<OAuth2Tokens>;
|
|
192
|
+
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
193
|
+
getUserInfo(token: OAuth2Tokens & {
|
|
194
|
+
expectedIdTokenNonce?: string | undefined;
|
|
195
|
+
user?: {
|
|
196
|
+
name?: {
|
|
197
|
+
firstName?: string;
|
|
198
|
+
lastName?: string;
|
|
199
|
+
};
|
|
200
|
+
email?: string;
|
|
201
|
+
} | undefined;
|
|
202
|
+
}): Promise<{
|
|
203
|
+
user: OAuth2UserInfo & Record<string, unknown>;
|
|
204
|
+
data: CloudflareProfile;
|
|
205
|
+
} | null>;
|
|
206
|
+
options: CloudflareOptions;
|
|
207
|
+
};
|
|
161
208
|
cognito: (options: CognitoOptions) => {
|
|
162
209
|
id: "cognito";
|
|
163
210
|
name: string;
|
|
164
211
|
accountSubject: ({
|
|
165
212
|
profile
|
|
166
213
|
}: OAuthAccountKeyContext<CognitoProfile>) => string;
|
|
167
|
-
accountIssuer: string;
|
|
168
214
|
createAuthorizationURL({
|
|
169
215
|
state,
|
|
170
216
|
scopes,
|
|
@@ -292,7 +338,6 @@ declare const socialProviders: {
|
|
|
292
338
|
accountSubject: ({
|
|
293
339
|
profile
|
|
294
340
|
}: OAuthAccountKeyContext<FacebookProfile>) => string;
|
|
295
|
-
accountIssuer: string;
|
|
296
341
|
createAuthorizationURL({
|
|
297
342
|
state,
|
|
298
343
|
scopes,
|
|
@@ -451,9 +496,6 @@ declare const socialProviders: {
|
|
|
451
496
|
accountSubject: ({
|
|
452
497
|
profile
|
|
453
498
|
}: OAuthAccountKeyContext<MicrosoftEntraIDProfile>) => string;
|
|
454
|
-
accountIssuer: ({
|
|
455
|
-
profile
|
|
456
|
-
}: OAuthAccountKeyContext<MicrosoftEntraIDProfile>) => string;
|
|
457
499
|
createAuthorizationURL(data: {
|
|
458
500
|
state: string;
|
|
459
501
|
codeVerifier: string;
|
|
@@ -503,7 +545,6 @@ declare const socialProviders: {
|
|
|
503
545
|
accountSubject: ({
|
|
504
546
|
profile
|
|
505
547
|
}: OAuthAccountKeyContext<GoogleProfile>) => string;
|
|
506
|
-
accountIssuer: string;
|
|
507
548
|
createAuthorizationURL({
|
|
508
549
|
state,
|
|
509
550
|
scopes,
|
|
@@ -1047,6 +1088,7 @@ declare const socialProviders: {
|
|
|
1047
1088
|
}): URL;
|
|
1048
1089
|
validateAuthorizationCode: ({
|
|
1049
1090
|
code,
|
|
1091
|
+
codeVerifier,
|
|
1050
1092
|
redirectURI
|
|
1051
1093
|
}: {
|
|
1052
1094
|
code: string;
|
|
@@ -1468,7 +1510,6 @@ declare const socialProviders: {
|
|
|
1468
1510
|
accountSubject: ({
|
|
1469
1511
|
profile
|
|
1470
1512
|
}: OAuthAccountKeyContext<LineIdTokenPayload | LineUserInfo>) => string;
|
|
1471
|
-
accountIssuer: string;
|
|
1472
1513
|
createAuthorizationURL({
|
|
1473
1514
|
state,
|
|
1474
1515
|
scopes,
|
|
@@ -1521,7 +1562,6 @@ declare const socialProviders: {
|
|
|
1521
1562
|
accountSubject: ({
|
|
1522
1563
|
profile
|
|
1523
1564
|
}: OAuthAccountKeyContext<PaybinProfile>) => string;
|
|
1524
|
-
accountIssuer: string;
|
|
1525
1565
|
createAuthorizationURL({
|
|
1526
1566
|
state,
|
|
1527
1567
|
scopes,
|
|
@@ -1588,23 +1628,15 @@ declare const socialProviders: {
|
|
|
1588
1628
|
}): Promise<URL>;
|
|
1589
1629
|
validateAuthorizationCode: ({
|
|
1590
1630
|
code,
|
|
1631
|
+
codeVerifier,
|
|
1591
1632
|
redirectURI
|
|
1592
1633
|
}: {
|
|
1593
1634
|
code: string;
|
|
1594
1635
|
redirectURI: string;
|
|
1595
1636
|
codeVerifier?: string | undefined;
|
|
1596
1637
|
deviceId?: string | undefined;
|
|
1597
|
-
}) => Promise<
|
|
1598
|
-
|
|
1599
|
-
refreshToken: string | undefined;
|
|
1600
|
-
accessTokenExpiresAt: Date | undefined;
|
|
1601
|
-
idToken: string | undefined;
|
|
1602
|
-
}>;
|
|
1603
|
-
refreshAccessToken: ((refreshToken: string) => Promise<OAuth2Tokens>) | ((refreshToken: string) => Promise<{
|
|
1604
|
-
accessToken: any;
|
|
1605
|
-
refreshToken: any;
|
|
1606
|
-
accessTokenExpiresAt: Date | undefined;
|
|
1607
|
-
}>);
|
|
1638
|
+
}) => Promise<OAuth2Tokens>;
|
|
1639
|
+
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
1608
1640
|
getUserInfo(token: OAuth2Tokens & {
|
|
1609
1641
|
expectedIdTokenNonce?: string | undefined;
|
|
1610
1642
|
user?: {
|
|
@@ -1840,4 +1872,4 @@ type SocialProviders = { [K in SocialProviderList[number]]?: AwaitableFunction<P
|
|
|
1840
1872
|
}> };
|
|
1841
1873
|
type SocialProviderList = typeof socialProviderList;
|
|
1842
1874
|
//#endregion
|
|
1843
|
-
export { AccountStatus, AppleNonConformUser, AppleOptions, AppleProfile, AtlassianOptions, AtlassianProfile, CognitoOptions, CognitoProfile, DiscordOptions, DiscordProfile, DropboxOptions, DropboxProfile, FacebookGraphProfile, FacebookLimitedLoginProfile, FacebookOptions, FacebookProfile, FigmaOptions, FigmaProfile, GithubOptions, GithubProfile, GitlabOptions, GitlabProfile, GoogleOptions, GoogleProfile, HuggingFaceOptions, HuggingFaceProfile, KakaoOptions, KakaoProfile, KickOptions, KickProfile, LineIdTokenPayload, LineOptions, LineUserInfo, LinearOptions, LinearProfile, LinearUser, LinkedInOptions, LinkedInProfile, LoginType, MicrosoftEntraIDProfile, MicrosoftOptions, NaverOptions, NaverProfile, NotionOptions, NotionProfile, PayPalOptions, PayPalProfile, PayPalTokenResponse, PaybinOptions, PaybinProfile, PhoneNumber, PolarOptions, PolarProfile, PronounOption, RailwayOptions, RailwayProfile, RedditOptions, RedditProfile, RobloxOptions, RobloxProfile, SalesforceOptions, SalesforceProfile, SlackOptions, SlackProfile, SocialProvider, SocialProviderList, SocialProviderListEnum, SocialProviders, SpotifyOptions, SpotifyProfile, TiktokOptions, TiktokProfile, TwitchOptions, TwitchProfile, TwitterOption, TwitterProfile, VercelOptions, VercelProfile, VerifyGoogleIdTokenOptions, VkOption, VkProfile, WeChatOptions, WeChatProfile, ZoomOptions, ZoomProfile, apple, atlassian, cognito, discord, dropbox, facebook, figma, getApplePublicKey, getCognitoPublicKey, getGooglePublicKey, getMicrosoftPublicKey, github, gitlab, google, huggingface, isGoogleHostedDomainAllowed, kakao, kick, line, linear, linkedin, microsoft, naver, notion, paybin, paypal, polar, railway, reddit, roblox, salesforce, slack, socialProviderList, socialProviders, spotify, tiktok, twitch, twitter, vercel, verifyGoogleIdToken, vk, wechat, zoom };
|
|
1875
|
+
export { AccountStatus, AppleNonConformUser, AppleOptions, AppleProfile, AtlassianOptions, AtlassianProfile, CloudflareOptions, CloudflareProfile, CognitoOptions, CognitoProfile, DiscordOptions, DiscordProfile, DropboxOptions, DropboxProfile, FacebookGraphProfile, FacebookLimitedLoginProfile, FacebookOptions, FacebookProfile, FigmaOptions, FigmaProfile, GithubOptions, GithubProfile, GitlabOptions, GitlabProfile, GoogleOptions, GoogleProfile, HuggingFaceOptions, HuggingFaceProfile, KakaoOptions, KakaoProfile, KickOptions, KickProfile, LineIdTokenPayload, LineOptions, LineUserInfo, LinearOptions, LinearProfile, LinearUser, LinkedInOptions, LinkedInProfile, LoginType, MicrosoftEntraIDProfile, MicrosoftOptions, NaverOptions, NaverProfile, NotionOptions, NotionProfile, PayPalOptions, PayPalProfile, PayPalTokenResponse, PaybinOptions, PaybinProfile, PhoneNumber, PolarOptions, PolarProfile, PronounOption, RailwayOptions, RailwayProfile, RedditOptions, RedditProfile, RobloxOptions, RobloxProfile, SalesforceOptions, SalesforceProfile, SlackOptions, SlackProfile, SocialProvider, SocialProviderList, SocialProviderListEnum, SocialProviders, SpotifyOptions, SpotifyProfile, TiktokOptions, TiktokProfile, TwitchOptions, TwitchProfile, TwitterOption, TwitterProfile, VercelOptions, VercelProfile, VerifyGoogleIdTokenOptions, VkOption, VkProfile, WeChatOptions, WeChatProfile, ZoomOptions, ZoomProfile, apple, atlassian, cloudflare, cognito, discord, dropbox, facebook, figma, getApplePublicKey, getCognitoPublicKey, getGooglePublicKey, getMicrosoftPublicKey, github, gitlab, google, huggingface, isGoogleHostedDomainAllowed, kakao, kick, line, linear, linkedin, microsoft, naver, notion, paybin, paypal, polar, railway, reddit, roblox, salesforce, slack, socialProviderList, socialProviders, spotify, tiktok, twitch, twitter, vercel, verifyGoogleIdToken, vk, wechat, zoom };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { apple, getApplePublicKey } from "./apple.mjs";
|
|
2
2
|
import { atlassian } from "./atlassian.mjs";
|
|
3
|
+
import { cloudflare } from "./cloudflare.mjs";
|
|
3
4
|
import { cognito, getCognitoPublicKey } from "./cognito.mjs";
|
|
4
5
|
import { discord } from "./discord.mjs";
|
|
5
6
|
import { dropbox } from "./dropbox.mjs";
|
|
@@ -38,6 +39,7 @@ import * as z from "zod";
|
|
|
38
39
|
const socialProviders = {
|
|
39
40
|
apple,
|
|
40
41
|
atlassian,
|
|
42
|
+
cloudflare,
|
|
41
43
|
cognito,
|
|
42
44
|
discord,
|
|
43
45
|
facebook,
|
|
@@ -75,4 +77,4 @@ const socialProviders = {
|
|
|
75
77
|
const socialProviderList = Object.keys(socialProviders);
|
|
76
78
|
const SocialProviderListEnum = z.enum(socialProviderList).or(z.string());
|
|
77
79
|
//#endregion
|
|
78
|
-
export { SocialProviderListEnum, apple, atlassian, cognito, discord, dropbox, facebook, figma, getApplePublicKey, getCognitoPublicKey, getGooglePublicKey, getMicrosoftPublicKey, github, gitlab, google, huggingface, isGoogleHostedDomainAllowed, kakao, kick, line, linear, linkedin, microsoft, naver, notion, paybin, paypal, polar, railway, reddit, roblox, salesforce, slack, socialProviderList, socialProviders, spotify, tiktok, twitch, twitter, vercel, verifyGoogleIdToken, vk, wechat, zoom };
|
|
80
|
+
export { SocialProviderListEnum, apple, atlassian, cloudflare, cognito, discord, dropbox, facebook, figma, getApplePublicKey, getCognitoPublicKey, getGooglePublicKey, getMicrosoftPublicKey, github, gitlab, google, huggingface, isGoogleHostedDomainAllowed, kakao, kick, line, linear, linkedin, microsoft, naver, notion, paybin, paypal, polar, railway, reddit, roblox, salesforce, slack, socialProviderList, socialProviders, spotify, tiktok, twitch, twitter, vercel, verifyGoogleIdToken, vk, wechat, zoom };
|
|
@@ -22,7 +22,6 @@ const line = (options) => {
|
|
|
22
22
|
id: "line",
|
|
23
23
|
name: "LINE",
|
|
24
24
|
accountSubject: ({ profile }) => profile.sub,
|
|
25
|
-
accountIssuer: "https://access.line.me",
|
|
26
25
|
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, additionalParams }) {
|
|
27
26
|
const _scopes = options.disableDefaultScope ? [] : [
|
|
28
27
|
"openid",
|
|
@@ -142,9 +142,6 @@ declare const microsoft: (options: MicrosoftOptions) => {
|
|
|
142
142
|
accountSubject: ({
|
|
143
143
|
profile
|
|
144
144
|
}: OAuthAccountKeyContext<MicrosoftEntraIDProfile>) => string;
|
|
145
|
-
accountIssuer: ({
|
|
146
|
-
profile
|
|
147
|
-
}: OAuthAccountKeyContext<MicrosoftEntraIDProfile>) => string;
|
|
148
145
|
createAuthorizationURL(data: {
|
|
149
146
|
state: string;
|
|
150
147
|
codeVerifier: string;
|
|
@@ -30,7 +30,6 @@ const microsoft = (options) => {
|
|
|
30
30
|
id: "microsoft",
|
|
31
31
|
name: "Microsoft EntraID",
|
|
32
32
|
accountSubject: ({ profile }) => profile.oid,
|
|
33
|
-
accountIssuer: ({ profile }) => profile.iss,
|
|
34
33
|
createAuthorizationURL(data) {
|
|
35
34
|
if (!getPrimaryClientId(options.clientId)) {
|
|
36
35
|
logger.error("Client Id is required for Microsoft Entra ID. Make sure to provide it in the options.");
|
|
@@ -13,7 +13,6 @@ const paybin = (options) => {
|
|
|
13
13
|
id: "paybin",
|
|
14
14
|
name: "Paybin",
|
|
15
15
|
accountSubject: ({ profile }) => profile.sub,
|
|
16
|
-
accountIssuer: issuer,
|
|
17
16
|
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, additionalParams }) {
|
|
18
17
|
if (!options.clientId || !options.clientSecret) {
|
|
19
18
|
logger.error("Client Id and Client Secret is required for Paybin. Make sure to provide them in the options.");
|
|
@@ -72,23 +72,15 @@ declare const paypal: (options: PayPalOptions) => {
|
|
|
72
72
|
}): Promise<URL>;
|
|
73
73
|
validateAuthorizationCode: ({
|
|
74
74
|
code,
|
|
75
|
+
codeVerifier,
|
|
75
76
|
redirectURI
|
|
76
77
|
}: {
|
|
77
78
|
code: string;
|
|
78
79
|
redirectURI: string;
|
|
79
80
|
codeVerifier?: string | undefined;
|
|
80
81
|
deviceId?: string | undefined;
|
|
81
|
-
}) => Promise<
|
|
82
|
-
|
|
83
|
-
refreshToken: string | undefined;
|
|
84
|
-
accessTokenExpiresAt: Date | undefined;
|
|
85
|
-
idToken: string | undefined;
|
|
86
|
-
}>;
|
|
87
|
-
refreshAccessToken: ((refreshToken: string) => Promise<OAuth2Tokens>) | ((refreshToken: string) => Promise<{
|
|
88
|
-
accessToken: any;
|
|
89
|
-
refreshToken: any;
|
|
90
|
-
accessTokenExpiresAt: Date | undefined;
|
|
91
|
-
}>);
|
|
82
|
+
}) => Promise<OAuth2Tokens>;
|
|
83
|
+
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
92
84
|
getUserInfo(token: OAuth2Tokens & {
|
|
93
85
|
expectedIdTokenNonce?: string | undefined;
|
|
94
86
|
user?: {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { BetterAuthError } from "../error/index.mjs";
|
|
2
2
|
import { logger } from "../env/logger.mjs";
|
|
3
3
|
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
5
|
+
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
5
6
|
import { decodeJwt } from "jose";
|
|
6
7
|
import { betterFetch } from "@better-fetch/fetch";
|
|
7
8
|
//#region src/social-providers/paypal.ts
|
|
@@ -10,6 +11,11 @@ const paypal = (options) => {
|
|
|
10
11
|
const authorizationEndpoint = isSandbox ? "https://www.sandbox.paypal.com/signin/authorize" : "https://www.paypal.com/signin/authorize";
|
|
11
12
|
const tokenEndpoint = isSandbox ? "https://api-m.sandbox.paypal.com/v1/oauth2/token" : "https://api-m.paypal.com/v1/oauth2/token";
|
|
12
13
|
const userInfoEndpoint = isSandbox ? "https://api-m.sandbox.paypal.com/v1/identity/oauth2/userinfo" : "https://api-m.paypal.com/v1/identity/oauth2/userinfo";
|
|
14
|
+
const tokenRequestOptions = {
|
|
15
|
+
clientId: options.clientId,
|
|
16
|
+
clientSecret: options.clientSecret
|
|
17
|
+
};
|
|
18
|
+
const tokenEndpointAuth = { method: "client_secret_basic" };
|
|
13
19
|
return {
|
|
14
20
|
id: "paypal",
|
|
15
21
|
name: "PayPal",
|
|
@@ -31,62 +37,29 @@ const paypal = (options) => {
|
|
|
31
37
|
additionalParams
|
|
32
38
|
});
|
|
33
39
|
},
|
|
34
|
-
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
35
|
-
/**
|
|
36
|
-
* PayPal requires Basic Auth for token exchange
|
|
37
|
-
**/
|
|
38
|
-
const credentials = base64.encode(`${options.clientId}:${options.clientSecret}`);
|
|
40
|
+
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
39
41
|
try {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
body: new URLSearchParams({
|
|
49
|
-
grant_type: "authorization_code",
|
|
50
|
-
code,
|
|
51
|
-
redirect_uri: redirectURI
|
|
52
|
-
}).toString()
|
|
42
|
+
return await validateAuthorizationCode({
|
|
43
|
+
code,
|
|
44
|
+
codeVerifier,
|
|
45
|
+
redirectURI: options.redirectURI || redirectURI,
|
|
46
|
+
options: tokenRequestOptions,
|
|
47
|
+
tokenEndpoint,
|
|
48
|
+
tokenEndpointAuth
|
|
53
49
|
});
|
|
54
|
-
if (!response.data) throw new BetterAuthError("FAILED_TO_GET_ACCESS_TOKEN");
|
|
55
|
-
const data = response.data;
|
|
56
|
-
return {
|
|
57
|
-
accessToken: data.access_token,
|
|
58
|
-
refreshToken: data.refresh_token,
|
|
59
|
-
accessTokenExpiresAt: data.expires_in ? new Date(Date.now() + data.expires_in * 1e3) : void 0,
|
|
60
|
-
idToken: data.id_token
|
|
61
|
-
};
|
|
62
50
|
} catch (error) {
|
|
63
51
|
logger.error("PayPal token exchange failed:", error);
|
|
64
52
|
throw new BetterAuthError("FAILED_TO_GET_ACCESS_TOKEN");
|
|
65
53
|
}
|
|
66
54
|
},
|
|
67
55
|
refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
|
|
68
|
-
const credentials = base64.encode(`${options.clientId}:${options.clientSecret}`);
|
|
69
56
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"Accept-Language": "en_US",
|
|
76
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
77
|
-
},
|
|
78
|
-
body: new URLSearchParams({
|
|
79
|
-
grant_type: "refresh_token",
|
|
80
|
-
refresh_token: refreshToken
|
|
81
|
-
}).toString()
|
|
57
|
+
return await refreshAccessToken({
|
|
58
|
+
refreshToken,
|
|
59
|
+
options: tokenRequestOptions,
|
|
60
|
+
tokenEndpoint,
|
|
61
|
+
tokenEndpointAuth
|
|
82
62
|
});
|
|
83
|
-
if (!response.data) throw new BetterAuthError("FAILED_TO_REFRESH_ACCESS_TOKEN");
|
|
84
|
-
const data = response.data;
|
|
85
|
-
return {
|
|
86
|
-
accessToken: data.access_token,
|
|
87
|
-
refreshToken: data.refresh_token,
|
|
88
|
-
accessTokenExpiresAt: data.expires_in ? new Date(Date.now() + data.expires_in * 1e3) : void 0
|
|
89
|
-
};
|
|
90
63
|
} catch (error) {
|
|
91
64
|
logger.error("PayPal token refresh failed:", error);
|
|
92
65
|
throw new BetterAuthError("FAILED_TO_REFRESH_ACCESS_TOKEN");
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import { getOAuth2Tokens } from "../oauth2/utils.mjs";
|
|
2
1
|
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
3
2
|
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
4
|
-
import {
|
|
3
|
+
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
4
|
+
import { createPlaceholderEmail } from "../utils/email.mjs";
|
|
5
5
|
import { betterFetch } from "@better-fetch/fetch";
|
|
6
6
|
//#region src/social-providers/reddit.ts
|
|
7
7
|
const reddit = (options) => {
|
|
8
|
+
const tokenEndpoint = "https://www.reddit.com/api/v1/access_token";
|
|
9
|
+
const tokenRequestOptions = {
|
|
10
|
+
clientId: options.clientId,
|
|
11
|
+
clientSecret: options.clientSecret
|
|
12
|
+
};
|
|
13
|
+
const tokenEndpointAuth = { method: "client_secret_basic" };
|
|
8
14
|
return {
|
|
9
15
|
id: "reddit",
|
|
10
16
|
name: "Reddit",
|
|
@@ -25,34 +31,24 @@ const reddit = (options) => {
|
|
|
25
31
|
});
|
|
26
32
|
},
|
|
27
33
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
28
|
-
|
|
29
|
-
grant_type: "authorization_code",
|
|
34
|
+
return validateAuthorizationCode({
|
|
30
35
|
code,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
redirectURI: options.redirectURI || redirectURI,
|
|
37
|
+
options: tokenRequestOptions,
|
|
38
|
+
tokenEndpoint,
|
|
39
|
+
tokenEndpointAuth,
|
|
35
40
|
headers: {
|
|
36
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
37
41
|
accept: "text/plain",
|
|
38
|
-
"user-agent": "better-auth"
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
body: body.toString()
|
|
42
|
+
"user-agent": "better-auth"
|
|
43
|
+
}
|
|
42
44
|
});
|
|
43
|
-
if (error) throw error;
|
|
44
|
-
return getOAuth2Tokens(data);
|
|
45
45
|
},
|
|
46
46
|
refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
|
|
47
47
|
return refreshAccessToken({
|
|
48
48
|
refreshToken,
|
|
49
|
-
options:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
clientSecret: options.clientSecret
|
|
53
|
-
},
|
|
54
|
-
authentication: "basic",
|
|
55
|
-
tokenEndpoint: "https://www.reddit.com/api/v1/access_token"
|
|
49
|
+
options: tokenRequestOptions,
|
|
50
|
+
tokenEndpoint,
|
|
51
|
+
tokenEndpointAuth
|
|
56
52
|
});
|
|
57
53
|
},
|
|
58
54
|
async getUserInfo(token) {
|
|
@@ -63,7 +59,10 @@ const reddit = (options) => {
|
|
|
63
59
|
} });
|
|
64
60
|
if (error) return null;
|
|
65
61
|
const userMap = await options.mapProfileToUser?.(profile);
|
|
66
|
-
const email = userMap?.email ||
|
|
62
|
+
const email = userMap?.email || createPlaceholderEmail({
|
|
63
|
+
identifier: profile.id,
|
|
64
|
+
namespace: "reddit"
|
|
65
|
+
});
|
|
67
66
|
return {
|
|
68
67
|
user: {
|
|
69
68
|
name: profile.name,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
2
2
|
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
3
3
|
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
4
|
+
import { createPlaceholderEmail } from "../utils/email.mjs";
|
|
4
5
|
import { betterFetch } from "@better-fetch/fetch";
|
|
5
6
|
//#region src/social-providers/roblox.ts
|
|
6
7
|
const roblox = (options) => {
|
|
@@ -53,7 +54,10 @@ const roblox = (options) => {
|
|
|
53
54
|
user: {
|
|
54
55
|
name: profile.nickname || profile.preferred_username || "",
|
|
55
56
|
image: profile.picture,
|
|
56
|
-
email:
|
|
57
|
+
email: createPlaceholderEmail({
|
|
58
|
+
identifier: profile.sub,
|
|
59
|
+
namespace: "roblox"
|
|
60
|
+
}),
|
|
57
61
|
emailVerified: false,
|
|
58
62
|
...userMap
|
|
59
63
|
},
|