@better-auth/core 1.7.0-beta.2 → 1.7.0-beta.4

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 (172) hide show
  1. package/dist/context/global.mjs +1 -1
  2. package/dist/db/adapter/factory.mjs +64 -3
  3. package/dist/db/adapter/index.d.mts +35 -1
  4. package/dist/db/adapter/types.d.mts +1 -1
  5. package/dist/db/type.d.mts +12 -0
  6. package/dist/error/codes.d.mts +1 -0
  7. package/dist/error/codes.mjs +1 -0
  8. package/dist/instrumentation/tracer.mjs +1 -1
  9. package/dist/oauth2/authorization-params.d.mts +12 -0
  10. package/dist/oauth2/authorization-params.mjs +12 -0
  11. package/dist/oauth2/basic-credentials.d.mts +30 -0
  12. package/dist/oauth2/basic-credentials.mjs +64 -0
  13. package/dist/oauth2/client-assertion.d.mts +38 -22
  14. package/dist/oauth2/client-assertion.mjs +63 -28
  15. package/dist/oauth2/client-credentials-token.d.mts +19 -40
  16. package/dist/oauth2/client-credentials-token.mjs +18 -29
  17. package/dist/oauth2/create-authorization-url.d.mts +9 -1
  18. package/dist/oauth2/create-authorization-url.mjs +23 -5
  19. package/dist/oauth2/index.d.mts +10 -7
  20. package/dist/oauth2/index.mjs +9 -7
  21. package/dist/oauth2/oauth-provider.d.mts +21 -2
  22. package/dist/oauth2/refresh-access-token.d.mts +20 -40
  23. package/dist/oauth2/refresh-access-token.mjs +19 -32
  24. package/dist/oauth2/token-endpoint-auth.d.mts +17 -0
  25. package/dist/oauth2/token-endpoint-auth.mjs +89 -0
  26. package/dist/oauth2/utils.d.mts +9 -1
  27. package/dist/oauth2/utils.mjs +12 -1
  28. package/dist/oauth2/validate-authorization-code.d.mts +17 -52
  29. package/dist/oauth2/validate-authorization-code.mjs +17 -30
  30. package/dist/oauth2/verify.mjs +15 -5
  31. package/dist/social-providers/apple.d.mts +5 -12
  32. package/dist/social-providers/apple.mjs +14 -3
  33. package/dist/social-providers/atlassian.d.mts +3 -1
  34. package/dist/social-providers/atlassian.mjs +5 -2
  35. package/dist/social-providers/cognito.d.mts +16 -1
  36. package/dist/social-providers/cognito.mjs +6 -2
  37. package/dist/social-providers/discord.d.mts +5 -3
  38. package/dist/social-providers/discord.mjs +16 -3
  39. package/dist/social-providers/dropbox.d.mts +3 -1
  40. package/dist/social-providers/dropbox.mjs +5 -4
  41. package/dist/social-providers/facebook.d.mts +5 -3
  42. package/dist/social-providers/facebook.mjs +6 -3
  43. package/dist/social-providers/figma.d.mts +3 -1
  44. package/dist/social-providers/figma.mjs +3 -2
  45. package/dist/social-providers/github.d.mts +4 -2
  46. package/dist/social-providers/github.mjs +5 -4
  47. package/dist/social-providers/gitlab.d.mts +3 -1
  48. package/dist/social-providers/gitlab.mjs +3 -2
  49. package/dist/social-providers/google.d.mts +3 -1
  50. package/dist/social-providers/google.mjs +5 -2
  51. package/dist/social-providers/huggingface.d.mts +3 -1
  52. package/dist/social-providers/huggingface.mjs +3 -2
  53. package/dist/social-providers/index.d.mts +104 -36
  54. package/dist/social-providers/kakao.d.mts +3 -1
  55. package/dist/social-providers/kakao.mjs +3 -2
  56. package/dist/social-providers/kick.d.mts +3 -1
  57. package/dist/social-providers/kick.mjs +3 -2
  58. package/dist/social-providers/line.d.mts +3 -1
  59. package/dist/social-providers/line.mjs +3 -2
  60. package/dist/social-providers/linear.d.mts +3 -1
  61. package/dist/social-providers/linear.mjs +3 -2
  62. package/dist/social-providers/linkedin.d.mts +5 -3
  63. package/dist/social-providers/linkedin.mjs +4 -3
  64. package/dist/social-providers/microsoft-entra-id.d.mts +3 -2
  65. package/dist/social-providers/microsoft-entra-id.mjs +3 -2
  66. package/dist/social-providers/naver.d.mts +3 -1
  67. package/dist/social-providers/naver.mjs +3 -2
  68. package/dist/social-providers/notion.d.mts +3 -1
  69. package/dist/social-providers/notion.mjs +5 -2
  70. package/dist/social-providers/paybin.d.mts +3 -1
  71. package/dist/social-providers/paybin.mjs +3 -2
  72. package/dist/social-providers/paypal.d.mts +3 -1
  73. package/dist/social-providers/paypal.mjs +4 -3
  74. package/dist/social-providers/polar.d.mts +3 -1
  75. package/dist/social-providers/polar.mjs +3 -2
  76. package/dist/social-providers/railway.d.mts +3 -1
  77. package/dist/social-providers/railway.mjs +3 -2
  78. package/dist/social-providers/reddit.d.mts +3 -1
  79. package/dist/social-providers/reddit.mjs +3 -2
  80. package/dist/social-providers/roblox.d.mts +4 -2
  81. package/dist/social-providers/roblox.mjs +12 -2
  82. package/dist/social-providers/salesforce.d.mts +3 -1
  83. package/dist/social-providers/salesforce.mjs +3 -2
  84. package/dist/social-providers/slack.d.mts +4 -2
  85. package/dist/social-providers/slack.mjs +11 -8
  86. package/dist/social-providers/spotify.d.mts +3 -1
  87. package/dist/social-providers/spotify.mjs +3 -2
  88. package/dist/social-providers/tiktok.d.mts +3 -1
  89. package/dist/social-providers/tiktok.mjs +14 -2
  90. package/dist/social-providers/twitch.d.mts +3 -1
  91. package/dist/social-providers/twitch.mjs +3 -2
  92. package/dist/social-providers/twitter.d.mts +5 -2
  93. package/dist/social-providers/twitter.mjs +2 -1
  94. package/dist/social-providers/vercel.d.mts +3 -1
  95. package/dist/social-providers/vercel.mjs +3 -2
  96. package/dist/social-providers/vk.d.mts +3 -1
  97. package/dist/social-providers/vk.mjs +3 -2
  98. package/dist/social-providers/wechat.d.mts +3 -1
  99. package/dist/social-providers/wechat.mjs +7 -1
  100. package/dist/social-providers/zoom.d.mts +4 -2
  101. package/dist/social-providers/zoom.mjs +10 -17
  102. package/dist/types/context.d.mts +30 -4
  103. package/dist/types/init-options.d.mts +29 -5
  104. package/dist/utils/ip.d.mts +5 -4
  105. package/dist/utils/ip.mjs +3 -3
  106. package/dist/utils/redirect-uri.d.mts +20 -0
  107. package/dist/utils/redirect-uri.mjs +48 -0
  108. package/dist/utils/string.d.mts +5 -1
  109. package/dist/utils/string.mjs +20 -1
  110. package/dist/utils/url.d.mts +18 -1
  111. package/dist/utils/url.mjs +30 -1
  112. package/package.json +9 -8
  113. package/src/db/adapter/factory.ts +121 -3
  114. package/src/db/adapter/index.ts +32 -0
  115. package/src/db/adapter/types.ts +1 -0
  116. package/src/db/get-tables.ts +2 -0
  117. package/src/db/schema/user.ts +3 -0
  118. package/src/db/type.ts +12 -0
  119. package/src/error/codes.ts +1 -0
  120. package/src/oauth2/authorization-params.ts +28 -0
  121. package/src/oauth2/basic-credentials.ts +87 -0
  122. package/src/oauth2/client-assertion.ts +131 -58
  123. package/src/oauth2/client-credentials-token.ts +48 -72
  124. package/src/oauth2/create-authorization-url.ts +28 -6
  125. package/src/oauth2/index.ts +25 -9
  126. package/src/oauth2/oauth-provider.ts +21 -2
  127. package/src/oauth2/refresh-access-token.ts +50 -76
  128. package/src/oauth2/token-endpoint-auth.ts +221 -0
  129. package/src/oauth2/utils.ts +19 -0
  130. package/src/oauth2/validate-authorization-code.ts +55 -85
  131. package/src/oauth2/verify.ts +20 -4
  132. package/src/social-providers/apple.ts +27 -3
  133. package/src/social-providers/atlassian.ts +8 -1
  134. package/src/social-providers/cognito.ts +26 -1
  135. package/src/social-providers/discord.ts +22 -18
  136. package/src/social-providers/dropbox.ts +7 -5
  137. package/src/social-providers/facebook.ts +14 -9
  138. package/src/social-providers/figma.ts +8 -1
  139. package/src/social-providers/github.ts +5 -3
  140. package/src/social-providers/gitlab.ts +2 -0
  141. package/src/social-providers/google.ts +2 -0
  142. package/src/social-providers/huggingface.ts +8 -1
  143. package/src/social-providers/kakao.ts +2 -1
  144. package/src/social-providers/kick.ts +8 -1
  145. package/src/social-providers/line.ts +2 -0
  146. package/src/social-providers/linear.ts +8 -1
  147. package/src/social-providers/linkedin.ts +5 -3
  148. package/src/social-providers/microsoft-entra-id.ts +2 -1
  149. package/src/social-providers/naver.ts +2 -1
  150. package/src/social-providers/notion.ts +8 -1
  151. package/src/social-providers/paybin.ts +2 -0
  152. package/src/social-providers/paypal.ts +7 -1
  153. package/src/social-providers/polar.ts +8 -1
  154. package/src/social-providers/railway.ts +8 -1
  155. package/src/social-providers/reddit.ts +2 -1
  156. package/src/social-providers/roblox.ts +16 -11
  157. package/src/social-providers/salesforce.ts +8 -1
  158. package/src/social-providers/slack.ts +15 -9
  159. package/src/social-providers/spotify.ts +8 -1
  160. package/src/social-providers/tiktok.ts +22 -9
  161. package/src/social-providers/twitch.ts +2 -1
  162. package/src/social-providers/twitter.ts +1 -0
  163. package/src/social-providers/vercel.ts +8 -1
  164. package/src/social-providers/vk.ts +8 -1
  165. package/src/social-providers/wechat.ts +9 -1
  166. package/src/social-providers/zoom.ts +15 -19
  167. package/src/types/context.ts +33 -5
  168. package/src/types/init-options.ts +29 -5
  169. package/src/utils/ip.ts +12 -13
  170. package/src/utils/redirect-uri.ts +54 -0
  171. package/src/utils/string.ts +37 -0
  172. package/src/utils/url.ts +28 -0
