@better-auth/core 1.7.2 → 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/global.mjs +1 -1
- 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/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 +17 -22
- package/dist/social-providers/tiktok.d.mts +1 -0
- package/dist/social-providers/tiktok.mjs +14 -9
- 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/package.json +2 -2
- 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/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 +27 -36
- package/src/social-providers/tiktok.ts +18 -13
- package/src/types/context.ts +11 -0
- package/src/types/init-options.ts +11 -0
- package/src/utils/ip.ts +13 -9
|
@@ -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,11 +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";
|
|
3
|
+
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
4
4
|
import { createPlaceholderEmail } from "../utils/email.mjs";
|
|
5
|
-
import { base64 } from "@better-auth/utils/base64";
|
|
6
5
|
import { betterFetch } from "@better-fetch/fetch";
|
|
7
6
|
//#region src/social-providers/reddit.ts
|
|
8
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" };
|
|
9
14
|
return {
|
|
10
15
|
id: "reddit",
|
|
11
16
|
name: "Reddit",
|
|
@@ -26,34 +31,24 @@ const reddit = (options) => {
|
|
|
26
31
|
});
|
|
27
32
|
},
|
|
28
33
|
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
29
|
-
|
|
30
|
-
grant_type: "authorization_code",
|
|
34
|
+
return validateAuthorizationCode({
|
|
31
35
|
code,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
redirectURI: options.redirectURI || redirectURI,
|
|
37
|
+
options: tokenRequestOptions,
|
|
38
|
+
tokenEndpoint,
|
|
39
|
+
tokenEndpointAuth,
|
|
36
40
|
headers: {
|
|
37
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
38
41
|
accept: "text/plain",
|
|
39
|
-
"user-agent": "better-auth"
|
|
40
|
-
|
|
41
|
-
},
|
|
42
|
-
body: body.toString()
|
|
42
|
+
"user-agent": "better-auth"
|
|
43
|
+
}
|
|
43
44
|
});
|
|
44
|
-
if (error) throw error;
|
|
45
|
-
return getOAuth2Tokens(data);
|
|
46
45
|
},
|
|
47
46
|
refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
|
|
48
47
|
return refreshAccessToken({
|
|
49
48
|
refreshToken,
|
|
50
|
-
options:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
clientSecret: options.clientSecret
|
|
54
|
-
},
|
|
55
|
-
authentication: "basic",
|
|
56
|
-
tokenEndpoint: "https://www.reddit.com/api/v1/access_token"
|
|
49
|
+
options: tokenRequestOptions,
|
|
50
|
+
tokenEndpoint,
|
|
51
|
+
tokenEndpointAuth
|
|
57
52
|
});
|
|
58
53
|
},
|
|
59
54
|
async getUserInfo(token) {
|
|
@@ -6,6 +6,13 @@ import { betterFetch } from "@better-fetch/fetch";
|
|
|
6
6
|
//#region src/social-providers/tiktok.ts
|
|
7
7
|
const tiktok = (options) => {
|
|
8
8
|
const tokenEndpoint = "https://open.tiktokapis.com/v2/oauth/token/";
|
|
9
|
+
const tokenEndpointAuth = {
|
|
10
|
+
method: "custom",
|
|
11
|
+
customizeRequest({ body }) {
|
|
12
|
+
body.set("client_key", options.clientKey);
|
|
13
|
+
body.set("client_secret", options.clientSecret);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
9
16
|
return {
|
|
10
17
|
id: "tiktok",
|
|
11
18
|
name: "TikTok",
|
|
@@ -27,24 +34,22 @@ const tiktok = (options) => {
|
|
|
27
34
|
}
|
|
28
35
|
return url;
|
|
29
36
|
},
|
|
30
|
-
validateAuthorizationCode: async ({ code, redirectURI }) => {
|
|
37
|
+
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
31
38
|
return validateAuthorizationCode({
|
|
32
39
|
code,
|
|
40
|
+
codeVerifier,
|
|
33
41
|
redirectURI: options.redirectURI || redirectURI,
|
|
34
|
-
options: {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
tokenEndpoint
|
|
42
|
+
options: {},
|
|
43
|
+
tokenEndpoint,
|
|
44
|
+
tokenEndpointAuth
|
|
39
45
|
});
|
|
40
46
|
},
|
|
41
47
|
refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
|
|
42
48
|
return refreshAccessToken({
|
|
43
49
|
refreshToken,
|
|
44
|
-
options: {
|
|
50
|
+
options: {},
|
|
45
51
|
tokenEndpoint,
|
|
46
|
-
|
|
47
|
-
extraParams: { client_key: options.clientKey }
|
|
52
|
+
tokenEndpointAuth
|
|
48
53
|
});
|
|
49
54
|
},
|
|
50
55
|
async getUserInfo(token) {
|
package/dist/types/context.d.mts
CHANGED
|
@@ -8,6 +8,7 @@ import { Awaitable, LiteralString } from "./helper.mjs";
|
|
|
8
8
|
import { BetterAuthPlugin } from "./plugin.mjs";
|
|
9
9
|
import { BetterAuthOptions, BetterAuthRateLimitOptions, UserProvisioningSource } from "./init-options.mjs";
|
|
10
10
|
import { Account, AccountKey } from "../db/schema/account.mjs";
|
|
11
|
+
import { SchemaCheck } from "../db/schema-check.mjs";
|
|
11
12
|
import { BetterAuthCookie, BetterAuthCookies, CookieCachePayload } from "./cookie.mjs";
|
|
12
13
|
import { SecretConfig } from "./secret.mjs";
|
|
13
14
|
import { OAuthProvider } from "../oauth2/oauth-provider.mjs";
|
|
@@ -263,6 +264,16 @@ type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> = Plugin
|
|
|
263
264
|
storage: "memory" | "database" | "secondary-storage";
|
|
264
265
|
} & Omit<BetterAuthRateLimitOptions, "enabled" | "window" | "max" | "storage">;
|
|
265
266
|
adapter: DBAdapter<Options>;
|
|
267
|
+
/**
|
|
268
|
+
* Confirms the database can hold what this configuration writes.
|
|
269
|
+
*
|
|
270
|
+
* Shared by initialization and requests for this adapter instance;
|
|
271
|
+
* returns nothing once the schema is known to be clean.
|
|
272
|
+
* Context construction does not await the verdict, so migration
|
|
273
|
+
* tooling can still use a context whose schema needs repair.
|
|
274
|
+
* Absent when the check is disabled or the adapter registers none.
|
|
275
|
+
*/
|
|
276
|
+
checkSchema?: SchemaCheck | undefined;
|
|
266
277
|
internalAdapter: InternalAdapter<Options>;
|
|
267
278
|
createAuthCookie: CreateCookieGetterFn;
|
|
268
279
|
secret: string;
|
|
@@ -387,6 +387,17 @@ type BetterAuthAdvancedOptions = {
|
|
|
387
387
|
* @default false
|
|
388
388
|
*/
|
|
389
389
|
joins?: boolean;
|
|
390
|
+
/**
|
|
391
|
+
* Validate the schema during initialization and report problems
|
|
392
|
+
* through the configured logger. Authentication requests await
|
|
393
|
+
* the same check and fail when the schema does not match.
|
|
394
|
+
* Kysely introspects the database; Drizzle and Prisma inspect
|
|
395
|
+
* local schema metadata without opening a connection.
|
|
396
|
+
* Set `false` to disable runtime schema validation.
|
|
397
|
+
*
|
|
398
|
+
* @default true
|
|
399
|
+
*/
|
|
400
|
+
validateSchema?: boolean;
|
|
390
401
|
} | undefined;
|
|
391
402
|
/**
|
|
392
403
|
* Trusted proxy headers
|
package/dist/utils/ip.mjs
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { isDevelopment, isTest } from "../env/env-impl.mjs";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
//#region src/utils/ip.ts
|
|
4
|
+
const ipv4Schema = z.ipv4();
|
|
5
|
+
const ipv6Schema = z.ipv6();
|
|
4
6
|
/**
|
|
5
7
|
* Checks if an IP is valid IPv4 or IPv6
|
|
6
8
|
*/
|
|
7
9
|
function isValidIP(ip) {
|
|
8
|
-
return
|
|
10
|
+
return isIPv4(ip) || isIPv6(ip);
|
|
11
|
+
}
|
|
12
|
+
function isIPv4(ip) {
|
|
13
|
+
return z.validate(ipv4Schema, ip);
|
|
9
14
|
}
|
|
10
|
-
/**
|
|
11
|
-
* Checks if an IP is IPv6
|
|
12
|
-
*/
|
|
13
15
|
function isIPv6(ip) {
|
|
14
|
-
return z.
|
|
16
|
+
return z.validate(ipv6Schema, ip);
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
17
19
|
* Converts IPv4-mapped IPv6 address to IPv4
|
|
@@ -21,12 +23,12 @@ function extractIPv4FromMapped(ipv6) {
|
|
|
21
23
|
const lower = ipv6.toLowerCase();
|
|
22
24
|
if (lower.startsWith("::ffff:")) {
|
|
23
25
|
const ipv4Part = lower.substring(7);
|
|
24
|
-
if (
|
|
26
|
+
if (isIPv4(ipv4Part)) return ipv4Part;
|
|
25
27
|
}
|
|
26
28
|
const parts = ipv6.split(":");
|
|
27
29
|
if (parts.length === 7 && parts[5]?.toLowerCase() === "ffff") {
|
|
28
30
|
const ipv4Part = parts[6];
|
|
29
|
-
if (ipv4Part &&
|
|
31
|
+
if (ipv4Part && isIPv4(ipv4Part)) return ipv4Part;
|
|
30
32
|
}
|
|
31
33
|
if (lower.includes("::ffff:") || lower.includes(":ffff:")) {
|
|
32
34
|
const groups = expandIPv6(ipv6);
|
|
@@ -96,7 +98,7 @@ function normalizeIPv6(ipv6, subnetPrefix) {
|
|
|
96
98
|
* // -> "2001:0db8:0000:0000:0000:0000:0000:0000" (subnet /64)
|
|
97
99
|
*/
|
|
98
100
|
function normalizeIP(ip, options = {}) {
|
|
99
|
-
if (
|
|
101
|
+
if (isIPv4(ip)) return ip.toLowerCase();
|
|
100
102
|
if (!isIPv6(ip)) return ip.toLowerCase();
|
|
101
103
|
const ipv4 = extractIPv4FromMapped(ip);
|
|
102
104
|
if (ipv4) return ipv4.toLowerCase();
|
|
@@ -106,7 +108,7 @@ function normalizeIP(ip, options = {}) {
|
|
|
106
108
|
* Raw bytes of an IP for CIDR comparison. Returns `null` for an invalid IP.
|
|
107
109
|
*/
|
|
108
110
|
function ipToBytes(ip) {
|
|
109
|
-
if (
|
|
111
|
+
if (isIPv4(ip)) return Uint8Array.from(ip.split(".").map((octet) => Number(octet)));
|
|
110
112
|
if (!isIPv6(ip)) return null;
|
|
111
113
|
const mapped = extractIPv4FromMapped(ip);
|
|
112
114
|
if (mapped) return Uint8Array.from(mapped.split(".").map((octet) => Number(octet)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/core",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
"dependencies": {
|
|
158
158
|
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
159
159
|
"@standard-schema/spec": "^1.1.0",
|
|
160
|
-
"zod": "^4.
|
|
160
|
+
"zod": "^4.5.4"
|
|
161
161
|
},
|
|
162
162
|
"devDependencies": {
|
|
163
163
|
"@better-auth/utils": "0.4.2",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AsyncLocalStorage } from "@better-auth/core/async_hooks";
|
|
2
2
|
import { getAsyncLocalStorage } from "@better-auth/core/async_hooks";
|
|
3
3
|
import type { DBAdapter, DBTransactionAdapter } from "../db/adapter";
|
|
4
|
+
import { schemaCheckFor } from "../db/schema-check";
|
|
4
5
|
import type { BetterAuthOptions } from "../types";
|
|
5
6
|
import { __getBetterAuthGlobal } from "./global";
|
|
6
7
|
|
|
@@ -114,6 +115,10 @@ export const runWithTransaction = async <
|
|
|
114
115
|
if (store?.isTransactionActive) {
|
|
115
116
|
return fn();
|
|
116
117
|
}
|
|
118
|
+
// Settle the schema verdict before this transaction holds the
|
|
119
|
+
// connection a single-connection store would need for the lookup.
|
|
120
|
+
const pendingSchemaCheck = schemaCheckFor(adapter)?.();
|
|
121
|
+
if (pendingSchemaCheck) await pendingSchemaCheck;
|
|
117
122
|
const pendingHooks: Array<() => Promise<void>> = [];
|
|
118
123
|
let result: Awaited<R>;
|
|
119
124
|
let error: unknown;
|