@depup/better-auth__core 1.6.23-depup.2

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 (310) hide show
  1. package/LICENSE.md +20 -0
  2. package/README.md +32 -0
  3. package/changes.json +14 -0
  4. package/dist/api/index.d.mts +281 -0
  5. package/dist/api/index.mjs +92 -0
  6. package/dist/async_hooks/index.d.mts +6 -0
  7. package/dist/async_hooks/index.mjs +20 -0
  8. package/dist/async_hooks/pure.index.d.mts +6 -0
  9. package/dist/async_hooks/pure.index.mjs +33 -0
  10. package/dist/context/endpoint-context.d.mts +18 -0
  11. package/dist/context/endpoint-context.mjs +29 -0
  12. package/dist/context/global.d.mts +6 -0
  13. package/dist/context/global.mjs +36 -0
  14. package/dist/context/index.d.mts +5 -0
  15. package/dist/context/index.mjs +5 -0
  16. package/dist/context/request-state.d.mts +27 -0
  17. package/dist/context/request-state.mjs +47 -0
  18. package/dist/context/transaction.d.mts +27 -0
  19. package/dist/context/transaction.mjs +96 -0
  20. package/dist/db/adapter/factory.d.mts +17 -0
  21. package/dist/db/adapter/factory.mjs +905 -0
  22. package/dist/db/adapter/get-default-field-name.d.mts +18 -0
  23. package/dist/db/adapter/get-default-field-name.mjs +36 -0
  24. package/dist/db/adapter/get-default-model-name.d.mts +12 -0
  25. package/dist/db/adapter/get-default-model-name.mjs +30 -0
  26. package/dist/db/adapter/get-field-attributes.d.mts +26 -0
  27. package/dist/db/adapter/get-field-attributes.mjs +37 -0
  28. package/dist/db/adapter/get-field-name.d.mts +18 -0
  29. package/dist/db/adapter/get-field-name.mjs +31 -0
  30. package/dist/db/adapter/get-id-field.d.mts +36 -0
  31. package/dist/db/adapter/get-id-field.mjs +64 -0
  32. package/dist/db/adapter/get-model-name.d.mts +12 -0
  33. package/dist/db/adapter/get-model-name.mjs +21 -0
  34. package/dist/db/adapter/index.d.mts +615 -0
  35. package/dist/db/adapter/index.mjs +24 -0
  36. package/dist/db/adapter/types.d.mts +105 -0
  37. package/dist/db/adapter/utils.d.mts +7 -0
  38. package/dist/db/adapter/utils.mjs +37 -0
  39. package/dist/db/get-tables.d.mts +6 -0
  40. package/dist/db/get-tables.mjs +266 -0
  41. package/dist/db/index.d.mts +10 -0
  42. package/dist/db/index.mjs +8 -0
  43. package/dist/db/plugin.d.mts +12 -0
  44. package/dist/db/schema/account.d.mts +28 -0
  45. package/dist/db/schema/account.mjs +17 -0
  46. package/dist/db/schema/rate-limit.d.mts +18 -0
  47. package/dist/db/schema/rate-limit.mjs +9 -0
  48. package/dist/db/schema/session.d.mts +23 -0
  49. package/dist/db/schema/session.mjs +12 -0
  50. package/dist/db/schema/shared.d.mts +10 -0
  51. package/dist/db/schema/shared.mjs +9 -0
  52. package/dist/db/schema/user.d.mts +22 -0
  53. package/dist/db/schema/user.mjs +11 -0
  54. package/dist/db/schema/verification.d.mts +21 -0
  55. package/dist/db/schema/verification.mjs +10 -0
  56. package/dist/db/type.d.mts +194 -0
  57. package/dist/env/color-depth.d.mts +4 -0
  58. package/dist/env/color-depth.mjs +86 -0
  59. package/dist/env/env-impl.d.mts +32 -0
  60. package/dist/env/env-impl.mjs +81 -0
  61. package/dist/env/index.d.mts +4 -0
  62. package/dist/env/index.mjs +4 -0
  63. package/dist/env/logger.d.mts +48 -0
  64. package/dist/env/logger.mjs +79 -0
  65. package/dist/error/codes.d.mts +69 -0
  66. package/dist/error/codes.mjs +55 -0
  67. package/dist/error/index.d.mts +26 -0
  68. package/dist/error/index.mjs +27 -0
  69. package/dist/index.d.mts +9 -0
  70. package/dist/index.mjs +1 -0
  71. package/dist/instrumentation/api.mjs +12 -0
  72. package/dist/instrumentation/attributes.d.mts +11 -0
  73. package/dist/instrumentation/attributes.mjs +10 -0
  74. package/dist/instrumentation/index.d.mts +3 -0
  75. package/dist/instrumentation/index.mjs +3 -0
  76. package/dist/instrumentation/noop.mjs +42 -0
  77. package/dist/instrumentation/pure.index.d.mts +7 -0
  78. package/dist/instrumentation/pure.index.mjs +7 -0
  79. package/dist/instrumentation/tracer.d.mts +13 -0
  80. package/dist/instrumentation/tracer.mjs +53 -0
  81. package/dist/oauth2/client-credentials-token.d.mts +56 -0
  82. package/dist/oauth2/client-credentials-token.mjs +64 -0
  83. package/dist/oauth2/create-authorization-url.d.mts +44 -0
  84. package/dist/oauth2/create-authorization-url.mjs +41 -0
  85. package/dist/oauth2/index.d.mts +8 -0
  86. package/dist/oauth2/index.mjs +7 -0
  87. package/dist/oauth2/oauth-provider.d.mts +192 -0
  88. package/dist/oauth2/refresh-access-token.d.mts +54 -0
  89. package/dist/oauth2/refresh-access-token.mjs +73 -0
  90. package/dist/oauth2/reject-redirects.mjs +60 -0
  91. package/dist/oauth2/utils.d.mts +24 -0
  92. package/dist/oauth2/utils.mjs +48 -0
  93. package/dist/oauth2/validate-authorization-code.d.mts +85 -0
  94. package/dist/oauth2/validate-authorization-code.mjs +86 -0
  95. package/dist/oauth2/verify.d.mts +65 -0
  96. package/dist/oauth2/verify.mjs +202 -0
  97. package/dist/social-providers/apple.d.mts +133 -0
  98. package/dist/social-providers/apple.mjs +117 -0
  99. package/dist/social-providers/atlassian.d.mts +70 -0
  100. package/dist/social-providers/atlassian.mjs +80 -0
  101. package/dist/social-providers/cognito.d.mts +85 -0
  102. package/dist/social-providers/cognito.mjs +162 -0
  103. package/dist/social-providers/discord.d.mts +124 -0
  104. package/dist/social-providers/discord.mjs +62 -0
  105. package/dist/social-providers/dropbox.d.mts +69 -0
  106. package/dist/social-providers/dropbox.mjs +72 -0
  107. package/dist/social-providers/facebook.d.mts +79 -0
  108. package/dist/social-providers/facebook.mjs +157 -0
  109. package/dist/social-providers/figma.d.mts +61 -0
  110. package/dist/social-providers/figma.mjs +83 -0
  111. package/dist/social-providers/github.d.mts +102 -0
  112. package/dist/social-providers/github.mjs +92 -0
  113. package/dist/social-providers/gitlab.d.mts +123 -0
  114. package/dist/social-providers/gitlab.mjs +79 -0
  115. package/dist/social-providers/google.d.mts +123 -0
  116. package/dist/social-providers/google.mjs +137 -0
  117. package/dist/social-providers/huggingface.d.mts +83 -0
  118. package/dist/social-providers/huggingface.mjs +73 -0
  119. package/dist/social-providers/index.d.mts +1834 -0
  120. package/dist/social-providers/index.mjs +78 -0
  121. package/dist/social-providers/kakao.d.mts +161 -0
  122. package/dist/social-providers/kakao.mjs +70 -0
  123. package/dist/social-providers/kick.d.mts +73 -0
  124. package/dist/social-providers/kick.mjs +68 -0
  125. package/dist/social-providers/line.d.mts +105 -0
  126. package/dist/social-providers/line.mjs +110 -0
  127. package/dist/social-providers/linear.d.mts +68 -0
  128. package/dist/social-providers/linear.mjs +85 -0
  129. package/dist/social-providers/linkedin.d.mts +67 -0
  130. package/dist/social-providers/linkedin.mjs +73 -0
  131. package/dist/social-providers/microsoft-entra-id.d.mts +174 -0
  132. package/dist/social-providers/microsoft-entra-id.mjs +152 -0
  133. package/dist/social-providers/naver.d.mts +92 -0
  134. package/dist/social-providers/naver.mjs +65 -0
  135. package/dist/social-providers/notion.d.mts +64 -0
  136. package/dist/social-providers/notion.mjs +72 -0
  137. package/dist/social-providers/paybin.d.mts +71 -0
  138. package/dist/social-providers/paybin.mjs +81 -0
  139. package/dist/social-providers/paypal.d.mts +131 -0
  140. package/dist/social-providers/paypal.mjs +188 -0
  141. package/dist/social-providers/polar.d.mts +74 -0
  142. package/dist/social-providers/polar.mjs +71 -0
  143. package/dist/social-providers/railway.d.mts +65 -0
  144. package/dist/social-providers/railway.mjs +74 -0
  145. package/dist/social-providers/reddit.d.mts +62 -0
  146. package/dist/social-providers/reddit.mjs +81 -0
  147. package/dist/social-providers/roblox.d.mts +70 -0
  148. package/dist/social-providers/roblox.mjs +57 -0
  149. package/dist/social-providers/salesforce.d.mts +79 -0
  150. package/dist/social-providers/salesforce.mjs +87 -0
  151. package/dist/social-providers/slack.d.mts +83 -0
  152. package/dist/social-providers/slack.mjs +66 -0
  153. package/dist/social-providers/spotify.d.mts +63 -0
  154. package/dist/social-providers/spotify.mjs +69 -0
  155. package/dist/social-providers/tiktok.d.mts +168 -0
  156. package/dist/social-providers/tiktok.mjs +60 -0
  157. package/dist/social-providers/twitch.d.mts +79 -0
  158. package/dist/social-providers/twitch.mjs +75 -0
  159. package/dist/social-providers/twitter.d.mts +126 -0
  160. package/dist/social-providers/twitter.mjs +85 -0
  161. package/dist/social-providers/vercel.d.mts +62 -0
  162. package/dist/social-providers/vercel.mjs +58 -0
  163. package/dist/social-providers/vk.d.mts +70 -0
  164. package/dist/social-providers/vk.mjs +81 -0
  165. package/dist/social-providers/wechat.d.mts +113 -0
  166. package/dist/social-providers/wechat.mjs +81 -0
  167. package/dist/social-providers/zoom.d.mts +163 -0
  168. package/dist/social-providers/zoom.mjs +69 -0
  169. package/dist/types/context.d.mts +319 -0
  170. package/dist/types/cookie.d.mts +15 -0
  171. package/dist/types/helper.d.mts +10 -0
  172. package/dist/types/index.d.mts +9 -0
  173. package/dist/types/init-options.d.mts +1430 -0
  174. package/dist/types/plugin-client.d.mts +123 -0
  175. package/dist/types/plugin.d.mts +124 -0
  176. package/dist/types/secret.d.mts +11 -0
  177. package/dist/utils/async.d.mts +22 -0
  178. package/dist/utils/async.mjs +32 -0
  179. package/dist/utils/db.d.mts +9 -0
  180. package/dist/utils/db.mjs +15 -0
  181. package/dist/utils/deprecate.d.mts +9 -0
  182. package/dist/utils/deprecate.mjs +16 -0
  183. package/dist/utils/error-codes.d.mts +13 -0
  184. package/dist/utils/error-codes.mjs +10 -0
  185. package/dist/utils/fetch-metadata.d.mts +4 -0
  186. package/dist/utils/fetch-metadata.mjs +6 -0
  187. package/dist/utils/host.d.mts +147 -0
  188. package/dist/utils/host.mjs +295 -0
  189. package/dist/utils/id.d.mts +4 -0
  190. package/dist/utils/id.mjs +7 -0
  191. package/dist/utils/ip.d.mts +77 -0
  192. package/dist/utils/ip.mjs +230 -0
  193. package/dist/utils/is-api-error.d.mts +6 -0
  194. package/dist/utils/is-api-error.mjs +8 -0
  195. package/dist/utils/json.d.mts +4 -0
  196. package/dist/utils/json.mjs +41 -0
  197. package/dist/utils/redirect-uri.d.mts +20 -0
  198. package/dist/utils/redirect-uri.mjs +48 -0
  199. package/dist/utils/string.d.mts +8 -0
  200. package/dist/utils/string.mjs +25 -0
  201. package/dist/utils/url.d.mts +37 -0
  202. package/dist/utils/url.mjs +61 -0
  203. package/package.json +216 -0
  204. package/src/api/index.ts +185 -0
  205. package/src/async_hooks/index.ts +40 -0
  206. package/src/async_hooks/pure.index.ts +46 -0
  207. package/src/context/endpoint-context.ts +50 -0
  208. package/src/context/global.ts +57 -0
  209. package/src/context/index.ts +23 -0
  210. package/src/context/request-state.ts +91 -0
  211. package/src/context/transaction.ts +169 -0
  212. package/src/db/adapter/factory.ts +1740 -0
  213. package/src/db/adapter/get-default-field-name.ts +59 -0
  214. package/src/db/adapter/get-default-model-name.ts +51 -0
  215. package/src/db/adapter/get-field-attributes.ts +62 -0
  216. package/src/db/adapter/get-field-name.ts +43 -0
  217. package/src/db/adapter/get-id-field.ts +150 -0
  218. package/src/db/adapter/get-model-name.ts +36 -0
  219. package/src/db/adapter/index.ts +656 -0
  220. package/src/db/adapter/types.ts +134 -0
  221. package/src/db/adapter/utils.ts +61 -0
  222. package/src/db/get-tables.ts +304 -0
  223. package/src/db/index.ts +43 -0
  224. package/src/db/plugin.ts +11 -0
  225. package/src/db/schema/account.ts +47 -0
  226. package/src/db/schema/rate-limit.ts +36 -0
  227. package/src/db/schema/session.ts +29 -0
  228. package/src/db/schema/shared.ts +7 -0
  229. package/src/db/schema/user.ts +31 -0
  230. package/src/db/schema/verification.ts +28 -0
  231. package/src/db/type.ts +360 -0
  232. package/src/env/color-depth.ts +172 -0
  233. package/src/env/env-impl.ts +123 -0
  234. package/src/env/index.ts +23 -0
  235. package/src/env/logger.ts +145 -0
  236. package/src/error/codes.ts +72 -0
  237. package/src/error/index.ts +44 -0
  238. package/src/index.ts +1 -0
  239. package/src/instrumentation/api.ts +17 -0
  240. package/src/instrumentation/attributes.ts +22 -0
  241. package/src/instrumentation/index.ts +2 -0
  242. package/src/instrumentation/noop.ts +74 -0
  243. package/src/instrumentation/pure.index.ts +31 -0
  244. package/src/instrumentation/tracer.ts +95 -0
  245. package/src/oauth2/client-credentials-token.ts +126 -0
  246. package/src/oauth2/create-authorization-url.ts +89 -0
  247. package/src/oauth2/index.ts +34 -0
  248. package/src/oauth2/oauth-provider.ts +222 -0
  249. package/src/oauth2/refresh-access-token.ts +157 -0
  250. package/src/oauth2/reject-redirects.ts +70 -0
  251. package/src/oauth2/utils.ts +70 -0
  252. package/src/oauth2/validate-authorization-code.ts +190 -0
  253. package/src/oauth2/verify.ts +408 -0
  254. package/src/social-providers/apple.ts +249 -0
  255. package/src/social-providers/atlassian.ts +133 -0
  256. package/src/social-providers/cognito.ts +281 -0
  257. package/src/social-providers/discord.ts +170 -0
  258. package/src/social-providers/dropbox.ts +112 -0
  259. package/src/social-providers/facebook.ts +288 -0
  260. package/src/social-providers/figma.ts +118 -0
  261. package/src/social-providers/github.ts +184 -0
  262. package/src/social-providers/gitlab.ts +155 -0
  263. package/src/social-providers/google.ts +263 -0
  264. package/src/social-providers/huggingface.ts +119 -0
  265. package/src/social-providers/index.ts +132 -0
  266. package/src/social-providers/kakao.ts +179 -0
  267. package/src/social-providers/kick.ts +109 -0
  268. package/src/social-providers/line.ts +169 -0
  269. package/src/social-providers/linear.ts +121 -0
  270. package/src/social-providers/linkedin.ts +110 -0
  271. package/src/social-providers/microsoft-entra-id.ts +391 -0
  272. package/src/social-providers/naver.ts +113 -0
  273. package/src/social-providers/notion.ts +108 -0
  274. package/src/social-providers/paybin.ts +118 -0
  275. package/src/social-providers/paypal.ts +371 -0
  276. package/src/social-providers/polar.ts +111 -0
  277. package/src/social-providers/railway.ts +100 -0
  278. package/src/social-providers/reddit.ts +126 -0
  279. package/src/social-providers/roblox.ts +112 -0
  280. package/src/social-providers/salesforce.ts +159 -0
  281. package/src/social-providers/slack.ts +112 -0
  282. package/src/social-providers/spotify.ts +94 -0
  283. package/src/social-providers/tiktok.ts +211 -0
  284. package/src/social-providers/twitch.ts +112 -0
  285. package/src/social-providers/twitter.ts +199 -0
  286. package/src/social-providers/vercel.ts +87 -0
  287. package/src/social-providers/vk.ts +125 -0
  288. package/src/social-providers/wechat.ts +220 -0
  289. package/src/social-providers/zoom.ts +230 -0
  290. package/src/types/context.ts +460 -0
  291. package/src/types/cookie.ts +10 -0
  292. package/src/types/helper.ts +27 -0
  293. package/src/types/index.ts +40 -0
  294. package/src/types/init-options.ts +1679 -0
  295. package/src/types/plugin-client.ts +143 -0
  296. package/src/types/plugin.ts +163 -0
  297. package/src/types/secret.ts +8 -0
  298. package/src/utils/async.ts +53 -0
  299. package/src/utils/db.ts +20 -0
  300. package/src/utils/deprecate.ts +21 -0
  301. package/src/utils/error-codes.ts +68 -0
  302. package/src/utils/fetch-metadata.ts +3 -0
  303. package/src/utils/host.ts +416 -0
  304. package/src/utils/id.ts +5 -0
  305. package/src/utils/ip.ts +395 -0
  306. package/src/utils/is-api-error.ts +10 -0
  307. package/src/utils/json.ts +56 -0
  308. package/src/utils/redirect-uri.ts +54 -0
  309. package/src/utils/string.ts +40 -0
  310. package/src/utils/url.ts +77 -0
