@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.
Files changed (96) hide show
  1. package/dist/api/index.d.mts +3 -0
  2. package/dist/context/endpoint-context.d.mts +19 -5
  3. package/dist/context/endpoint-context.mjs +35 -16
  4. package/dist/context/global.mjs +5 -2
  5. package/dist/context/index.d.mts +2 -2
  6. package/dist/context/index.mjs +2 -2
  7. package/dist/context/transaction.mjs +3 -0
  8. package/dist/db/adapter/atomic-fallback.mjs +134 -0
  9. package/dist/db/adapter/factory.mjs +22 -4
  10. package/dist/db/adapter/index.d.mts +15 -11
  11. package/dist/db/get-tables.mjs +1 -9
  12. package/dist/db/index.d.mts +2 -2
  13. package/dist/db/index.mjs +2 -2
  14. package/dist/db/internal.d.mts +3 -1
  15. package/dist/db/internal.mjs +3 -1
  16. package/dist/db/schema/account.d.mts +2 -13
  17. package/dist/db/schema/account.mjs +1 -19
  18. package/dist/db/schema-check.d.mts +48 -0
  19. package/dist/db/schema-check.mjs +80 -0
  20. package/dist/db/schema-diff.d.mts +104 -0
  21. package/dist/db/schema-diff.mjs +154 -0
  22. package/dist/env/logger.mjs +16 -1
  23. package/dist/instrumentation/tracer.mjs +1 -1
  24. package/dist/oauth2/index.d.mts +2 -2
  25. package/dist/oauth2/oauth-provider.d.mts +0 -10
  26. package/dist/oauth2/token-endpoint-auth.d.mts +26 -2
  27. package/dist/oauth2/token-endpoint-auth.mjs +11 -0
  28. package/dist/social-providers/apple.d.mts +0 -1
  29. package/dist/social-providers/apple.mjs +0 -1
  30. package/dist/social-providers/cloudflare.d.mts +132 -0
  31. package/dist/social-providers/cloudflare.mjs +85 -0
  32. package/dist/social-providers/cognito.d.mts +0 -1
  33. package/dist/social-providers/cognito.mjs +0 -1
  34. package/dist/social-providers/facebook.d.mts +0 -1
  35. package/dist/social-providers/facebook.mjs +0 -1
  36. package/dist/social-providers/google.d.mts +0 -1
  37. package/dist/social-providers/google.mjs +0 -1
  38. package/dist/social-providers/index.d.mts +53 -21
  39. package/dist/social-providers/index.mjs +3 -1
  40. package/dist/social-providers/line.d.mts +0 -1
  41. package/dist/social-providers/line.mjs +0 -1
  42. package/dist/social-providers/microsoft-entra-id.d.mts +0 -3
  43. package/dist/social-providers/microsoft-entra-id.mjs +0 -1
  44. package/dist/social-providers/paybin.d.mts +0 -1
  45. package/dist/social-providers/paybin.mjs +0 -1
  46. package/dist/social-providers/paypal.d.mts +3 -11
  47. package/dist/social-providers/paypal.mjs +20 -47
  48. package/dist/social-providers/reddit.mjs +22 -23
  49. package/dist/social-providers/roblox.mjs +5 -1
  50. package/dist/social-providers/tiktok.d.mts +1 -0
  51. package/dist/social-providers/tiktok.mjs +19 -10
  52. package/dist/social-providers/twitter.mjs +5 -1
  53. package/dist/social-providers/wechat.mjs +6 -1
  54. package/dist/types/context.d.mts +11 -0
  55. package/dist/types/init-options.d.mts +11 -0
  56. package/dist/utils/ip.mjs +11 -9
  57. package/dist/utils/url.d.mts +10 -1
  58. package/dist/utils/url.mjs +21 -1
  59. package/package.json +3 -3
  60. package/src/context/endpoint-context.ts +46 -21
  61. package/src/context/global.ts +7 -0
  62. package/src/context/index.ts +2 -0
  63. package/src/context/transaction.ts +5 -0
  64. package/src/db/adapter/atomic-fallback.ts +237 -0
  65. package/src/db/adapter/factory.ts +33 -17
  66. package/src/db/adapter/index.ts +15 -11
  67. package/src/db/get-tables.ts +1 -14
  68. package/src/db/index.ts +0 -2
  69. package/src/db/internal.ts +19 -0
  70. package/src/db/schema/account.ts +3 -22
  71. package/src/db/schema/user.ts +1 -1
  72. package/src/db/schema-check.ts +107 -0
  73. package/src/db/schema-diff.ts +270 -0
  74. package/src/env/logger.ts +22 -1
  75. package/src/oauth2/index.ts +2 -0
  76. package/src/oauth2/oauth-provider.ts +0 -10
  77. package/src/oauth2/token-endpoint-auth.ts +39 -6
  78. package/src/social-providers/apple.ts +0 -1
  79. package/src/social-providers/cloudflare.ts +221 -0
  80. package/src/social-providers/cognito.ts +0 -1
  81. package/src/social-providers/facebook.ts +0 -1
  82. package/src/social-providers/google.ts +0 -1
  83. package/src/social-providers/index.ts +3 -0
  84. package/src/social-providers/line.ts +0 -1
  85. package/src/social-providers/microsoft-entra-id.ts +0 -1
  86. package/src/social-providers/paybin.ts +0 -1
  87. package/src/social-providers/paypal.ts +30 -71
  88. package/src/social-providers/reddit.ts +34 -37
  89. package/src/social-providers/roblox.ts +5 -3
  90. package/src/social-providers/tiktok.ts +25 -14
  91. package/src/social-providers/twitter.ts +8 -2
  92. package/src/social-providers/wechat.ts +6 -6
  93. package/src/types/context.ts +11 -0
  94. package/src/types/init-options.ts +11 -0
  95. package/src/utils/ip.ts +13 -9
  96. 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 };
