@better-auth/core 1.7.0-beta.3 → 1.7.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/dist/api/index.d.mts +3 -3
  2. package/dist/context/global.mjs +1 -1
  3. package/dist/db/adapter/factory.mjs +62 -0
  4. package/dist/db/adapter/index.d.mts +35 -1
  5. package/dist/db/adapter/types.d.mts +1 -1
  6. package/dist/db/get-tables.mjs +3 -3
  7. package/dist/db/schema/account.d.mts +1 -1
  8. package/dist/db/schema/account.mjs +1 -1
  9. package/dist/db/type.d.mts +12 -0
  10. package/dist/env/env-impl.mjs +1 -1
  11. package/dist/error/codes.d.mts +6 -0
  12. package/dist/error/codes.mjs +6 -0
  13. package/dist/index.d.mts +2 -2
  14. package/dist/instrumentation/tracer.mjs +1 -1
  15. package/dist/oauth2/authorization-params.d.mts +12 -0
  16. package/dist/oauth2/authorization-params.mjs +12 -0
  17. package/dist/oauth2/basic-credentials.d.mts +30 -0
  18. package/dist/oauth2/basic-credentials.mjs +64 -0
  19. package/dist/oauth2/client-assertion.d.mts +38 -22
  20. package/dist/oauth2/client-assertion.mjs +63 -28
  21. package/dist/oauth2/client-credentials-token.d.mts +19 -40
  22. package/dist/oauth2/client-credentials-token.mjs +18 -29
  23. package/dist/oauth2/create-authorization-url.d.mts +13 -2
  24. package/dist/oauth2/create-authorization-url.mjs +28 -7
  25. package/dist/oauth2/index.d.mts +13 -8
  26. package/dist/oauth2/index.mjs +11 -7
  27. package/dist/oauth2/oauth-provider.d.mts +149 -11
  28. package/dist/oauth2/refresh-access-token.d.mts +20 -40
  29. package/dist/oauth2/refresh-access-token.mjs +20 -33
  30. package/dist/oauth2/scopes.d.mts +76 -0
  31. package/dist/oauth2/scopes.mjs +96 -0
  32. package/dist/oauth2/token-endpoint-auth.d.mts +17 -0
  33. package/dist/oauth2/token-endpoint-auth.mjs +89 -0
  34. package/dist/oauth2/utils.d.mts +9 -1
  35. package/dist/oauth2/utils.mjs +14 -2
  36. package/dist/oauth2/validate-authorization-code.d.mts +17 -52
  37. package/dist/oauth2/validate-authorization-code.mjs +17 -30
  38. package/dist/oauth2/verify-id-token.d.mts +26 -0
  39. package/dist/oauth2/verify-id-token.mjs +62 -0
  40. package/dist/oauth2/verify.d.mts +14 -0
  41. package/dist/oauth2/verify.mjs +38 -12
  42. package/dist/social-providers/apple.d.mts +18 -20
  43. package/dist/social-providers/apple.mjs +15 -28
  44. package/dist/social-providers/atlassian.d.mts +8 -2
  45. package/dist/social-providers/atlassian.mjs +9 -6
  46. package/dist/social-providers/cognito.d.mts +29 -3
  47. package/dist/social-providers/cognito.mjs +30 -34
  48. package/dist/social-providers/discord.d.mts +8 -2
  49. package/dist/social-providers/discord.mjs +20 -6
  50. package/dist/social-providers/dropbox.d.mts +8 -2
  51. package/dist/social-providers/dropbox.mjs +10 -9
  52. package/dist/social-providers/facebook.d.mts +24 -3
  53. package/dist/social-providers/facebook.mjs +51 -24
  54. package/dist/social-providers/figma.d.mts +8 -2
  55. package/dist/social-providers/figma.mjs +8 -7
  56. package/dist/social-providers/github.d.mts +8 -2
  57. package/dist/social-providers/github.mjs +9 -8
  58. package/dist/social-providers/gitlab.d.mts +8 -2
  59. package/dist/social-providers/gitlab.mjs +8 -7
  60. package/dist/social-providers/google.d.mts +32 -4
  61. package/dist/social-providers/google.mjs +26 -29
  62. package/dist/social-providers/huggingface.d.mts +8 -2
  63. package/dist/social-providers/huggingface.mjs +11 -10
  64. package/dist/social-providers/index.d.mts +322 -75
  65. package/dist/social-providers/kakao.d.mts +8 -2
  66. package/dist/social-providers/kakao.mjs +11 -10
  67. package/dist/social-providers/kick.d.mts +8 -2
  68. package/dist/social-providers/kick.mjs +7 -6
  69. package/dist/social-providers/line.d.mts +11 -3
  70. package/dist/social-providers/line.mjs +14 -15
  71. package/dist/social-providers/linear.d.mts +8 -2
  72. package/dist/social-providers/linear.mjs +7 -6
  73. package/dist/social-providers/linkedin.d.mts +8 -2
  74. package/dist/social-providers/linkedin.mjs +12 -11
  75. package/dist/social-providers/microsoft-entra-id.d.mts +33 -7
  76. package/dist/social-providers/microsoft-entra-id.mjs +28 -38
  77. package/dist/social-providers/naver.d.mts +8 -2
  78. package/dist/social-providers/naver.mjs +7 -6
  79. package/dist/social-providers/notion.d.mts +8 -2
  80. package/dist/social-providers/notion.mjs +9 -6
  81. package/dist/social-providers/paybin.d.mts +8 -2
  82. package/dist/social-providers/paybin.mjs +12 -11
  83. package/dist/social-providers/paypal.d.mts +8 -3
  84. package/dist/social-providers/paypal.mjs +10 -14
  85. package/dist/social-providers/polar.d.mts +8 -2
  86. package/dist/social-providers/polar.mjs +11 -10
  87. package/dist/social-providers/railway.d.mts +8 -2
  88. package/dist/social-providers/railway.mjs +11 -10
  89. package/dist/social-providers/reddit.d.mts +8 -2
  90. package/dist/social-providers/reddit.mjs +11 -9
  91. package/dist/social-providers/roblox.d.mts +8 -2
  92. package/dist/social-providers/roblox.mjs +15 -5
  93. package/dist/social-providers/salesforce.d.mts +8 -2
  94. package/dist/social-providers/salesforce.mjs +11 -10
  95. package/dist/social-providers/slack.d.mts +8 -2
  96. package/dist/social-providers/slack.mjs +18 -15
  97. package/dist/social-providers/spotify.d.mts +8 -2
  98. package/dist/social-providers/spotify.mjs +7 -6
  99. package/dist/social-providers/tiktok.d.mts +8 -2
  100. package/dist/social-providers/tiktok.mjs +21 -5
  101. package/dist/social-providers/twitch.d.mts +8 -2
  102. package/dist/social-providers/twitch.mjs +7 -6
  103. package/dist/social-providers/twitter.d.mts +7 -2
  104. package/dist/social-providers/twitter.mjs +11 -10
  105. package/dist/social-providers/vercel.d.mts +8 -2
  106. package/dist/social-providers/vercel.mjs +7 -9
  107. package/dist/social-providers/vk.d.mts +8 -2
  108. package/dist/social-providers/vk.mjs +7 -6
  109. package/dist/social-providers/wechat.d.mts +8 -2
  110. package/dist/social-providers/wechat.mjs +16 -6
  111. package/dist/social-providers/zoom.d.mts +10 -3
  112. package/dist/social-providers/zoom.mjs +14 -15
  113. package/dist/types/context.d.mts +33 -11
  114. package/dist/types/index.d.mts +1 -1
  115. package/dist/types/init-options.d.mts +121 -6
  116. package/dist/utils/ip.d.mts +5 -4
  117. package/dist/utils/ip.mjs +3 -3
  118. package/dist/utils/redirect-uri.d.mts +20 -0
  119. package/dist/utils/redirect-uri.mjs +48 -0
  120. package/dist/utils/string.d.mts +5 -1
  121. package/dist/utils/string.mjs +20 -1
  122. package/dist/utils/url.d.mts +18 -1
  123. package/dist/utils/url.mjs +30 -1
  124. package/package.json +13 -12
  125. package/src/db/adapter/factory.ts +126 -0
  126. package/src/db/adapter/index.ts +32 -0
  127. package/src/db/adapter/types.ts +1 -0
  128. package/src/db/get-tables.ts +8 -3
  129. package/src/db/schema/account.ts +14 -2
  130. package/src/db/type.ts +12 -0
  131. package/src/env/env-impl.ts +1 -2
  132. package/src/error/codes.ts +6 -0
  133. package/src/oauth2/authorization-params.ts +28 -0
  134. package/src/oauth2/basic-credentials.ts +87 -0
  135. package/src/oauth2/client-assertion.ts +131 -58
  136. package/src/oauth2/client-credentials-token.ts +48 -72
  137. package/src/oauth2/create-authorization-url.ts +30 -8
  138. package/src/oauth2/index.ts +42 -10
  139. package/src/oauth2/oauth-provider.ts +161 -12
  140. package/src/oauth2/refresh-access-token.ts +52 -78
  141. package/src/oauth2/scopes.ts +118 -0
  142. package/src/oauth2/token-endpoint-auth.ts +221 -0
  143. package/src/oauth2/utils.ts +21 -5
  144. package/src/oauth2/validate-authorization-code.ts +55 -85
  145. package/src/oauth2/verify-id-token.ts +111 -0
  146. package/src/oauth2/verify.ts +82 -15
  147. package/src/social-providers/apple.ts +32 -45
  148. package/src/social-providers/atlassian.ts +20 -9
  149. package/src/social-providers/cognito.ts +51 -48
  150. package/src/social-providers/discord.ts +37 -22
  151. package/src/social-providers/dropbox.ts +20 -12
  152. package/src/social-providers/facebook.ts +108 -57
  153. package/src/social-providers/figma.ts +21 -10
  154. package/src/social-providers/github.ts +16 -10
  155. package/src/social-providers/gitlab.ts +16 -8
  156. package/src/social-providers/google.ts +67 -46
  157. package/src/social-providers/huggingface.ts +20 -9
  158. package/src/social-providers/kakao.ts +18 -9
  159. package/src/social-providers/kick.ts +20 -8
  160. package/src/social-providers/line.ts +39 -37
  161. package/src/social-providers/linear.ts +20 -7
  162. package/src/social-providers/linkedin.ts +16 -10
  163. package/src/social-providers/microsoft-entra-id.ts +66 -64
  164. package/src/social-providers/naver.ts +14 -7
  165. package/src/social-providers/notion.ts +20 -7
  166. package/src/social-providers/paybin.ts +16 -11
  167. package/src/social-providers/paypal.ts +12 -25
  168. package/src/social-providers/polar.ts +20 -9
  169. package/src/social-providers/railway.ts +20 -9
  170. package/src/social-providers/reddit.ts +22 -10
  171. package/src/social-providers/roblox.ts +31 -15
  172. package/src/social-providers/salesforce.ts +21 -10
  173. package/src/social-providers/slack.ts +31 -16
  174. package/src/social-providers/spotify.ts +20 -7
  175. package/src/social-providers/tiktok.ts +32 -13
  176. package/src/social-providers/twitch.ts +14 -9
  177. package/src/social-providers/twitter.ts +18 -8
  178. package/src/social-providers/vercel.ts +24 -11
  179. package/src/social-providers/vk.ts +20 -7
  180. package/src/social-providers/wechat.ts +28 -8
  181. package/src/social-providers/zoom.ts +28 -19
  182. package/src/types/context.ts +33 -12
  183. package/src/types/index.ts +7 -0
  184. package/src/types/init-options.ts +148 -5
  185. package/src/utils/ip.ts +12 -13
  186. package/src/utils/redirect-uri.ts +54 -0
  187. package/src/utils/string.ts +37 -0
  188. package/src/utils/url.ts +28 -0
