@hammadj/better-auth-core 1.5.0-beta.9

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 (353) hide show
  1. package/.turbo/turbo-build.log +266 -0
  2. package/.turbo/turbo-test.log +2 -0
  3. package/LICENSE.md +20 -0
  4. package/dist/api/index.d.mts +181 -0
  5. package/dist/api/index.mjs +34 -0
  6. package/dist/api/index.mjs.map +1 -0
  7. package/dist/async_hooks/index.d.mts +7 -0
  8. package/dist/async_hooks/index.mjs +22 -0
  9. package/dist/async_hooks/index.mjs.map +1 -0
  10. package/dist/async_hooks/pure.index.d.mts +7 -0
  11. package/dist/async_hooks/pure.index.mjs +35 -0
  12. package/dist/async_hooks/pure.index.mjs.map +1 -0
  13. package/dist/context/endpoint-context.d.mts +19 -0
  14. package/dist/context/endpoint-context.mjs +32 -0
  15. package/dist/context/endpoint-context.mjs.map +1 -0
  16. package/dist/context/global.d.mts +7 -0
  17. package/dist/context/global.mjs +38 -0
  18. package/dist/context/global.mjs.map +1 -0
  19. package/dist/context/index.d.mts +5 -0
  20. package/dist/context/index.mjs +6 -0
  21. package/dist/context/request-state.d.mts +26 -0
  22. package/dist/context/request-state.mjs +50 -0
  23. package/dist/context/request-state.mjs.map +1 -0
  24. package/dist/context/transaction.d.mts +25 -0
  25. package/dist/context/transaction.mjs +96 -0
  26. package/dist/context/transaction.mjs.map +1 -0
  27. package/dist/db/adapter/factory.d.mts +28 -0
  28. package/dist/db/adapter/factory.mjs +716 -0
  29. package/dist/db/adapter/factory.mjs.map +1 -0
  30. package/dist/db/adapter/get-default-field-name.d.mts +19 -0
  31. package/dist/db/adapter/get-default-field-name.mjs +39 -0
  32. package/dist/db/adapter/get-default-field-name.mjs.map +1 -0
  33. package/dist/db/adapter/get-default-model-name.d.mts +13 -0
  34. package/dist/db/adapter/get-default-model-name.mjs +33 -0
  35. package/dist/db/adapter/get-default-model-name.mjs.map +1 -0
  36. package/dist/db/adapter/get-field-attributes.d.mts +30 -0
  37. package/dist/db/adapter/get-field-attributes.mjs +40 -0
  38. package/dist/db/adapter/get-field-attributes.mjs.map +1 -0
  39. package/dist/db/adapter/get-field-name.d.mts +19 -0
  40. package/dist/db/adapter/get-field-name.mjs +34 -0
  41. package/dist/db/adapter/get-field-name.mjs.map +1 -0
  42. package/dist/db/adapter/get-id-field.d.mts +40 -0
  43. package/dist/db/adapter/get-id-field.mjs +68 -0
  44. package/dist/db/adapter/get-id-field.mjs.map +1 -0
  45. package/dist/db/adapter/get-model-name.d.mts +13 -0
  46. package/dist/db/adapter/get-model-name.mjs +24 -0
  47. package/dist/db/adapter/get-model-name.mjs.map +1 -0
  48. package/dist/db/adapter/index.d.mts +515 -0
  49. package/dist/db/adapter/index.mjs +10 -0
  50. package/dist/db/adapter/types.d.mts +140 -0
  51. package/dist/db/adapter/utils.d.mts +8 -0
  52. package/dist/db/adapter/utils.mjs +39 -0
  53. package/dist/db/adapter/utils.mjs.map +1 -0
  54. package/dist/db/get-tables.d.mts +9 -0
  55. package/dist/db/get-tables.mjs +267 -0
  56. package/dist/db/get-tables.mjs.map +1 -0
  57. package/dist/db/index.d.mts +10 -0
  58. package/dist/db/index.mjs +9 -0
  59. package/dist/db/plugin.d.mts +13 -0
  60. package/dist/db/schema/account.d.mts +27 -0
  61. package/dist/db/schema/account.mjs +20 -0
  62. package/dist/db/schema/account.mjs.map +1 -0
  63. package/dist/db/schema/rate-limit.d.mts +15 -0
  64. package/dist/db/schema/rate-limit.mjs +12 -0
  65. package/dist/db/schema/rate-limit.mjs.map +1 -0
  66. package/dist/db/schema/session.d.mts +22 -0
  67. package/dist/db/schema/session.mjs +15 -0
  68. package/dist/db/schema/session.mjs.map +1 -0
  69. package/dist/db/schema/shared.d.mts +11 -0
  70. package/dist/db/schema/shared.mjs +12 -0
  71. package/dist/db/schema/shared.mjs.map +1 -0
  72. package/dist/db/schema/user.d.mts +21 -0
  73. package/dist/db/schema/user.mjs +14 -0
  74. package/dist/db/schema/user.mjs.map +1 -0
  75. package/dist/db/schema/verification.d.mts +20 -0
  76. package/dist/db/schema/verification.mjs +13 -0
  77. package/dist/db/schema/verification.mjs.map +1 -0
  78. package/dist/db/type.d.mts +147 -0
  79. package/dist/env/color-depth.d.mts +5 -0
  80. package/dist/env/color-depth.mjs +89 -0
  81. package/dist/env/color-depth.mjs.map +1 -0
  82. package/dist/env/env-impl.d.mts +33 -0
  83. package/dist/env/env-impl.mjs +83 -0
  84. package/dist/env/env-impl.mjs.map +1 -0
  85. package/dist/env/index.d.mts +4 -0
  86. package/dist/env/index.mjs +5 -0
  87. package/dist/env/logger.d.mts +49 -0
  88. package/dist/env/logger.mjs +82 -0
  89. package/dist/env/logger.mjs.map +1 -0
  90. package/dist/error/codes.d.mts +199 -0
  91. package/dist/error/codes.mjs +57 -0
  92. package/dist/error/codes.mjs.map +1 -0
  93. package/dist/error/index.d.mts +20 -0
  94. package/dist/error/index.mjs +30 -0
  95. package/dist/error/index.mjs.map +1 -0
  96. package/dist/index.d.mts +8 -0
  97. package/dist/index.mjs +1 -0
  98. package/dist/oauth2/client-credentials-token.d.mts +37 -0
  99. package/dist/oauth2/client-credentials-token.mjs +55 -0
  100. package/dist/oauth2/client-credentials-token.mjs.map +1 -0
  101. package/dist/oauth2/create-authorization-url.d.mts +46 -0
  102. package/dist/oauth2/create-authorization-url.mjs +43 -0
  103. package/dist/oauth2/create-authorization-url.mjs.map +1 -0
  104. package/dist/oauth2/index.d.mts +8 -0
  105. package/dist/oauth2/index.mjs +8 -0
  106. package/dist/oauth2/oauth-provider.d.mts +195 -0
  107. package/dist/oauth2/refresh-access-token.d.mts +36 -0
  108. package/dist/oauth2/refresh-access-token.mjs +59 -0
  109. package/dist/oauth2/refresh-access-token.mjs.map +1 -0
  110. package/dist/oauth2/utils.d.mts +8 -0
  111. package/dist/oauth2/utils.mjs +28 -0
  112. package/dist/oauth2/utils.mjs.map +1 -0
  113. package/dist/oauth2/validate-authorization-code.d.mts +56 -0
  114. package/dist/oauth2/validate-authorization-code.mjs +72 -0
  115. package/dist/oauth2/validate-authorization-code.mjs.map +1 -0
  116. package/dist/oauth2/verify.d.mts +43 -0
  117. package/dist/oauth2/verify.mjs +96 -0
  118. package/dist/oauth2/verify.mjs.map +1 -0
  119. package/dist/social-providers/apple.d.mts +120 -0
  120. package/dist/social-providers/apple.mjs +105 -0
  121. package/dist/social-providers/apple.mjs.map +1 -0
  122. package/dist/social-providers/atlassian.d.mts +73 -0
  123. package/dist/social-providers/atlassian.mjs +84 -0
  124. package/dist/social-providers/atlassian.mjs.map +1 -0
  125. package/dist/social-providers/cognito.d.mts +88 -0
  126. package/dist/social-providers/cognito.mjs +166 -0
  127. package/dist/social-providers/cognito.mjs.map +1 -0
  128. package/dist/social-providers/discord.d.mts +127 -0
  129. package/dist/social-providers/discord.mjs +65 -0
  130. package/dist/social-providers/discord.mjs.map +1 -0
  131. package/dist/social-providers/dropbox.d.mts +72 -0
  132. package/dist/social-providers/dropbox.mjs +76 -0
  133. package/dist/social-providers/dropbox.mjs.map +1 -0
  134. package/dist/social-providers/facebook.d.mts +82 -0
  135. package/dist/social-providers/facebook.mjs +121 -0
  136. package/dist/social-providers/facebook.mjs.map +1 -0
  137. package/dist/social-providers/figma.d.mts +64 -0
  138. package/dist/social-providers/figma.mjs +87 -0
  139. package/dist/social-providers/figma.mjs.map +1 -0
  140. package/dist/social-providers/github.d.mts +105 -0
  141. package/dist/social-providers/github.mjs +97 -0
  142. package/dist/social-providers/github.mjs.map +1 -0
  143. package/dist/social-providers/gitlab.d.mts +126 -0
  144. package/dist/social-providers/gitlab.mjs +83 -0
  145. package/dist/social-providers/gitlab.mjs.map +1 -0
  146. package/dist/social-providers/google.d.mts +100 -0
  147. package/dist/social-providers/google.mjs +109 -0
  148. package/dist/social-providers/google.mjs.map +1 -0
  149. package/dist/social-providers/huggingface.d.mts +86 -0
  150. package/dist/social-providers/huggingface.mjs +76 -0
  151. package/dist/social-providers/huggingface.mjs.map +1 -0
  152. package/dist/social-providers/index.d.mts +1725 -0
  153. package/dist/social-providers/index.mjs +77 -0
  154. package/dist/social-providers/index.mjs.map +1 -0
  155. package/dist/social-providers/kakao.d.mts +164 -0
  156. package/dist/social-providers/kakao.mjs +73 -0
  157. package/dist/social-providers/kakao.mjs.map +1 -0
  158. package/dist/social-providers/kick.d.mts +76 -0
  159. package/dist/social-providers/kick.mjs +72 -0
  160. package/dist/social-providers/kick.mjs.map +1 -0
  161. package/dist/social-providers/line.d.mts +108 -0
  162. package/dist/social-providers/line.mjs +114 -0
  163. package/dist/social-providers/line.mjs.map +1 -0
  164. package/dist/social-providers/linear.d.mts +71 -0
  165. package/dist/social-providers/linear.mjs +89 -0
  166. package/dist/social-providers/linear.mjs.map +1 -0
  167. package/dist/social-providers/linkedin.d.mts +70 -0
  168. package/dist/social-providers/linkedin.mjs +77 -0
  169. package/dist/social-providers/linkedin.mjs.map +1 -0
  170. package/dist/social-providers/microsoft-entra-id.d.mts +175 -0
  171. package/dist/social-providers/microsoft-entra-id.mjs +107 -0
  172. package/dist/social-providers/microsoft-entra-id.mjs.map +1 -0
  173. package/dist/social-providers/naver.d.mts +95 -0
  174. package/dist/social-providers/naver.mjs +68 -0
  175. package/dist/social-providers/naver.mjs.map +1 -0
  176. package/dist/social-providers/notion.d.mts +67 -0
  177. package/dist/social-providers/notion.mjs +76 -0
  178. package/dist/social-providers/notion.mjs.map +1 -0
  179. package/dist/social-providers/paybin.d.mts +74 -0
  180. package/dist/social-providers/paybin.mjs +86 -0
  181. package/dist/social-providers/paybin.mjs.map +1 -0
  182. package/dist/social-providers/paypal.d.mts +132 -0
  183. package/dist/social-providers/paypal.mjs +145 -0
  184. package/dist/social-providers/paypal.mjs.map +1 -0
  185. package/dist/social-providers/polar.d.mts +77 -0
  186. package/dist/social-providers/polar.mjs +74 -0
  187. package/dist/social-providers/polar.mjs.map +1 -0
  188. package/dist/social-providers/reddit.d.mts +65 -0
  189. package/dist/social-providers/reddit.mjs +84 -0
  190. package/dist/social-providers/reddit.mjs.map +1 -0
  191. package/dist/social-providers/roblox.d.mts +73 -0
  192. package/dist/social-providers/roblox.mjs +60 -0
  193. package/dist/social-providers/roblox.mjs.map +1 -0
  194. package/dist/social-providers/salesforce.d.mts +82 -0
  195. package/dist/social-providers/salesforce.mjs +92 -0
  196. package/dist/social-providers/salesforce.mjs.map +1 -0
  197. package/dist/social-providers/slack.d.mts +86 -0
  198. package/dist/social-providers/slack.mjs +69 -0
  199. package/dist/social-providers/slack.mjs.map +1 -0
  200. package/dist/social-providers/spotify.d.mts +66 -0
  201. package/dist/social-providers/spotify.mjs +72 -0
  202. package/dist/social-providers/spotify.mjs.map +1 -0
  203. package/dist/social-providers/tiktok.d.mts +171 -0
  204. package/dist/social-providers/tiktok.mjs +63 -0
  205. package/dist/social-providers/tiktok.mjs.map +1 -0
  206. package/dist/social-providers/twitch.d.mts +82 -0
  207. package/dist/social-providers/twitch.mjs +79 -0
  208. package/dist/social-providers/twitch.mjs.map +1 -0
  209. package/dist/social-providers/twitter.d.mts +129 -0
  210. package/dist/social-providers/twitter.mjs +88 -0
  211. package/dist/social-providers/twitter.mjs.map +1 -0
  212. package/dist/social-providers/vercel.d.mts +65 -0
  213. package/dist/social-providers/vercel.mjs +62 -0
  214. package/dist/social-providers/vercel.mjs.map +1 -0
  215. package/dist/social-providers/vk.d.mts +73 -0
  216. package/dist/social-providers/vk.mjs +84 -0
  217. package/dist/social-providers/vk.mjs.map +1 -0
  218. package/dist/social-providers/zoom.d.mts +173 -0
  219. package/dist/social-providers/zoom.mjs +73 -0
  220. package/dist/social-providers/zoom.mjs.map +1 -0
  221. package/dist/types/context.d.mts +267 -0
  222. package/dist/types/cookie.d.mts +16 -0
  223. package/dist/types/helper.d.mts +10 -0
  224. package/dist/types/index.d.mts +8 -0
  225. package/dist/types/init-options.d.mts +1314 -0
  226. package/dist/types/plugin-client.d.mts +112 -0
  227. package/dist/types/plugin.d.mts +125 -0
  228. package/dist/utils/db.d.mts +12 -0
  229. package/dist/utils/db.mjs +17 -0
  230. package/dist/utils/db.mjs.map +1 -0
  231. package/dist/utils/deprecate.d.mts +10 -0
  232. package/dist/utils/deprecate.mjs +18 -0
  233. package/dist/utils/deprecate.mjs.map +1 -0
  234. package/dist/utils/error-codes.d.mts +13 -0
  235. package/dist/utils/error-codes.mjs +12 -0
  236. package/dist/utils/error-codes.mjs.map +1 -0
  237. package/dist/utils/id.d.mts +5 -0
  238. package/dist/utils/id.mjs +10 -0
  239. package/dist/utils/id.mjs.map +1 -0
  240. package/dist/utils/ip.d.mts +55 -0
  241. package/dist/utils/ip.mjs +119 -0
  242. package/dist/utils/ip.mjs.map +1 -0
  243. package/dist/utils/json.d.mts +5 -0
  244. package/dist/utils/json.mjs +26 -0
  245. package/dist/utils/json.mjs.map +1 -0
  246. package/dist/utils/string.d.mts +5 -0
  247. package/dist/utils/string.mjs +8 -0
  248. package/dist/utils/string.mjs.map +1 -0
  249. package/dist/utils/url.d.mts +21 -0
  250. package/dist/utils/url.mjs +33 -0
  251. package/dist/utils/url.mjs.map +1 -0
  252. package/package.json +147 -0
  253. package/src/api/index.ts +106 -0
  254. package/src/async_hooks/index.ts +40 -0
  255. package/src/async_hooks/pure.index.ts +46 -0
  256. package/src/context/endpoint-context.ts +50 -0
  257. package/src/context/global.ts +57 -0
  258. package/src/context/index.ts +23 -0
  259. package/src/context/request-state.test.ts +94 -0
  260. package/src/context/request-state.ts +91 -0
  261. package/src/context/transaction.ts +136 -0
  262. package/src/db/adapter/factory.ts +1362 -0
  263. package/src/db/adapter/get-default-field-name.ts +59 -0
  264. package/src/db/adapter/get-default-model-name.ts +51 -0
  265. package/src/db/adapter/get-field-attributes.ts +62 -0
  266. package/src/db/adapter/get-field-name.ts +43 -0
  267. package/src/db/adapter/get-id-field.ts +141 -0
  268. package/src/db/adapter/get-model-name.ts +36 -0
  269. package/src/db/adapter/index.ts +554 -0
  270. package/src/db/adapter/types.ts +171 -0
  271. package/src/db/adapter/utils.ts +61 -0
  272. package/src/db/get-tables.ts +296 -0
  273. package/src/db/index.ts +18 -0
  274. package/src/db/plugin.ts +11 -0
  275. package/src/db/schema/account.ts +34 -0
  276. package/src/db/schema/rate-limit.ts +21 -0
  277. package/src/db/schema/session.ts +17 -0
  278. package/src/db/schema/shared.ts +7 -0
  279. package/src/db/schema/user.ts +16 -0
  280. package/src/db/schema/verification.ts +15 -0
  281. package/src/db/test/get-tables.test.ts +116 -0
  282. package/src/db/type.ts +180 -0
  283. package/src/env/color-depth.ts +172 -0
  284. package/src/env/env-impl.ts +124 -0
  285. package/src/env/index.ts +23 -0
  286. package/src/env/logger.test.ts +34 -0
  287. package/src/env/logger.ts +145 -0
  288. package/src/error/codes.ts +58 -0
  289. package/src/error/index.ts +35 -0
  290. package/src/index.ts +1 -0
  291. package/src/oauth2/client-credentials-token.ts +102 -0
  292. package/src/oauth2/create-authorization-url.ts +87 -0
  293. package/src/oauth2/index.ts +26 -0
  294. package/src/oauth2/oauth-provider.ts +222 -0
  295. package/src/oauth2/refresh-access-token.ts +124 -0
  296. package/src/oauth2/utils.ts +38 -0
  297. package/src/oauth2/validate-authorization-code.ts +149 -0
  298. package/src/oauth2/validate-token.test.ts +174 -0
  299. package/src/oauth2/verify.ts +221 -0
  300. package/src/social-providers/apple.ts +223 -0
  301. package/src/social-providers/atlassian.ts +132 -0
  302. package/src/social-providers/cognito.ts +279 -0
  303. package/src/social-providers/discord.ts +169 -0
  304. package/src/social-providers/dropbox.ts +112 -0
  305. package/src/social-providers/facebook.ts +206 -0
  306. package/src/social-providers/figma.ts +117 -0
  307. package/src/social-providers/github.ts +184 -0
  308. package/src/social-providers/gitlab.ts +155 -0
  309. package/src/social-providers/google.ts +199 -0
  310. package/src/social-providers/huggingface.ts +118 -0
  311. package/src/social-providers/index.ts +127 -0
  312. package/src/social-providers/kakao.ts +178 -0
  313. package/src/social-providers/kick.ts +109 -0
  314. package/src/social-providers/line.ts +169 -0
  315. package/src/social-providers/linear.ts +121 -0
  316. package/src/social-providers/linkedin.ts +110 -0
  317. package/src/social-providers/microsoft-entra-id.ts +259 -0
  318. package/src/social-providers/naver.ts +112 -0
  319. package/src/social-providers/notion.ts +108 -0
  320. package/src/social-providers/paybin.ts +122 -0
  321. package/src/social-providers/paypal.ts +263 -0
  322. package/src/social-providers/polar.ts +110 -0
  323. package/src/social-providers/reddit.ts +122 -0
  324. package/src/social-providers/roblox.ts +111 -0
  325. package/src/social-providers/salesforce.ts +159 -0
  326. package/src/social-providers/slack.ts +111 -0
  327. package/src/social-providers/spotify.ts +93 -0
  328. package/src/social-providers/tiktok.ts +209 -0
  329. package/src/social-providers/twitch.ts +111 -0
  330. package/src/social-providers/twitter.ts +198 -0
  331. package/src/social-providers/vercel.ts +87 -0
  332. package/src/social-providers/vk.ts +124 -0
  333. package/src/social-providers/zoom.ts +238 -0
  334. package/src/types/context.ts +396 -0
  335. package/src/types/cookie.ts +10 -0
  336. package/src/types/helper.ts +26 -0
  337. package/src/types/index.ts +32 -0
  338. package/src/types/init-options.ts +1529 -0
  339. package/src/types/plugin-client.ts +127 -0
  340. package/src/types/plugin.ts +157 -0
  341. package/src/utils/db.ts +20 -0
  342. package/src/utils/deprecate.test.ts +72 -0
  343. package/src/utils/deprecate.ts +21 -0
  344. package/src/utils/error-codes.ts +65 -0
  345. package/src/utils/id.ts +5 -0
  346. package/src/utils/ip.test.ts +255 -0
  347. package/src/utils/ip.ts +211 -0
  348. package/src/utils/json.ts +25 -0
  349. package/src/utils/string.ts +3 -0
  350. package/src/utils/url.ts +43 -0
  351. package/tsconfig.json +7 -0
  352. package/tsdown.config.ts +35 -0
  353. package/vitest.config.ts +3 -0