@@ -0,0 +1,100 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
+ import {
4
+ createAuthorizationURL,
5
+ refreshAccessToken,
6
+ validateAuthorizationCode,
7
+ } from "../oauth2";
8
+
9
+ const authorizationEndpoint = "https://backboard.railway.com/oauth/auth";
10
+ const tokenEndpoint = "https://backboard.railway.com/oauth/token";
11
+ const userinfoEndpoint = "https://backboard.railway.com/oauth/me";
12
+
13
+ export interface RailwayProfile {
14
+ /** The user's unique ID (OAuth `sub` claim). */
15
+ sub: string;
16
+ /** The user's email address. */
17
+ email: string;
18
+ /** The user's display name. */
19
+ name: string;
20
+ /** URL of the user's profile picture. */
21
+ picture: string;
22
+ }
23
+
24
+ export interface RailwayOptions extends ProviderOptions<RailwayProfile> {
25
+ clientId: string;
26
+ }
27
+
28
+ export const railway = (options: RailwayOptions) => {
29
+ return {
30
+ id: "railway",
31
+ 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);
38
+ return createAuthorizationURL({
39
+ id: "railway",
40
+ options,
41
+ authorizationEndpoint,
42
+ scopes: _scopes,
43
+ state,
44
+ codeVerifier,
45
+ redirectURI,
46
+ });
47
+ },
48
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
49
+ return validateAuthorizationCode({
50
+ code,
51
+ codeVerifier,
52
+ redirectURI,
53
+ options,
54
+ tokenEndpoint,
55
+ authentication: "basic",
56
+ });
57
+ },
58
+ refreshAccessToken: options.refreshAccessToken
59
+ ? options.refreshAccessToken
60
+ : async (refreshToken) => {
61
+ return refreshAccessToken({
62
+ refreshToken,
63
+ options: {
64
+ clientId: options.clientId,
65
+ clientKey: options.clientKey,
66
+ clientSecret: options.clientSecret,
67
+ },
68
+ tokenEndpoint,
69
+ authentication: "basic",
70
+ });
71
+ },
72
+ async getUserInfo(token) {
73
+ if (options.getUserInfo) {
74
+ return options.getUserInfo(token);
75
+ }
76
+ const { data: profile, error } = await betterFetch<RailwayProfile>(
77
+ userinfoEndpoint,
78
+ { headers: { authorization: `Bearer ${token.accessToken}` } },
79
+ );
80
+ if (error || !profile) {
81
+ return null;
82
+ }
83
+ const userMap = await options.mapProfileToUser?.(profile);
84
+ // Railway does not provide an email_verified claim.
85
+ // We default to false for security consistency.
86
+ return {
87
+ user: {
88
+ id: profile.sub,
89
+ name: profile.name,
90
+ email: profile.email,
91
+ image: profile.picture,
92
+ emailVerified: false,
93
+ ...userMap,
94
+ },
95
+ data: profile,
96
+ };
97
+ },
98
+ options,
99
+ } satisfies OAuthProvider<RailwayProfile>;
100
+ };
@@ -0,0 +1,126 @@
1
+ import { base64 } from "@better-auth/utils/base64";
2
+ import { betterFetch } from "@better-fetch/fetch";
3
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
4
+ import {
5
+ createAuthorizationURL,
6
+ getOAuth2Tokens,
7
+ refreshAccessToken,
8
+ } from "../oauth2";
9
+
10
+ export interface RedditProfile {
11
+ id: string;
12
+ name: string;
13
+ icon_img: string | null;
14
+ has_verified_email: boolean;
15
+ oauth_client_id: string;
16
+ verified: boolean;
17
+ }
18
+
19
+ export interface RedditOptions extends ProviderOptions<RedditProfile> {
20
+ clientId: string;
21
+ duration?: string | undefined;
22
+ }
23
+
24
+ export const reddit = (options: RedditOptions) => {
25
+ return {
26
+ id: "reddit",
27
+ 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);
32
+ return createAuthorizationURL({
33
+ id: "reddit",
34
+ options,
35
+ authorizationEndpoint: "https://www.reddit.com/api/v1/authorize",
36
+ scopes: _scopes,
37
+ state,
38
+ redirectURI,
39
+ duration: options.duration,
40
+ });
41
+ },
42
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
43
+ const body = new URLSearchParams({
44
+ grant_type: "authorization_code",
45
+ code,
46
+ redirect_uri: options.redirectURI || redirectURI,
47
+ });
48
+ const headers = {
49
+ "content-type": "application/x-www-form-urlencoded",
50
+ accept: "text/plain",
51
+ "user-agent": "better-auth",
52
+ Authorization: `Basic ${base64.encode(
53
+ `${options.clientId}:${options.clientSecret}`,
54
+ )}`,
55
+ };
56
+
57
+ const { data, error } = await betterFetch<object>(
58
+ "https://www.reddit.com/api/v1/access_token",
59
+ {
60
+ method: "POST",
61
+ headers,
62
+ body: body.toString(),
63
+ },
64
+ );
65
+
66
+ if (error) {
67
+ throw error;
68
+ }
69
+
70
+ return getOAuth2Tokens(data);
71
+ },
72
+
73
+ refreshAccessToken: options.refreshAccessToken
74
+ ? options.refreshAccessToken
75
+ : async (refreshToken) => {
76
+ return refreshAccessToken({
77
+ refreshToken,
78
+ options: {
79
+ clientId: options.clientId,
80
+ clientKey: options.clientKey,
81
+ clientSecret: options.clientSecret,
82
+ },
83
+ authentication: "basic",
84
+ tokenEndpoint: "https://www.reddit.com/api/v1/access_token",
85
+ });
86
+ },
87
+ async getUserInfo(token) {
88
+ if (options.getUserInfo) {
89
+ return options.getUserInfo(token);
90
+ }
91
+
92
+ const { data: profile, error } = await betterFetch<RedditProfile>(
93
+ "https://oauth.reddit.com/api/v1/me",
94
+ {
95
+ headers: {
96
+ Authorization: `Bearer ${token.accessToken}`,
97
+ "User-Agent": "better-auth",
98
+ },
99
+ },
100
+ );
101
+
102
+ if (error) {
103
+ return null;
104
+ }
105
+
106
+ const userMap = await options.mapProfileToUser?.(profile);
107
+ // Reddit's identity scope does not return an email. Synthesize a stable,
108
+ // non-routable placeholder (RFC 2606 `.invalid`) keyed to the user's
109
+ // Reddit id rather than the routable `reddit.com`, which could collide
110
+ // with a real address. Left unverified; `mapProfileToUser` can override.
111
+ const email = userMap?.email || `${profile.id}@reddit.invalid`;
112
+ return {
113
+ user: {
114
+ id: profile.id,
115
+ name: profile.name,
116
+ image: profile.icon_img?.split("?")[0]!,
117
+ ...userMap,
118
+ email,
119
+ emailVerified: userMap?.emailVerified ?? false,
120
+ },
121
+ data: profile,
122
+ };
123
+ },
124
+ options,
125
+ } satisfies OAuthProvider<RedditProfile>;
126
+ };
@@ -0,0 +1,112 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
+ import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
4
+
5
+ export interface RobloxProfile extends Record<string, any> {
6
+ /** the user's id */
7
+ sub: string;
8
+ /** the user's username */
9
+ preferred_username: string;
10
+ /** the user's display name, will return the same value as the preferred_username if not set */
11
+ nickname: string;
12
+ /** the user's display name, again, will return the same value as the preferred_username if not set */
13
+ name: string;
14
+ /** the account creation date as a unix timestamp in seconds */
15
+ created_at: number;
16
+ /** the user's profile URL */
17
+ profile: string;
18
+ /** the user's avatar URL */
19
+ picture: string;
20
+ }
21
+
22
+ export interface RobloxOptions extends ProviderOptions<RobloxProfile> {
23
+ clientId: string;
24
+ prompt?:
25
+ | (
26
+ | "none"
27
+ | "consent"
28
+ | "login"
29
+ | "select_account"
30
+ | "select_account consent"
31
+ )
32
+ | undefined;
33
+ }
34
+
35
+ export const roblox = (options: RobloxOptions) => {
36
+ const tokenEndpoint = "https://apis.roblox.com/oauth/v1/token";
37
+ return {
38
+ id: "roblox",
39
+ 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"}`,
52
+ );
53
+ },
54
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
55
+ return validateAuthorizationCode({
56
+ code,
57
+ redirectURI: options.redirectURI || redirectURI,
58
+ options,
59
+ tokenEndpoint,
60
+ authentication: "post",
61
+ });
62
+ },
63
+ refreshAccessToken: options.refreshAccessToken
64
+ ? options.refreshAccessToken
65
+ : async (refreshToken) => {
66
+ return refreshAccessToken({
67
+ refreshToken,
68
+ options: {
69
+ clientId: options.clientId,
70
+ clientKey: options.clientKey,
71
+ clientSecret: options.clientSecret,
72
+ },
73
+ tokenEndpoint,
74
+ });
75
+ },
76
+ async getUserInfo(token) {
77
+ if (options.getUserInfo) {
78
+ return options.getUserInfo(token);
79
+ }
80
+ const { data: profile, error } = await betterFetch<RobloxProfile>(
81
+ "https://apis.roblox.com/oauth/v1/userinfo",
82
+ {
83
+ headers: {
84
+ authorization: `Bearer ${token.accessToken}`,
85
+ },
86
+ },
87
+ );
88
+
89
+ if (error) {
90
+ return null;
91
+ }
92
+
93
+ const userMap = await options.mapProfileToUser?.(profile);
94
+ // Roblox does not provide email or email_verified claim.
95
+ // We default to false for security consistency.
96
+ return {
97
+ user: {
98
+ id: profile.sub,
99
+ name: profile.nickname || profile.preferred_username || "",
100
+ image: profile.picture,
101
+ email: profile.preferred_username || null, // Roblox does not provide email
102
+ emailVerified: false,
103
+ ...userMap,
104
+ },
105
+ data: {
106
+ ...profile,
107
+ },
108
+ };
109
+ },
110
+ options,
111
+ } satisfies OAuthProvider<RobloxProfile>;
112
+ };
@@ -0,0 +1,159 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+ import { logger } from "../env";
3
+ import { BetterAuthError } from "../error";
4
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
5
+ import {
6
+ createAuthorizationURL,
7
+ refreshAccessToken,
8
+ validateAuthorizationCode,
9
+ } from "../oauth2";
10
+
11
+ export interface SalesforceProfile {
12
+ sub: string;
13
+ user_id: string;
14
+ organization_id: string;
15
+ preferred_username?: string | undefined;
16
+ email: string;
17
+ email_verified?: boolean | undefined;
18
+ name: string;
19
+ given_name?: string | undefined;
20
+ family_name?: string | undefined;
21
+ zoneinfo?: string | undefined;
22
+ photos?:
23
+ | {
24
+ picture?: string;
25
+ thumbnail?: string;
26
+ }
27
+ | undefined;
28
+ }
29
+
30
+ export interface SalesforceOptions extends ProviderOptions<SalesforceProfile> {
31
+ clientId: string;
32
+ environment?: ("sandbox" | "production") | undefined;
33
+ loginUrl?: string | undefined;
34
+ /**
35
+ * Override the redirect URI if auto-detection fails.
36
+ * Should match the Callback URL configured in your Salesforce Connected App.
37
+ * @example "http://localhost:3000/api/auth/callback/salesforce"
38
+ */
39
+ redirectURI?: string | undefined;
40
+ }
41
+
42
+ export const salesforce = (options: SalesforceOptions) => {
43
+ const environment = options.environment ?? "production";
44
+ const isSandbox = environment === "sandbox";
45
+ const authorizationEndpoint = options.loginUrl
46
+ ? `https://${options.loginUrl}/services/oauth2/authorize`
47
+ : isSandbox
48
+ ? "https://test.salesforce.com/services/oauth2/authorize"
49
+ : "https://login.salesforce.com/services/oauth2/authorize";
50
+
51
+ const tokenEndpoint = options.loginUrl
52
+ ? `https://${options.loginUrl}/services/oauth2/token`
53
+ : isSandbox
54
+ ? "https://test.salesforce.com/services/oauth2/token"
55
+ : "https://login.salesforce.com/services/oauth2/token";
56
+
57
+ const userInfoEndpoint = options.loginUrl
58
+ ? `https://${options.loginUrl}/services/oauth2/userinfo`
59
+ : isSandbox
60
+ ? "https://test.salesforce.com/services/oauth2/userinfo"
61
+ : "https://login.salesforce.com/services/oauth2/userinfo";
62
+
63
+ return {
64
+ id: "salesforce",
65
+ name: "Salesforce",
66
+
67
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
68
+ if (!options.clientId || !options.clientSecret) {
69
+ logger.error(
70
+ "Client Id and Client Secret are required for Salesforce. Make sure to provide them in the options.",
71
+ );
72
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
73
+ }
74
+ if (!codeVerifier) {
75
+ throw new BetterAuthError("codeVerifier is required for Salesforce");
76
+ }
77
+
78
+ const _scopes = options.disableDefaultScope
79
+ ? []
80
+ : ["openid", "email", "profile"];
81
+ if (options.scope) _scopes.push(...options.scope);
82
+ if (scopes) _scopes.push(...scopes);
83
+
84
+ return createAuthorizationURL({
85
+ id: "salesforce",
86
+ options,
87
+ authorizationEndpoint,
88
+ scopes: _scopes,
89
+ state,
90
+ codeVerifier,
91
+ redirectURI: options.redirectURI || redirectURI,
92
+ });
93
+ },
94
+
95
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
96
+ return validateAuthorizationCode({
97
+ code,
98
+ codeVerifier,
99
+ redirectURI: options.redirectURI || redirectURI,
100
+ options,
101
+ tokenEndpoint,
102
+ });
103
+ },
104
+
105
+ refreshAccessToken: options.refreshAccessToken
106
+ ? options.refreshAccessToken
107
+ : async (refreshToken) => {
108
+ return refreshAccessToken({
109
+ refreshToken,
110
+ options: {
111
+ clientId: options.clientId,
112
+ clientSecret: options.clientSecret,
113
+ },
114
+ tokenEndpoint,
115
+ });
116
+ },
117
+
118
+ async getUserInfo(token) {
119
+ if (options.getUserInfo) {
120
+ return options.getUserInfo(token);
121
+ }
122
+
123
+ try {
124
+ const { data: user } = await betterFetch<SalesforceProfile>(
125
+ userInfoEndpoint,
126
+ {
127
+ headers: {
128
+ Authorization: `Bearer ${token.accessToken}`,
129
+ },
130
+ },
131
+ );
132
+
133
+ if (!user) {
134
+ logger.error("Failed to fetch user info from Salesforce");
135
+ return null;
136
+ }
137
+
138
+ const userMap = await options.mapProfileToUser?.(user);
139
+
140
+ return {
141
+ user: {
142
+ id: user.user_id,
143
+ name: user.name,
144
+ email: user.email,
145
+ image: user.photos?.picture || user.photos?.thumbnail,
146
+ emailVerified: user.email_verified ?? false,
147
+ ...userMap,
148
+ },
149
+ data: user,
150
+ };
151
+ } catch (error) {
152
+ logger.error("Failed to fetch user info from Salesforce:", error);
153
+ return null;
154
+ }
155
+ },
156
+
157
+ options,
158
+ } satisfies OAuthProvider<SalesforceProfile>;
159
+ };
@@ -0,0 +1,112 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
+ import { refreshAccessToken, validateAuthorizationCode } from "../oauth2";
4
+
5
+ export interface SlackProfile extends Record<string, any> {
6
+ ok: boolean;
7
+ sub: string;
8
+ "https://slack.com/user_id": string;
9
+ "https://slack.com/team_id": string;
10
+ email: string;
11
+ email_verified: boolean;
12
+ date_email_verified: number;
13
+ name: string;
14
+ picture: string;
15
+ given_name: string;
16
+ family_name: string;
17
+ locale: string;
18
+ "https://slack.com/team_name": string;
19
+ "https://slack.com/team_domain": string;
20
+ "https://slack.com/user_image_24": string;
21
+ "https://slack.com/user_image_32": string;
22
+ "https://slack.com/user_image_48": string;
23
+ "https://slack.com/user_image_72": string;
24
+ "https://slack.com/user_image_192": string;
25
+ "https://slack.com/user_image_512": string;
26
+ "https://slack.com/team_image_34": string;
27
+ "https://slack.com/team_image_44": string;
28
+ "https://slack.com/team_image_68": string;
29
+ "https://slack.com/team_image_88": string;
30
+ "https://slack.com/team_image_102": string;
31
+ "https://slack.com/team_image_132": string;
32
+ "https://slack.com/team_image_230": string;
33
+ "https://slack.com/team_image_default": boolean;
34
+ }
35
+
36
+ export interface SlackOptions extends ProviderOptions<SlackProfile> {
37
+ clientId: string;
38
+ }
39
+
40
+ export const slack = (options: SlackOptions) => {
41
+ const tokenEndpoint = "https://slack.com/api/openid.connect.token";
42
+ return {
43
+ id: "slack",
44
+ 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;
58
+ },
59
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
60
+ return validateAuthorizationCode({
61
+ code,
62
+ redirectURI,
63
+ options,
64
+ tokenEndpoint,
65
+ });
66
+ },
67
+ refreshAccessToken: options.refreshAccessToken
68
+ ? options.refreshAccessToken
69
+ : async (refreshToken) => {
70
+ return refreshAccessToken({
71
+ refreshToken,
72
+ options: {
73
+ clientId: options.clientId,
74
+ clientKey: options.clientKey,
75
+ clientSecret: options.clientSecret,
76
+ },
77
+ tokenEndpoint,
78
+ });
79
+ },
80
+ async getUserInfo(token) {
81
+ if (options.getUserInfo) {
82
+ return options.getUserInfo(token);
83
+ }
84
+ const { data: profile, error } = await betterFetch<SlackProfile>(
85
+ "https://slack.com/api/openid.connect.userInfo",
86
+ {
87
+ headers: {
88
+ authorization: `Bearer ${token.accessToken}`,
89
+ },
90
+ },
91
+ );
92
+
93
+ if (error) {
94
+ return null;
95
+ }
96
+
97
+ const userMap = await options.mapProfileToUser?.(profile);
98
+ return {
99
+ user: {
100
+ id: profile["https://slack.com/user_id"],
101
+ name: profile.name || "",
102
+ email: profile.email,
103
+ emailVerified: profile.email_verified,
104
+ image: profile.picture || profile["https://slack.com/user_image_512"],
105
+ ...userMap,
106
+ },
107
+ data: profile,
108
+ };
109
+ },
110
+ options,
111
+ } satisfies OAuthProvider<SlackProfile>;
112
+ };
@@ -0,0 +1,94 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
+ import {
4
+ createAuthorizationURL,
5
+ refreshAccessToken,
6
+ validateAuthorizationCode,
7
+ } from "../oauth2";
8
+
9
+ export interface SpotifyProfile {
10
+ id: string;
11
+ display_name: string;
12
+ email: string;
13
+ images: {
14
+ url: string;
15
+ }[];
16
+ }
17
+
18
+ export interface SpotifyOptions extends ProviderOptions<SpotifyProfile> {
19
+ clientId: string;
20
+ }
21
+
22
+ export const spotify = (options: SpotifyOptions) => {
23
+ const tokenEndpoint = "https://accounts.spotify.com/api/token";
24
+ return {
25
+ id: "spotify",
26
+ 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);
31
+ return createAuthorizationURL({
32
+ id: "spotify",
33
+ options,
34
+ authorizationEndpoint: "https://accounts.spotify.com/authorize",
35
+ scopes: _scopes,
36
+ state,
37
+ codeVerifier,
38
+ redirectURI,
39
+ });
40
+ },
41
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
42
+ return validateAuthorizationCode({
43
+ code,
44
+ codeVerifier,
45
+ redirectURI,
46
+ options,
47
+ tokenEndpoint,
48
+ });
49
+ },
50
+ refreshAccessToken: options.refreshAccessToken
51
+ ? options.refreshAccessToken
52
+ : async (refreshToken) => {
53
+ return refreshAccessToken({
54
+ refreshToken,
55
+ options: {
56
+ clientId: options.clientId,
57
+ clientKey: options.clientKey,
58
+ clientSecret: options.clientSecret,
59
+ },
60
+ tokenEndpoint,
61
+ });
62
+ },
63
+ async getUserInfo(token) {
64
+ if (options.getUserInfo) {
65
+ return options.getUserInfo(token);
66
+ }
67
+ const { data: profile, error } = await betterFetch<SpotifyProfile>(
68
+ "https://api.spotify.com/v1/me",
69
+ {
70
+ method: "GET",
71
+ headers: {
72
+ Authorization: `Bearer ${token.accessToken}`,
73
+ },
74
+ },
75
+ );
76
+ if (error) {
77
+ return null;
78
+ }
79
+ const userMap = await options.mapProfileToUser?.(profile);
80
+ return {
81
+ user: {
82
+ id: profile.id,
83
+ name: profile.display_name,
84
+ email: profile.email,
85
+ image: profile.images[0]?.url,
86
+ emailVerified: false,
87
+ ...userMap,
88
+ },
89
+ data: profile,
90
+ };
91
+ },
92
+ options,
93
+ } satisfies OAuthProvider<SpotifyProfile>;
94
+ };