@@ -1,10 +1,11 @@
1
1
  import { decodeJwt } from "jose";
2
2
  import { logger } from "../env";
3
3
  import { BetterAuthError } from "../error";
4
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
4
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
5
5
  import {
6
6
  createAuthorizationURL,
7
7
  refreshAccessToken,
8
+ resolveRequestedScopes,
8
9
  validateAuthorizationCode,
9
10
  } from "../oauth2";
10
11
 
@@ -28,6 +29,8 @@ export interface PaybinOptions extends ProviderOptions<PaybinProfile> {
28
29
  issuer?: string | undefined;
29
30
  }
30
31
 
32
+ const PAYBIN_DEFAULT_SCOPES = ["openid", "email", "profile"];
33
+
31
34
  export const paybin = (options: PaybinOptions) => {
32
35
  const issuer = options.issuer || "https://idp.paybin.io";
33
36
  const authorizationEndpoint = `${issuer}/oauth2/authorize`;
@@ -36,12 +39,14 @@ export const paybin = (options: PaybinOptions) => {
36
39
  return {
37
40
  id: "paybin",
38
41
  name: "Paybin",
39
- async createAuthorizationURL({
42
+ callbackPath: "/callback/paybin",
43
+ createAuthorizationURL({
40
44
  state,
41
45
  scopes,
42
46
  codeVerifier,
43
47
  redirectURI,
44
48
  loginHint,
49
+ additionalParams,
45
50
  }) {
46
51
  if (!options.clientId || !options.clientSecret) {
47
52
  logger.error(
@@ -52,23 +57,23 @@ export const paybin = (options: PaybinOptions) => {
52
57
  if (!codeVerifier) {
53
58
  throw new BetterAuthError("codeVerifier is required for Paybin");
54
59
  }
55
- const _scopes = options.disableDefaultScope
56
- ? []
57
- : ["openid", "email", "profile"];
58
- if (options.scope) _scopes.push(...options.scope);
59
- if (scopes) _scopes.push(...scopes);
60
- const url = await createAuthorizationURL({
60
+ const requestedScopes = resolveRequestedScopes(
61
+ options,
62
+ PAYBIN_DEFAULT_SCOPES,
63
+ scopes,
64
+ );
65
+ return createAuthorizationURL({
61
66
  id: "paybin",
62
67
  options,
63
68
  authorizationEndpoint,
64
- scopes: _scopes,
69
+ scopes: requestedScopes,
65
70
  state,
66
71
  codeVerifier,
67
72
  redirectURI,
68
73
  prompt: options.prompt,
69
74
  loginHint,
75
+ additionalParams,
70
76
  });
71
- return url;
72
77
  },
73
78
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
74
79
  return validateAuthorizationCode({
@@ -114,5 +119,5 @@ export const paybin = (options: PaybinOptions) => {
114
119
  };
115
120
  },
116
121
  options,
117
- } satisfies OAuthProvider<PaybinProfile>;
122
+ } satisfies UpstreamProvider<PaybinProfile>;
118
123
  };
@@ -1,9 +1,8 @@
1
1
  import { base64 } from "@better-auth/utils/base64";
2
2
  import { betterFetch } from "@better-fetch/fetch";
3
- import { decodeJwt } from "jose";
4
3
  import { logger } from "../env";
5
4
  import { BetterAuthError } from "../error";
6
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
5
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
7
6
  import { createAuthorizationURL } from "../oauth2";
8
7
 
9
8
  export interface PayPalProfile {
@@ -78,7 +77,13 @@ export const paypal = (options: PayPalOptions) => {
78
77
  return {
79
78
  id: "paypal",
80
79
  name: "PayPal",
81
- async createAuthorizationURL({ state, codeVerifier, redirectURI }) {
80
+ callbackPath: "/callback/paypal",
81
+ 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.",
@@ -92,19 +97,17 @@ export const paypal = (options: PayPalOptions) => {
92
97
  * We don't pass any scopes to avoid "invalid scope" errors
93
98
  **/
94
99
 
95
- const _scopes: string[] = [];
96
-
97
- const url = await createAuthorizationURL({
100
+ return createAuthorizationURL({
98
101
  id: "paypal",
99
102
  options,
100
103
  authorizationEndpoint,
101
- scopes: _scopes,
104
+ scopes: [],
102
105
  state,
103
106
  codeVerifier,
104
107
  redirectURI,
105
108
  prompt: options.prompt,
109
+ additionalParams,
106
110
  });
107
- return url;
108
111
  },
109
112
 
110
113
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -194,22 +197,6 @@ export const paypal = (options: PayPalOptions) => {
194
197
  }
195
198
  },
196
199
 
197
- async verifyIdToken(token, nonce) {
198
- if (options.disableIdTokenSignIn) {
199
- return false;
200
- }
201
- if (options.verifyIdToken) {
202
- return options.verifyIdToken(token, nonce);
203
- }
204
- try {
205
- const payload = decodeJwt(token);
206
- return !!payload.sub;
207
- } catch (error) {
208
- logger.error("Failed to verify PayPal ID token:", error);
209
- return false;
210
- }
211
- },
212
-
213
200
  async getUserInfo(token) {
214
201
  if (options.getUserInfo) {
215
202
  return options.getUserInfo(token);
@@ -259,5 +246,5 @@ export const paypal = (options: PayPalOptions) => {
259
246
  },
260
247
 
261
248
  options,
262
- } satisfies OAuthProvider<PayPalProfile>;
249
+ } satisfies UpstreamProvider<PayPalProfile>;
263
250
  };
@@ -1,8 +1,9 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
2
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
3
3
  import {
4
4
  createAuthorizationURL,
5
5
  refreshAccessToken,
6
+ resolveRequestedScopes,
6
7
  validateAuthorizationCode,
7
8
  } from "../oauth2";
8
9
 
@@ -32,26 +33,36 @@ export interface PolarProfile {
32
33
 
33
34
  export interface PolarOptions extends ProviderOptions<PolarProfile> {}
34
35
 
36
+ const POLAR_DEFAULT_SCOPES = ["openid", "profile", "email"];
37
+
35
38
  export const polar = (options: PolarOptions) => {
36
39
  const tokenEndpoint = "https://api.polar.sh/v1/oauth2/token";
37
40
  return {
38
41
  id: "polar",
39
42
  name: "Polar",
40
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
41
- const _scopes = options.disableDefaultScope
42
- ? []
43
- : ["openid", "profile", "email"];
44
- if (options.scope) _scopes.push(...options.scope);
45
- if (scopes) _scopes.push(...scopes);
43
+ callbackPath: "/callback/polar",
44
+ createAuthorizationURL({
45
+ state,
46
+ scopes,
47
+ codeVerifier,
48
+ redirectURI,
49
+ additionalParams,
50
+ }) {
51
+ const requestedScopes = resolveRequestedScopes(
52
+ options,
53
+ POLAR_DEFAULT_SCOPES,
54
+ scopes,
55
+ );
46
56
  return createAuthorizationURL({
47
57
  id: "polar",
48
58
  options,
49
59
  authorizationEndpoint: "https://polar.sh/oauth2/authorize",
50
- scopes: _scopes,
60
+ scopes: requestedScopes,
51
61
  state,
52
62
  codeVerifier,
53
63
  redirectURI,
54
64
  prompt: options.prompt,
65
+ additionalParams,
55
66
  });
56
67
  },
57
68
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -107,5 +118,5 @@ export const polar = (options: PolarOptions) => {
107
118
  };
108
119
  },
109
120
  options,
110
- } satisfies OAuthProvider<PolarProfile>;
121
+ } satisfies UpstreamProvider<PolarProfile>;
111
122
  };
@@ -1,8 +1,9 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
2
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
3
3
  import {
4
4
  createAuthorizationURL,
5
5
  refreshAccessToken,
6
+ resolveRequestedScopes,
6
7
  validateAuthorizationCode,
7
8
  } from "../oauth2";
8
9
 
@@ -25,24 +26,34 @@ export interface RailwayOptions extends ProviderOptions<RailwayProfile> {
25
26
  clientId: string;
26
27
  }
27
28
 
29
+ const RAILWAY_DEFAULT_SCOPES = ["openid", "email", "profile"];
30
+
28
31
  export const railway = (options: RailwayOptions) => {
29
32
  return {
30
33
  id: "railway",
31
34
  name: "Railway",
32
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
33
- const _scopes = options.disableDefaultScope
34
- ? []
35
- : ["openid", "email", "profile"];
36
- if (options.scope) _scopes.push(...options.scope);
37
- if (scopes) _scopes.push(...scopes);
35
+ callbackPath: "/callback/railway",
36
+ async createAuthorizationURL({
37
+ state,
38
+ scopes,
39
+ codeVerifier,
40
+ redirectURI,
41
+ additionalParams,
42
+ }) {
43
+ const requestedScopes = resolveRequestedScopes(
44
+ options,
45
+ RAILWAY_DEFAULT_SCOPES,
46
+ scopes,
47
+ );
38
48
  return createAuthorizationURL({
39
49
  id: "railway",
40
50
  options,
41
51
  authorizationEndpoint,
42
- scopes: _scopes,
52
+ scopes: requestedScopes,
43
53
  state,
44
54
  codeVerifier,
45
55
  redirectURI,
56
+ additionalParams,
46
57
  });
47
58
  },
48
59
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -96,5 +107,5 @@ export const railway = (options: RailwayOptions) => {
96
107
  };
97
108
  },
98
109
  options,
99
- } satisfies OAuthProvider<RailwayProfile>;
110
+ } satisfies UpstreamProvider<RailwayProfile>;
100
111
  };
@@ -1,10 +1,11 @@
1
1
  import { base64 } from "@better-auth/utils/base64";
2
2
  import { betterFetch } from "@better-fetch/fetch";
3
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
4
4
  import {
5
5
  createAuthorizationURL,
6
6
  getOAuth2Tokens,
7
7
  refreshAccessToken,
8
+ resolveRequestedScopes,
8
9
  } from "../oauth2";
9
10
 
10
11
  export interface RedditProfile {
@@ -21,22 +22,33 @@ export interface RedditOptions extends ProviderOptions<RedditProfile> {
21
22
  duration?: string | undefined;
22
23
  }
23
24
 
25
+ const REDDIT_DEFAULT_SCOPES = ["identity"];
26
+
24
27
  export const reddit = (options: RedditOptions) => {
25
28
  return {
26
29
  id: "reddit",
27
30
  name: "Reddit",
28
- createAuthorizationURL({ state, scopes, redirectURI }) {
29
- const _scopes = options.disableDefaultScope ? [] : ["identity"];
30
- if (options.scope) _scopes.push(...options.scope);
31
- if (scopes) _scopes.push(...scopes);
31
+ callbackPath: "/callback/reddit",
32
+ async createAuthorizationURL({
33
+ state,
34
+ scopes,
35
+ redirectURI,
36
+ additionalParams,
37
+ }) {
38
+ const requestedScopes = resolveRequestedScopes(
39
+ options,
40
+ REDDIT_DEFAULT_SCOPES,
41
+ scopes,
42
+ );
32
43
  return createAuthorizationURL({
33
44
  id: "reddit",
34
45
  options,
35
46
  authorizationEndpoint: "https://www.reddit.com/api/v1/authorize",
36
- scopes: _scopes,
47
+ scopes: requestedScopes,
37
48
  state,
38
49
  redirectURI,
39
50
  duration: options.duration,
51
+ additionalParams,
40
52
  });
41
53
  },
42
54
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -104,19 +116,19 @@ export const reddit = (options: RedditOptions) => {
104
116
  }
105
117
 
106
118
  const userMap = await options.mapProfileToUser?.(profile);
107
-
119
+ const email = userMap?.email || `${profile.id}@reddit.com`;
108
120
  return {
109
121
  user: {
110
122
  id: profile.id,
111
123
  name: profile.name,
112
- email: profile.oauth_client_id,
113
- emailVerified: profile.has_verified_email,
114
124
  image: profile.icon_img?.split("?")[0]!,
115
125
  ...userMap,
126
+ email,
127
+ emailVerified: userMap?.emailVerified ?? false,
116
128
  },
117
129
  data: profile,
118
130
  };
119
131
  },
120
132
  options,
121
- } satisfies OAuthProvider<RedditProfile>;
133
+ } satisfies UpstreamProvider<RedditProfile>;
122
134
  };
@@ -1,6 +1,11 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
- import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
2
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
3
+ import {
4
+ createAuthorizationURL,
5
+ refreshAccessToken,
6
+ resolveRequestedScopes,
7
+ validateAuthorizationCode,
8
+ } from "../oauth2";
4
9
 
5
10
  export interface RobloxProfile extends Record<string, any> {
6
11
  /** the user's id */
@@ -32,24 +37,35 @@ export interface RobloxOptions extends ProviderOptions<RobloxProfile> {
32
37
  | undefined;
33
38
  }
34
39
 
40
+ const ROBLOX_DEFAULT_SCOPES = ["openid", "profile"];
41
+
35
42
  export const roblox = (options: RobloxOptions) => {
36
43
  const tokenEndpoint = "https://apis.roblox.com/oauth/v1/token";
37
44
  return {
38
45
  id: "roblox",
39
46
  name: "Roblox",
40
- createAuthorizationURL({ state, scopes, redirectURI }) {
41
- const _scopes = options.disableDefaultScope ? [] : ["openid", "profile"];
42
- if (options.scope) _scopes.push(...options.scope);
43
- 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"}`,
47
+ callbackPath: "/callback/roblox",
48
+ async createAuthorizationURL({
49
+ state,
50
+ scopes,
51
+ redirectURI,
52
+ additionalParams,
53
+ }) {
54
+ const requestedScopes = resolveRequestedScopes(
55
+ options,
56
+ ROBLOX_DEFAULT_SCOPES,
57
+ scopes,
52
58
  );
59
+ return createAuthorizationURL({
60
+ id: "roblox",
61
+ options,
62
+ authorizationEndpoint: "https://apis.roblox.com/oauth/v1/authorize",
63
+ scopes: requestedScopes,
64
+ state,
65
+ redirectURI,
66
+ prompt: options.prompt || "select_account consent",
67
+ additionalParams,
68
+ });
53
69
  },
54
70
  validateAuthorizationCode: async ({ code, redirectURI }) => {
55
71
  return validateAuthorizationCode({
@@ -108,5 +124,5 @@ export const roblox = (options: RobloxOptions) => {
108
124
  };
109
125
  },
110
126
  options,
111
- } satisfies OAuthProvider<RobloxProfile>;
127
+ } satisfies UpstreamProvider<RobloxProfile>;
112
128
  };
@@ -1,10 +1,11 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
2
  import { logger } from "../env";
3
3
  import { BetterAuthError } from "../error";
4
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
4
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
5
5
  import {
6
6
  createAuthorizationURL,
7
7
  refreshAccessToken,
8
+ resolveRequestedScopes,
8
9
  validateAuthorizationCode,
9
10
  } from "../oauth2";
10
11
 
@@ -39,6 +40,8 @@ export interface SalesforceOptions extends ProviderOptions<SalesforceProfile> {
39
40
  redirectURI?: string | undefined;
40
41
  }
41
42
 
43
+ const SALESFORCE_DEFAULT_SCOPES = ["openid", "email", "profile"];
44
+
42
45
  export const salesforce = (options: SalesforceOptions) => {
43
46
  const environment = options.environment ?? "production";
44
47
  const isSandbox = environment === "sandbox";
@@ -63,8 +66,15 @@ export const salesforce = (options: SalesforceOptions) => {
63
66
  return {
64
67
  id: "salesforce",
65
68
  name: "Salesforce",
66
-
67
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
69
+ callbackPath: "/callback/salesforce",
70
+
71
+ async createAuthorizationURL({
72
+ state,
73
+ scopes,
74
+ codeVerifier,
75
+ redirectURI,
76
+ additionalParams,
77
+ }) {
68
78
  if (!options.clientId || !options.clientSecret) {
69
79
  logger.error(
70
80
  "Client Id and Client Secret are required for Salesforce. Make sure to provide them in the options.",
@@ -75,20 +85,21 @@ export const salesforce = (options: SalesforceOptions) => {
75
85
  throw new BetterAuthError("codeVerifier is required for Salesforce");
76
86
  }
77
87
 
78
- const _scopes = options.disableDefaultScope
79
- ? []
80
- : ["openid", "email", "profile"];
81
- if (options.scope) _scopes.push(...options.scope);
82
- if (scopes) _scopes.push(...scopes);
88
+ const requestedScopes = resolveRequestedScopes(
89
+ options,
90
+ SALESFORCE_DEFAULT_SCOPES,
91
+ scopes,
92
+ );
83
93
 
84
94
  return createAuthorizationURL({
85
95
  id: "salesforce",
86
96
  options,
87
97
  authorizationEndpoint,
88
- scopes: _scopes,
98
+ scopes: requestedScopes,
89
99
  state,
90
100
  codeVerifier,
91
101
  redirectURI: options.redirectURI || redirectURI,
102
+ additionalParams,
92
103
  });
93
104
  },
94
105
 
@@ -155,5 +166,5 @@ export const salesforce = (options: SalesforceOptions) => {
155
166
  },
156
167
 
157
168
  options,
158
- } satisfies OAuthProvider<SalesforceProfile>;
169
+ } satisfies UpstreamProvider<SalesforceProfile>;
159
170
  };
@@ -1,6 +1,11 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
- import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
2
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
3
+ import {
4
+ createAuthorizationURL,
5
+ refreshAccessToken,
6
+ resolveRequestedScopes,
7
+ validateAuthorizationCode,
8
+ } from "../oauth2";
4
9
 
5
10
  export interface SlackProfile extends Record<string, any> {
6
11
  ok: boolean;
@@ -37,24 +42,34 @@ export interface SlackOptions extends ProviderOptions<SlackProfile> {
37
42
  clientId: string;
38
43
  }
39
44
 
45
+ const SLACK_DEFAULT_SCOPES = ["openid", "profile", "email"];
46
+
40
47
  export const slack = (options: SlackOptions) => {
41
48
  const tokenEndpoint = "https://slack.com/api/openid.connect.token";
42
49
  return {
43
50
  id: "slack",
44
51
  name: "Slack",
45
- createAuthorizationURL({ state, scopes, redirectURI }) {
46
- const _scopes = options.disableDefaultScope
47
- ? []
48
- : ["openid", "profile", "email"];
49
- if (scopes) _scopes.push(...scopes);
50
- 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;
52
+ callbackPath: "/callback/slack",
53
+ async createAuthorizationURL({
54
+ state,
55
+ scopes,
56
+ redirectURI,
57
+ additionalParams,
58
+ }) {
59
+ const requestedScopes = resolveRequestedScopes(
60
+ options,
61
+ SLACK_DEFAULT_SCOPES,
62
+ scopes,
63
+ );
64
+ return createAuthorizationURL({
65
+ id: "slack",
66
+ options,
67
+ authorizationEndpoint: "https://slack.com/openid/connect/authorize",
68
+ scopes: requestedScopes,
69
+ state,
70
+ redirectURI,
71
+ additionalParams,
72
+ });
58
73
  },
59
74
  validateAuthorizationCode: async ({ code, redirectURI }) => {
60
75
  return validateAuthorizationCode({
@@ -108,5 +123,5 @@ export const slack = (options: SlackOptions) => {
108
123
  };
109
124
  },
110
125
  options,
111
- } satisfies OAuthProvider<SlackProfile>;
126
+ } satisfies UpstreamProvider<SlackProfile>;
112
127
  };
@@ -1,8 +1,9 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
- import type { OAuthProvider, ProviderOptions } from "../oauth2";
2
+ import type { ProviderOptions, UpstreamProvider } from "../oauth2";
3
3
  import {
4
4
  createAuthorizationURL,
5
5
  refreshAccessToken,
6
+ resolveRequestedScopes,
6
7
  validateAuthorizationCode,
7
8
  } from "../oauth2";
8
9
 
@@ -19,23 +20,35 @@ export interface SpotifyOptions extends ProviderOptions<SpotifyProfile> {
19
20
  clientId: string;
20
21
  }
21
22
 
23
+ const SPOTIFY_DEFAULT_SCOPES = ["user-read-email"];
24
+
22
25
  export const spotify = (options: SpotifyOptions) => {
23
26
  const tokenEndpoint = "https://accounts.spotify.com/api/token";
24
27
  return {
25
28
  id: "spotify",
26
29
  name: "Spotify",
27
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
28
- const _scopes = options.disableDefaultScope ? [] : ["user-read-email"];
29
- if (options.scope) _scopes.push(...options.scope);
30
- if (scopes) _scopes.push(...scopes);
30
+ callbackPath: "/callback/spotify",
31
+ async createAuthorizationURL({
32
+ state,
33
+ scopes,
34
+ codeVerifier,
35
+ redirectURI,
36
+ additionalParams,
37
+ }) {
38
+ const requestedScopes = resolveRequestedScopes(
39
+ options,
40
+ SPOTIFY_DEFAULT_SCOPES,
41
+ scopes,
42
+ );
31
43
  return createAuthorizationURL({
32
44
  id: "spotify",
33
45
  options,
34
46
  authorizationEndpoint: "https://accounts.spotify.com/authorize",
35
- scopes: _scopes,
47
+ scopes: requestedScopes,
36
48
  state,
37
49
  codeVerifier,
38
50
  redirectURI,
51
+ additionalParams,
39
52
  });
40
53
  },
41
54
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -90,5 +103,5 @@ export const spotify = (options: SpotifyOptions) => {
90
103
  };
91
104
  },
92
105
  options,
93
- } satisfies OAuthProvider<SpotifyProfile>;
106
+ } satisfies UpstreamProvider<SpotifyProfile>;
94
107
  };