@@ -0,0 +1,263 @@
1
+ import { base64 } from "@better-auth/utils/base64";
2
+ import { betterFetch } from "@better-fetch/fetch";
3
+ import { decodeJwt } from "jose";
4
+ import { logger } from "../env";
5
+ import { BetterAuthError } from "../error";
6
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
7
+ import { createAuthorizationURL } from "../oauth2";
8
+
9
+ export interface PayPalProfile {
10
+ user_id: string;
11
+ name: string;
12
+ given_name: string;
13
+ family_name: string;
14
+ middle_name?: string | undefined;
15
+ picture?: string | undefined;
16
+ email: string;
17
+ email_verified: boolean;
18
+ gender?: string | undefined;
19
+ birthdate?: string | undefined;
20
+ zoneinfo?: string | undefined;
21
+ locale?: string | undefined;
22
+ phone_number?: string | undefined;
23
+ address?:
24
+ | {
25
+ street_address?: string;
26
+ locality?: string;
27
+ region?: string;
28
+ postal_code?: string;
29
+ country?: string;
30
+ }
31
+ | undefined;
32
+ verified_account?: boolean | undefined;
33
+ account_type?: string | undefined;
34
+ age_range?: string | undefined;
35
+ payer_id?: string | undefined;
36
+ }
37
+
38
+ export interface PayPalTokenResponse {
39
+ scope?: string | undefined;
40
+ access_token: string;
41
+ refresh_token?: string | undefined;
42
+ token_type: "Bearer";
43
+ id_token?: string | undefined;
44
+ expires_in: number;
45
+ nonce?: string | undefined;
46
+ }
47
+
48
+ export interface PayPalOptions extends ProviderOptions<PayPalProfile> {
49
+ clientId: string;
50
+ /**
51
+ * PayPal environment - 'sandbox' for testing, 'live' for production
52
+ * @default 'sandbox'
53
+ */
54
+ environment?: ("sandbox" | "live") | undefined;
55
+ /**
56
+ * Whether to request shipping address information
57
+ * @default false
58
+ */
59
+ requestShippingAddress?: boolean | undefined;
60
+ }
61
+
62
+ export const paypal = (options: PayPalOptions) => {
63
+ const environment = options.environment || "sandbox";
64
+ const isSandbox = environment === "sandbox";
65
+
66
+ const authorizationEndpoint = isSandbox
67
+ ? "https://www.sandbox.paypal.com/signin/authorize"
68
+ : "https://www.paypal.com/signin/authorize";
69
+
70
+ const tokenEndpoint = isSandbox
71
+ ? "https://api-m.sandbox.paypal.com/v1/oauth2/token"
72
+ : "https://api-m.paypal.com/v1/oauth2/token";
73
+
74
+ const userInfoEndpoint = isSandbox
75
+ ? "https://api-m.sandbox.paypal.com/v1/identity/oauth2/userinfo"
76
+ : "https://api-m.paypal.com/v1/identity/oauth2/userinfo";
77
+
78
+ return {
79
+ id: "paypal",
80
+ name: "PayPal",
81
+ async createAuthorizationURL({ state, codeVerifier, redirectURI }) {
82
+ if (!options.clientId || !options.clientSecret) {
83
+ logger.error(
84
+ "Client Id and Client Secret is required for PayPal. Make sure to provide them in the options.",
85
+ );
86
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
87
+ }
88
+
89
+ /**
90
+ * Log in with PayPal doesn't use traditional OAuth2 scopes
91
+ * Instead, permissions are configured in the PayPal Developer Dashboard
92
+ * We don't pass any scopes to avoid "invalid scope" errors
93
+ **/
94
+
95
+ const _scopes: string[] = [];
96
+
97
+ const url = await createAuthorizationURL({
98
+ id: "paypal",
99
+ options,
100
+ authorizationEndpoint,
101
+ scopes: _scopes,
102
+ state,
103
+ codeVerifier,
104
+ redirectURI,
105
+ prompt: options.prompt,
106
+ });
107
+ return url;
108
+ },
109
+
110
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
111
+ /**
112
+ * PayPal requires Basic Auth for token exchange
113
+ **/
114
+
115
+ const credentials = base64.encode(
116
+ `${options.clientId}:${options.clientSecret}`,
117
+ );
118
+
119
+ try {
120
+ const response = await betterFetch(tokenEndpoint, {
121
+ method: "POST",
122
+ headers: {
123
+ Authorization: `Basic ${credentials}`,
124
+ Accept: "application/json",
125
+ "Accept-Language": "en_US",
126
+ "Content-Type": "application/x-www-form-urlencoded",
127
+ },
128
+ body: new URLSearchParams({
129
+ grant_type: "authorization_code",
130
+ code: code,
131
+ redirect_uri: redirectURI,
132
+ }).toString(),
133
+ });
134
+
135
+ if (!response.data) {
136
+ throw new BetterAuthError("FAILED_TO_GET_ACCESS_TOKEN");
137
+ }
138
+
139
+ const data = response.data as PayPalTokenResponse;
140
+
141
+ const result = {
142
+ accessToken: data.access_token,
143
+ refreshToken: data.refresh_token,
144
+ accessTokenExpiresAt: data.expires_in
145
+ ? new Date(Date.now() + data.expires_in * 1000)
146
+ : undefined,
147
+ idToken: data.id_token,
148
+ };
149
+
150
+ return result;
151
+ } catch (error) {
152
+ logger.error("PayPal token exchange failed:", error);
153
+ throw new BetterAuthError("FAILED_TO_GET_ACCESS_TOKEN");
154
+ }
155
+ },
156
+
157
+ refreshAccessToken: options.refreshAccessToken
158
+ ? options.refreshAccessToken
159
+ : async (refreshToken) => {
160
+ const credentials = base64.encode(
161
+ `${options.clientId}:${options.clientSecret}`,
162
+ );
163
+
164
+ try {
165
+ const response = await betterFetch(tokenEndpoint, {
166
+ method: "POST",
167
+ headers: {
168
+ Authorization: `Basic ${credentials}`,
169
+ Accept: "application/json",
170
+ "Accept-Language": "en_US",
171
+ "Content-Type": "application/x-www-form-urlencoded",
172
+ },
173
+ body: new URLSearchParams({
174
+ grant_type: "refresh_token",
175
+ refresh_token: refreshToken,
176
+ }).toString(),
177
+ });
178
+
179
+ if (!response.data) {
180
+ throw new BetterAuthError("FAILED_TO_REFRESH_ACCESS_TOKEN");
181
+ }
182
+
183
+ const data = response.data as any;
184
+ return {
185
+ accessToken: data.access_token,
186
+ refreshToken: data.refresh_token,
187
+ accessTokenExpiresAt: data.expires_in
188
+ ? new Date(Date.now() + data.expires_in * 1000)
189
+ : undefined,
190
+ };
191
+ } catch (error) {
192
+ logger.error("PayPal token refresh failed:", error);
193
+ throw new BetterAuthError("FAILED_TO_REFRESH_ACCESS_TOKEN");
194
+ }
195
+ },
196
+
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
+ async getUserInfo(token) {
214
+ if (options.getUserInfo) {
215
+ return options.getUserInfo(token);
216
+ }
217
+
218
+ if (!token.accessToken) {
219
+ logger.error("Access token is required to fetch PayPal user info");
220
+ return null;
221
+ }
222
+
223
+ try {
224
+ const response = await betterFetch<PayPalProfile>(
225
+ `${userInfoEndpoint}?schema=paypalv1.1`,
226
+ {
227
+ headers: {
228
+ Authorization: `Bearer ${token.accessToken}`,
229
+ Accept: "application/json",
230
+ },
231
+ },
232
+ );
233
+
234
+ if (!response.data) {
235
+ logger.error("Failed to fetch user info from PayPal");
236
+ return null;
237
+ }
238
+
239
+ const userInfo = response.data;
240
+ const userMap = await options.mapProfileToUser?.(userInfo);
241
+
242
+ const result = {
243
+ user: {
244
+ id: userInfo.user_id,
245
+ name: userInfo.name,
246
+ email: userInfo.email,
247
+ image: userInfo.picture,
248
+ emailVerified: userInfo.email_verified,
249
+ ...userMap,
250
+ },
251
+ data: userInfo,
252
+ };
253
+
254
+ return result;
255
+ } catch (error) {
256
+ logger.error("Failed to fetch user info from PayPal:", error);
257
+ return null;
258
+ }
259
+ },
260
+
261
+ options,
262
+ } satisfies OAuthProvider<PayPalProfile>;
263
+ };
@@ -0,0 +1,110 @@
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 PolarProfile {
10
+ id: string;
11
+ email: string;
12
+ username: string;
13
+ avatar_url: string;
14
+ github_username?: string | undefined;
15
+ account_id?: string | undefined;
16
+ public_name?: string | undefined;
17
+ email_verified?: boolean | undefined;
18
+ profile_settings?:
19
+ | {
20
+ profile_settings_enabled?: boolean;
21
+ profile_settings_public_name?: string;
22
+ profile_settings_public_avatar?: string;
23
+ profile_settings_public_bio?: string;
24
+ profile_settings_public_location?: string;
25
+ profile_settings_public_website?: string;
26
+ profile_settings_public_twitter?: string;
27
+ profile_settings_public_github?: string;
28
+ profile_settings_public_email?: string;
29
+ }
30
+ | undefined;
31
+ }
32
+
33
+ export interface PolarOptions extends ProviderOptions<PolarProfile> {}
34
+
35
+ export const polar = (options: PolarOptions) => {
36
+ return {
37
+ id: "polar",
38
+ name: "Polar",
39
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
40
+ const _scopes = options.disableDefaultScope
41
+ ? []
42
+ : ["openid", "profile", "email"];
43
+ if (options.scope) _scopes.push(...options.scope);
44
+ if (scopes) _scopes.push(...scopes);
45
+ return createAuthorizationURL({
46
+ id: "polar",
47
+ options,
48
+ authorizationEndpoint: "https://polar.sh/oauth2/authorize",
49
+ scopes: _scopes,
50
+ state,
51
+ codeVerifier,
52
+ redirectURI,
53
+ prompt: options.prompt,
54
+ });
55
+ },
56
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
57
+ return validateAuthorizationCode({
58
+ code,
59
+ codeVerifier,
60
+ redirectURI,
61
+ options,
62
+ tokenEndpoint: "https://api.polar.sh/v1/oauth2/token",
63
+ });
64
+ },
65
+ refreshAccessToken: options.refreshAccessToken
66
+ ? options.refreshAccessToken
67
+ : async (refreshToken) => {
68
+ return refreshAccessToken({
69
+ refreshToken,
70
+ options: {
71
+ clientId: options.clientId,
72
+ clientKey: options.clientKey,
73
+ clientSecret: options.clientSecret,
74
+ },
75
+ tokenEndpoint: "https://api.polar.sh/v1/oauth2/token",
76
+ });
77
+ },
78
+ async getUserInfo(token) {
79
+ if (options.getUserInfo) {
80
+ return options.getUserInfo(token);
81
+ }
82
+ const { data: profile, error } = await betterFetch<PolarProfile>(
83
+ "https://api.polar.sh/v1/oauth2/userinfo",
84
+ {
85
+ headers: {
86
+ Authorization: `Bearer ${token.accessToken}`,
87
+ },
88
+ },
89
+ );
90
+ if (error) {
91
+ return null;
92
+ }
93
+ const userMap = await options.mapProfileToUser?.(profile);
94
+ // Polar may provide email_verified claim, but it's not guaranteed.
95
+ // We check for it first, then default to false for security consistency.
96
+ return {
97
+ user: {
98
+ id: profile.id,
99
+ name: profile.public_name || profile.username,
100
+ email: profile.email,
101
+ image: profile.avatar_url,
102
+ emailVerified: profile.email_verified ?? false,
103
+ ...userMap,
104
+ },
105
+ data: profile,
106
+ };
107
+ },
108
+ options,
109
+ } satisfies OAuthProvider<PolarProfile>;
110
+ };
@@ -0,0 +1,122 @@
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
+
108
+ return {
109
+ user: {
110
+ id: profile.id,
111
+ name: profile.name,
112
+ email: profile.oauth_client_id,
113
+ emailVerified: profile.has_verified_email,
114
+ image: profile.icon_img?.split("?")[0]!,
115
+ ...userMap,
116
+ },
117
+ data: profile,
118
+ };
119
+ },
120
+ options,
121
+ } satisfies OAuthProvider<RedditProfile>;
122
+ };
@@ -0,0 +1,111 @@
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
+ return {
37
+ id: "roblox",
38
+ name: "Roblox",
39
+ createAuthorizationURL({ state, scopes, redirectURI }) {
40
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile"];
41
+ if (options.scope) _scopes.push(...options.scope);
42
+ if (scopes) _scopes.push(...scopes);
43
+ return new URL(
44
+ `https://apis.roblox.com/oauth/v1/authorize?scope=${_scopes.join(
45
+ "+",
46
+ )}&response_type=code&client_id=${
47
+ options.clientId
48
+ }&redirect_uri=${encodeURIComponent(
49
+ options.redirectURI || redirectURI,
50
+ )}&state=${state}&prompt=${options.prompt || "select_account consent"}`,
51
+ );
52
+ },
53
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
54
+ return validateAuthorizationCode({
55
+ code,
56
+ redirectURI: options.redirectURI || redirectURI,
57
+ options,
58
+ tokenEndpoint: "https://apis.roblox.com/oauth/v1/token",
59
+ authentication: "post",
60
+ });
61
+ },
62
+ refreshAccessToken: options.refreshAccessToken
63
+ ? options.refreshAccessToken
64
+ : async (refreshToken) => {
65
+ return refreshAccessToken({
66
+ refreshToken,
67
+ options: {
68
+ clientId: options.clientId,
69
+ clientKey: options.clientKey,
70
+ clientSecret: options.clientSecret,
71
+ },
72
+ tokenEndpoint: "https://apis.roblox.com/oauth/v1/token",
73
+ });
74
+ },
75
+ async getUserInfo(token) {
76
+ if (options.getUserInfo) {
77
+ return options.getUserInfo(token);
78
+ }
79
+ const { data: profile, error } = await betterFetch<RobloxProfile>(
80
+ "https://apis.roblox.com/oauth/v1/userinfo",
81
+ {
82
+ headers: {
83
+ authorization: `Bearer ${token.accessToken}`,
84
+ },
85
+ },
86
+ );
87
+
88
+ if (error) {
89
+ return null;
90
+ }
91
+
92
+ const userMap = await options.mapProfileToUser?.(profile);
93
+ // Roblox does not provide email or email_verified claim.
94
+ // We default to false for security consistency.
95
+ return {
96
+ user: {
97
+ id: profile.sub,
98
+ name: profile.nickname || profile.preferred_username || "",
99
+ image: profile.picture,
100
+ email: profile.preferred_username || null, // Roblox does not provide email
101
+ emailVerified: false,
102
+ ...userMap,
103
+ },
104
+ data: {
105
+ ...profile,
106
+ },
107
+ };
108
+ },
109
+ options,
110
+ } satisfies OAuthProvider<RobloxProfile>;
111
+ };