@@ -36,14 +36,11 @@ export const dropbox = (options: DropboxOptions) => {
36
36
  scopes,
37
37
  codeVerifier,
38
38
  redirectURI,
39
+ additionalParams,
39
40
  }) => {
40
41
  const _scopes = options.disableDefaultScope ? [] : ["account_info.read"];
41
42
  if (options.scope) _scopes.push(...options.scope);
42
43
  if (scopes) _scopes.push(...scopes);
43
- const additionalParams: Record<string, string> = {};
44
- if (options.accessType) {
45
- additionalParams.token_access_type = options.accessType;
46
- }
47
44
  return await createAuthorizationURL({
48
45
  id: "dropbox",
49
46
  options,
@@ -52,7 +49,12 @@ export const dropbox = (options: DropboxOptions) => {
52
49
  state,
53
50
  redirectURI,
54
51
  codeVerifier,
55
- additionalParams,
52
+ additionalParams: {
53
+ ...(options.accessType
54
+ ? { token_access_type: options.accessType }
55
+ : {}),
56
+ ...(additionalParams ?? {}),
57
+ },
56
58
  });
57
59
  },
58
60
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -12,8 +12,8 @@ import {
12
12
  export interface FacebookProfile {
13
13
  id: string;
14
14
  name: string;
15
- email: string;
16
- email_verified: boolean;
15
+ email?: string;
16
+ email_verified?: boolean;
17
17
  picture: {
18
18
  data: {
19
19
  height: number;
@@ -43,7 +43,13 @@ export const facebook = (options: FacebookOptions) => {
43
43
  return {
44
44
  id: "facebook",
45
45
  name: "Facebook",
46
- async createAuthorizationURL({ state, scopes, redirectURI, loginHint }) {
46
+ async createAuthorizationURL({
47
+ state,
48
+ scopes,
49
+ redirectURI,
50
+ loginHint,
51
+ additionalParams,
52
+ }) {
47
53
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
48
54
  logger.error(
49
55
  "Client ID and client secret are required for Facebook. Make sure to provide them in the options.",
@@ -63,11 +69,10 @@ export const facebook = (options: FacebookOptions) => {
63
69
  state,
64
70
  redirectURI,
65
71
  loginHint,
66
- additionalParams: options.configId
67
- ? {
68
- config_id: options.configId,
69
- }
70
- : {},
72
+ additionalParams: {
73
+ ...(options.configId ? { config_id: options.configId } : {}),
74
+ ...(additionalParams ?? {}),
75
+ },
71
76
  });
72
77
  },
73
78
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -204,7 +209,7 @@ export const facebook = (options: FacebookOptions) => {
204
209
  name: profile.name,
205
210
  email: profile.email,
206
211
  image: profile.picture.data.url,
207
- emailVerified: profile.email_verified,
212
+ emailVerified: profile.email_verified ?? false,
208
213
  ...userMap,
209
214
  },
210
215
  data: profile,
@@ -24,7 +24,13 @@ export const figma = (options: FigmaOptions) => {
24
24
  return {
25
25
  id: "figma",
26
26
  name: "Figma",
27
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
27
+ async createAuthorizationURL({
28
+ state,
29
+ scopes,
30
+ codeVerifier,
31
+ redirectURI,
32
+ additionalParams,
33
+ }) {
28
34
  if (!options.clientId || !options.clientSecret) {
29
35
  logger.error(
30
36
  "Client Id and Client Secret are required for Figma. Make sure to provide them in the options.",
@@ -47,6 +53,7 @@ export const figma = (options: FigmaOptions) => {
47
53
  state,
48
54
  codeVerifier,
49
55
  redirectURI,
56
+ additionalParams,
50
57
  });
51
58
 
52
59
  return url;
@@ -6,7 +6,7 @@ import {
6
6
  getOAuth2Tokens,
7
7
  refreshAccessToken,
8
8
  } from "../oauth2";
9
- import { createAuthorizationCodeRequest } from "../oauth2/validate-authorization-code";
9
+ import { authorizationCodeRequest } from "../oauth2/validate-authorization-code";
10
10
 
11
11
  export interface GithubProfile {
12
12
  login: string;
@@ -31,7 +31,7 @@ export interface GithubProfile {
31
31
  company: string;
32
32
  blog: string;
33
33
  location: string;
34
- email: string;
34
+ email: string | null;
35
35
  hireable: boolean;
36
36
  bio: string;
37
37
  twitter_username: string;
@@ -69,6 +69,7 @@ export const github = (options: GithubOptions) => {
69
69
  loginHint,
70
70
  codeVerifier,
71
71
  redirectURI,
72
+ additionalParams,
72
73
  }) {
73
74
  const _scopes = options.disableDefaultScope
74
75
  ? []
@@ -85,10 +86,11 @@ export const github = (options: GithubOptions) => {
85
86
  redirectURI,
86
87
  loginHint,
87
88
  prompt: options.prompt,
89
+ additionalParams,
88
90
  });
89
91
  },
90
92
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
91
- const { body, headers: requestHeaders } = createAuthorizationCodeRequest({
93
+ const { body, headers: requestHeaders } = await authorizationCodeRequest({
92
94
  code,
93
95
  codeVerifier,
94
96
  redirectURI,
@@ -87,6 +87,7 @@ export const gitlab = (options: GitlabOptions) => {
87
87
  codeVerifier,
88
88
  loginHint,
89
89
  redirectURI,
90
+ additionalParams,
90
91
  }) => {
91
92
  const _scopes = options.disableDefaultScope ? [] : ["read_user"];
92
93
  if (options.scope) _scopes.push(...options.scope);
@@ -100,6 +101,7 @@ export const gitlab = (options: GitlabOptions) => {
100
101
  redirectURI,
101
102
  codeVerifier,
102
103
  loginHint,
104
+ additionalParams,
103
105
  });
104
106
  },
105
107
  validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
@@ -64,6 +64,7 @@ export const google = (options: GoogleOptions) => {
64
64
  redirectURI,
65
65
  loginHint,
66
66
  display,
67
+ additionalParams,
67
68
  }) {
68
69
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
69
70
  logger.error(
@@ -94,6 +95,7 @@ export const google = (options: GoogleOptions) => {
94
95
  hd: options.hd,
95
96
  additionalParams: {
96
97
  include_granted_scopes: "true",
98
+ ...(additionalParams ?? {}),
97
99
  },
98
100
  });
99
101
  return url;
@@ -47,7 +47,13 @@ export const huggingface = (options: HuggingFaceOptions) => {
47
47
  return {
48
48
  id: "huggingface",
49
49
  name: "Hugging Face",
50
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
50
+ createAuthorizationURL({
51
+ state,
52
+ scopes,
53
+ codeVerifier,
54
+ redirectURI,
55
+ additionalParams,
56
+ }) {
51
57
  const _scopes = options.disableDefaultScope
52
58
  ? []
53
59
  : ["openid", "profile", "email"];
@@ -61,6 +67,7 @@ export const huggingface = (options: HuggingFaceOptions) => {
61
67
  state,
62
68
  codeVerifier,
63
69
  redirectURI,
70
+ additionalParams,
64
71
  });
65
72
  },
66
73
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -106,7 +106,7 @@ export const kakao = (options: KakaoOptions) => {
106
106
  return {
107
107
  id: "kakao",
108
108
  name: "Kakao",
109
- createAuthorizationURL({ state, scopes, redirectURI }) {
109
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
110
110
  const _scopes = options.disableDefaultScope
111
111
  ? []
112
112
  : ["account_email", "profile_image", "profile_nickname"];
@@ -119,6 +119,7 @@ export const kakao = (options: KakaoOptions) => {
119
119
  scopes: _scopes,
120
120
  state,
121
121
  redirectURI,
122
+ additionalParams,
122
123
  });
123
124
  },
124
125
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -33,7 +33,13 @@ export const kick = (options: KickOptions) => {
33
33
  return {
34
34
  id: "kick",
35
35
  name: "Kick",
36
- createAuthorizationURL({ state, scopes, redirectURI, codeVerifier }) {
36
+ createAuthorizationURL({
37
+ state,
38
+ scopes,
39
+ redirectURI,
40
+ codeVerifier,
41
+ additionalParams,
42
+ }) {
37
43
  const _scopes = options.disableDefaultScope ? [] : ["user:read"];
38
44
  if (options.scope) _scopes.push(...options.scope);
39
45
  if (scopes) _scopes.push(...scopes);
@@ -46,6 +52,7 @@ export const kick = (options: KickOptions) => {
46
52
  scopes: _scopes,
47
53
  codeVerifier,
48
54
  state,
55
+ additionalParams,
49
56
  });
50
57
  },
51
58
  async validateAuthorizationCode({ code, redirectURI, codeVerifier }) {
@@ -56,6 +56,7 @@ export const line = (options: LineOptions) => {
56
56
  codeVerifier,
57
57
  redirectURI,
58
58
  loginHint,
59
+ additionalParams,
59
60
  }) {
60
61
  const _scopes = options.disableDefaultScope
61
62
  ? []
@@ -71,6 +72,7 @@ export const line = (options: LineOptions) => {
71
72
  codeVerifier,
72
73
  redirectURI,
73
74
  loginHint,
75
+ additionalParams,
74
76
  });
75
77
  },
76
78
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -31,7 +31,13 @@ export const linear = (options: LinearOptions) => {
31
31
  return {
32
32
  id: "linear",
33
33
  name: "Linear",
34
- createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
34
+ createAuthorizationURL({
35
+ state,
36
+ scopes,
37
+ loginHint,
38
+ redirectURI,
39
+ additionalParams,
40
+ }) {
35
41
  const _scopes = options.disableDefaultScope ? [] : ["read"];
36
42
  if (options.scope) _scopes.push(...options.scope);
37
43
  if (scopes) _scopes.push(...scopes);
@@ -43,6 +49,7 @@ export const linear = (options: LinearOptions) => {
43
49
  state,
44
50
  redirectURI,
45
51
  loginHint,
52
+ additionalParams,
46
53
  });
47
54
  },
48
55
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -16,8 +16,8 @@ export interface LinkedInProfile {
16
16
  country: string;
17
17
  language: string;
18
18
  };
19
- email: string;
20
- email_verified: boolean;
19
+ email?: string;
20
+ email_verified?: boolean;
21
21
  }
22
22
 
23
23
  export interface LinkedInOptions extends ProviderOptions<LinkedInProfile> {
@@ -37,6 +37,7 @@ export const linkedin = (options: LinkedInOptions) => {
37
37
  scopes,
38
38
  redirectURI,
39
39
  loginHint,
40
+ additionalParams,
40
41
  }) => {
41
42
  const _scopes = options.disableDefaultScope
42
43
  ? []
@@ -51,6 +52,7 @@ export const linkedin = (options: LinkedInOptions) => {
51
52
  state,
52
53
  loginHint,
53
54
  redirectURI,
55
+ additionalParams,
54
56
  });
55
57
  },
56
58
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -98,7 +100,7 @@ export const linkedin = (options: LinkedInOptions) => {
98
100
  id: profile.sub,
99
101
  name: profile.name,
100
102
  email: profile.email,
101
- emailVerified: profile.email_verified || false,
103
+ emailVerified: profile.email_verified ?? false,
102
104
  image: profile.picture,
103
105
  ...userMap,
104
106
  },
@@ -36,7 +36,7 @@ export interface MicrosoftEntraIDProfile extends Record<string, any> {
36
36
  /** The primary username that represents the user */
37
37
  preferred_username: string;
38
38
  /** User's email address */
39
- email: string;
39
+ email?: string;
40
40
  /** Human-readable value that identifies the subject of the token */
41
41
  name: string;
42
42
  /** Matches the parameter included in the original authorize request */
@@ -174,6 +174,7 @@ export const microsoft = (options: MicrosoftOptions) => {
174
174
  redirectURI: data.redirectURI,
175
175
  prompt: options.prompt,
176
176
  loginHint: data.loginHint,
177
+ additionalParams: data.additionalParams,
177
178
  });
178
179
  },
179
180
  validateAuthorizationCode({ code, codeVerifier, redirectURI }) {
@@ -44,7 +44,7 @@ export const naver = (options: NaverOptions) => {
44
44
  return {
45
45
  id: "naver",
46
46
  name: "Naver",
47
- createAuthorizationURL({ state, scopes, redirectURI }) {
47
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
48
48
  const _scopes = options.disableDefaultScope ? [] : ["profile", "email"];
49
49
  if (options.scope) _scopes.push(...options.scope);
50
50
  if (scopes) _scopes.push(...scopes);
@@ -55,6 +55,7 @@ export const naver = (options: NaverOptions) => {
55
55
  scopes: _scopes,
56
56
  state,
57
57
  redirectURI,
58
+ additionalParams,
58
59
  });
59
60
  },
60
61
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -28,7 +28,13 @@ export const notion = (options: NotionOptions) => {
28
28
  return {
29
29
  id: "notion",
30
30
  name: "Notion",
31
- createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
31
+ createAuthorizationURL({
32
+ state,
33
+ scopes,
34
+ loginHint,
35
+ redirectURI,
36
+ additionalParams,
37
+ }) {
32
38
  const _scopes: string[] = options.disableDefaultScope ? [] : [];
33
39
  if (options.scope) _scopes.push(...options.scope);
34
40
  if (scopes) _scopes.push(...scopes);
@@ -41,6 +47,7 @@ export const notion = (options: NotionOptions) => {
41
47
  redirectURI,
42
48
  loginHint,
43
49
  additionalParams: {
50
+ ...(additionalParams ?? {}),
44
51
  owner: "user",
45
52
  },
46
53
  });
@@ -42,6 +42,7 @@ export const paybin = (options: PaybinOptions) => {
42
42
  codeVerifier,
43
43
  redirectURI,
44
44
  loginHint,
45
+ additionalParams,
45
46
  }) {
46
47
  if (!options.clientId || !options.clientSecret) {
47
48
  logger.error(
@@ -67,6 +68,7 @@ export const paybin = (options: PaybinOptions) => {
67
68
  redirectURI,
68
69
  prompt: options.prompt,
69
70
  loginHint,
71
+ additionalParams,
70
72
  });
71
73
  return url;
72
74
  },
@@ -78,7 +78,12 @@ export const paypal = (options: PayPalOptions) => {
78
78
  return {
79
79
  id: "paypal",
80
80
  name: "PayPal",
81
- async createAuthorizationURL({ state, codeVerifier, redirectURI }) {
81
+ async createAuthorizationURL({
82
+ state,
83
+ codeVerifier,
84
+ redirectURI,
85
+ additionalParams,
86
+ }) {
82
87
  if (!options.clientId || !options.clientSecret) {
83
88
  logger.error(
84
89
  "Client Id and Client Secret is required for PayPal. Make sure to provide them in the options.",
@@ -103,6 +108,7 @@ export const paypal = (options: PayPalOptions) => {
103
108
  codeVerifier,
104
109
  redirectURI,
105
110
  prompt: options.prompt,
111
+ additionalParams,
106
112
  });
107
113
  return url;
108
114
  },
@@ -37,7 +37,13 @@ export const polar = (options: PolarOptions) => {
37
37
  return {
38
38
  id: "polar",
39
39
  name: "Polar",
40
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
40
+ createAuthorizationURL({
41
+ state,
42
+ scopes,
43
+ codeVerifier,
44
+ redirectURI,
45
+ additionalParams,
46
+ }) {
41
47
  const _scopes = options.disableDefaultScope
42
48
  ? []
43
49
  : ["openid", "profile", "email"];
@@ -52,6 +58,7 @@ export const polar = (options: PolarOptions) => {
52
58
  codeVerifier,
53
59
  redirectURI,
54
60
  prompt: options.prompt,
61
+ additionalParams,
55
62
  });
56
63
  },
57
64
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -29,7 +29,13 @@ export const railway = (options: RailwayOptions) => {
29
29
  return {
30
30
  id: "railway",
31
31
  name: "Railway",
32
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
32
+ createAuthorizationURL({
33
+ state,
34
+ scopes,
35
+ codeVerifier,
36
+ redirectURI,
37
+ additionalParams,
38
+ }) {
33
39
  const _scopes = options.disableDefaultScope
34
40
  ? []
35
41
  : ["openid", "email", "profile"];
@@ -43,6 +49,7 @@ export const railway = (options: RailwayOptions) => {
43
49
  state,
44
50
  codeVerifier,
45
51
  redirectURI,
52
+ additionalParams,
46
53
  });
47
54
  },
48
55
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -25,7 +25,7 @@ export const reddit = (options: RedditOptions) => {
25
25
  return {
26
26
  id: "reddit",
27
27
  name: "Reddit",
28
- createAuthorizationURL({ state, scopes, redirectURI }) {
28
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
29
29
  const _scopes = options.disableDefaultScope ? [] : ["identity"];
30
30
  if (options.scope) _scopes.push(...options.scope);
31
31
  if (scopes) _scopes.push(...scopes);
@@ -37,6 +37,7 @@ export const reddit = (options: RedditOptions) => {
37
37
  state,
38
38
  redirectURI,
39
39
  duration: options.duration,
40
+ additionalParams,
40
41
  });
41
42
  },
42
43
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -1,6 +1,10 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
2
  import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
- import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
3
+ import {
4
+ createAuthorizationURL,
5
+ refreshAccessToken,
6
+ validateAuthorizationCode,
7
+ } from "../oauth2";
4
8
 
5
9
  export interface RobloxProfile extends Record<string, any> {
6
10
  /** the user's id */
@@ -37,19 +41,20 @@ export const roblox = (options: RobloxOptions) => {
37
41
  return {
38
42
  id: "roblox",
39
43
  name: "Roblox",
40
- createAuthorizationURL({ state, scopes, redirectURI }) {
44
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
41
45
  const _scopes = options.disableDefaultScope ? [] : ["openid", "profile"];
42
46
  if (options.scope) _scopes.push(...options.scope);
43
47
  if (scopes) _scopes.push(...scopes);
44
- return new URL(
45
- `https://apis.roblox.com/oauth/v1/authorize?scope=${_scopes.join(
46
- "+",
47
- )}&response_type=code&client_id=${
48
- options.clientId
49
- }&redirect_uri=${encodeURIComponent(
50
- options.redirectURI || redirectURI,
51
- )}&state=${state}&prompt=${options.prompt || "select_account consent"}`,
52
- );
48
+ return createAuthorizationURL({
49
+ id: "roblox",
50
+ options,
51
+ authorizationEndpoint: "https://apis.roblox.com/oauth/v1/authorize",
52
+ scopes: _scopes,
53
+ state,
54
+ redirectURI,
55
+ prompt: options.prompt || "select_account consent",
56
+ additionalParams,
57
+ });
53
58
  },
54
59
  validateAuthorizationCode: async ({ code, redirectURI }) => {
55
60
  return validateAuthorizationCode({
@@ -64,7 +64,13 @@ export const salesforce = (options: SalesforceOptions) => {
64
64
  id: "salesforce",
65
65
  name: "Salesforce",
66
66
 
67
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
67
+ async createAuthorizationURL({
68
+ state,
69
+ scopes,
70
+ codeVerifier,
71
+ redirectURI,
72
+ additionalParams,
73
+ }) {
68
74
  if (!options.clientId || !options.clientSecret) {
69
75
  logger.error(
70
76
  "Client Id and Client Secret are required for Salesforce. Make sure to provide them in the options.",
@@ -89,6 +95,7 @@ export const salesforce = (options: SalesforceOptions) => {
89
95
  state,
90
96
  codeVerifier,
91
97
  redirectURI: options.redirectURI || redirectURI,
98
+ additionalParams,
92
99
  });
93
100
  },
94
101
 
@@ -1,6 +1,10 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
2
  import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
- import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
3
+ import {
4
+ createAuthorizationURL,
5
+ refreshAccessToken,
6
+ validateAuthorizationCode,
7
+ } from "../oauth2";
4
8
 
5
9
  export interface SlackProfile extends Record<string, any> {
6
10
  ok: boolean;
@@ -42,19 +46,21 @@ export const slack = (options: SlackOptions) => {
42
46
  return {
43
47
  id: "slack",
44
48
  name: "Slack",
45
- createAuthorizationURL({ state, scopes, redirectURI }) {
49
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
46
50
  const _scopes = options.disableDefaultScope
47
51
  ? []
48
52
  : ["openid", "profile", "email"];
49
53
  if (scopes) _scopes.push(...scopes);
50
54
  if (options.scope) _scopes.push(...options.scope);
51
- const url = new URL("https://slack.com/openid/connect/authorize");
52
- url.searchParams.set("scope", _scopes.join(" "));
53
- url.searchParams.set("response_type", "code");
54
- url.searchParams.set("client_id", options.clientId);
55
- url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
56
- url.searchParams.set("state", state);
57
- return url;
55
+ return createAuthorizationURL({
56
+ id: "slack",
57
+ options,
58
+ authorizationEndpoint: "https://slack.com/openid/connect/authorize",
59
+ scopes: _scopes,
60
+ state,
61
+ redirectURI,
62
+ additionalParams,
63
+ });
58
64
  },
59
65
  validateAuthorizationCode: async ({ code, redirectURI }) => {
60
66
  return validateAuthorizationCode({
@@ -24,7 +24,13 @@ export const spotify = (options: SpotifyOptions) => {
24
24
  return {
25
25
  id: "spotify",
26
26
  name: "Spotify",
27
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
27
+ createAuthorizationURL({
28
+ state,
29
+ scopes,
30
+ codeVerifier,
31
+ redirectURI,
32
+ additionalParams,
33
+ }) {
28
34
  const _scopes = options.disableDefaultScope ? [] : ["user-read-email"];
29
35
  if (options.scope) _scopes.push(...options.scope);
30
36
  if (scopes) _scopes.push(...scopes);
@@ -36,6 +42,7 @@ export const spotify = (options: SpotifyOptions) => {
36
42
  state,
37
43
  codeVerifier,
38
44
  redirectURI,
45
+ additionalParams,
39
46
  });
40
47
  },
41
48
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -1,6 +1,10 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
2
  import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
- import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
3
+ import {
4
+ RESERVED_AUTHORIZATION_PARAMS_SET,
5
+ refreshAccessToken,
6
+ validateAuthorizationCode,
7
+ } from "../oauth2";
4
8
 
5
9
  /**
6
10
  * [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)
@@ -131,17 +135,26 @@ export const tiktok = (options: TiktokOptions) => {
131
135
  return {
132
136
  id: "tiktok",
133
137
  name: "TikTok",
134
- createAuthorizationURL({ state, scopes, redirectURI }) {
138
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
135
139
  const _scopes = options.disableDefaultScope ? [] : ["user.info.profile"];
136
140
  if (options.scope) _scopes.push(...options.scope);
137
141
  if (scopes) _scopes.push(...scopes);
138
- return new URL(
139
- `https://www.tiktok.com/v2/auth/authorize?scope=${_scopes.join(
140
- ",",
141
- )}&response_type=code&client_key=${options.clientKey}&redirect_uri=${encodeURIComponent(
142
- options.redirectURI || redirectURI,
143
- )}&state=${state}`,
144
- );
142
+ // TikTok uses `client_key` instead of the standard `client_id`, so the
143
+ // shared createAuthorizationURL helper cannot be used directly.
144
+ const url = new URL("https://www.tiktok.com/v2/auth/authorize");
145
+ url.searchParams.set("scope", _scopes.join(","));
146
+ url.searchParams.set("response_type", "code");
147
+ url.searchParams.set("client_key", options.clientKey);
148
+ url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
149
+ url.searchParams.set("state", state);
150
+ if (additionalParams) {
151
+ for (const [key, value] of Object.entries(additionalParams)) {
152
+ if (RESERVED_AUTHORIZATION_PARAMS_SET.has(key)) continue;
153
+ if (key === "client_key") continue;
154
+ url.searchParams.set(key, value);
155
+ }
156
+ }
157
+ return url;
145
158
  },
146
159
 
147
160
  validateAuthorizationCode: async ({ code, redirectURI }) => {