@@ -52,7 +52,6 @@ declare const cognito: (options: CognitoOptions) => {
52
52
  accountSubject: ({
53
53
  profile
54
54
  }: OAuthAccountKeyContext<CognitoProfile>) => string;
55
- accountIssuer: string;
56
55
  createAuthorizationURL({
57
56
  state,
58
57
  scopes,
@@ -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.");
@@ -42,7 +42,6 @@ declare const facebook: (options: FacebookOptions) => {
42
42
  accountSubject: ({
43
43
  profile
44
44
  }: OAuthAccountKeyContext<FacebookProfile>) => string;
45
- accountIssuer: string;
46
45
  createAuthorizationURL({
47
46
  state,
48
47
  scopes,
@@ -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.");
@@ -85,7 +85,6 @@ declare const google: (options: GoogleOptions) => {
85
85
  accountSubject: ({
86
86
  profile
87
87
  }: OAuthAccountKeyContext<GoogleProfile>) => string;
88
- accountIssuer: string;
89
88
  createAuthorizationURL({
90
89
  state,
91
90
  scopes,
@@ -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
- accessToken: string;
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 };
@@ -36,7 +36,6 @@ declare const line: (options: LineOptions) => {
36
36
  accountSubject: ({
37
37
  profile
38
38
  }: OAuthAccountKeyContext<LineIdTokenPayload | LineUserInfo>) => string;
39
- accountIssuer: string;
40
39
  createAuthorizationURL({
41
40
  state,
42
41
  scopes,
@@ -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.");
@@ -24,7 +24,6 @@ declare const paybin: (options: PaybinOptions) => {
24
24
  accountSubject: ({
25
25
  profile
26
26
  }: OAuthAccountKeyContext<PaybinProfile>) => string;
27
- accountIssuer: string;
28
27
  createAuthorizationURL({
29
28
  state,
30
29
  scopes,
@@ -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
- accessToken: string;
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 { base64 } from "@better-auth/utils/base64";
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
- const response = await betterFetch(tokenEndpoint, {
41
- method: "POST",
42
- headers: {
43
- Authorization: `Basic ${credentials}`,
44
- Accept: "application/json",
45
- "Accept-Language": "en_US",
46
- "Content-Type": "application/x-www-form-urlencoded"
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
- const response = await betterFetch(tokenEndpoint, {
71
- method: "POST",
72
- headers: {
73
- Authorization: `Basic ${credentials}`,
74
- Accept: "application/json",
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 { base64 } from "@better-auth/utils/base64";
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
- const body = new URLSearchParams({
29
- grant_type: "authorization_code",
34
+ return validateAuthorizationCode({
30
35
  code,
31
- redirect_uri: options.redirectURI || redirectURI
32
- });
33
- const { data, error } = await betterFetch("https://www.reddit.com/api/v1/access_token", {
34
- method: "POST",
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
- Authorization: `Basic ${base64.encode(`${options.clientId}:${options.clientSecret}`)}`
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
- clientId: options.clientId,
51
- clientKey: options.clientKey,
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 || `${profile.id}@reddit.invalid`;
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: profile.preferred_username || null,
57
+ email: createPlaceholderEmail({
58
+ identifier: profile.sub,
59
+ namespace: "roblox"
60
+ }),
57
61
  emailVerified: false,
58
62
  ...userMap
59
63
  },
@@ -141,6 +141,7 @@ declare const tiktok: (options: TiktokOptions) => {
141
141
  }): URL;
142
142
  validateAuthorizationCode: ({
143
143
  code,
144
+ codeVerifier,
144
145
  redirectURI
145
146
  }: {
146
147
  code: string;