@better-auth/core 1.5.0-beta.2 → 1.5.0-beta.20

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 (331) hide show
  1. package/LICENSE.md +15 -12
  2. package/README.md +17 -0
  3. package/dist/api/index.d.mts +280 -2
  4. package/dist/api/index.mjs +4 -2
  5. package/dist/api/index.mjs.map +1 -0
  6. package/dist/async_hooks/index.d.mts +2 -1
  7. package/dist/async_hooks/index.mjs +2 -1
  8. package/dist/async_hooks/index.mjs.map +1 -0
  9. package/dist/async_hooks/pure.index.d.mts +2 -1
  10. package/dist/async_hooks/pure.index.mjs +2 -1
  11. package/dist/async_hooks/pure.index.mjs.map +1 -0
  12. package/dist/context/endpoint-context.d.mts +20 -0
  13. package/dist/context/endpoint-context.mjs +32 -0
  14. package/dist/context/endpoint-context.mjs.map +1 -0
  15. package/dist/context/global.d.mts +7 -0
  16. package/dist/context/global.mjs +38 -0
  17. package/dist/context/global.mjs.map +1 -0
  18. package/dist/context/index.d.mts +5 -53
  19. package/dist/context/index.mjs +5 -2
  20. package/dist/context/request-state.d.mts +28 -0
  21. package/dist/context/request-state.mjs +50 -0
  22. package/dist/context/request-state.mjs.map +1 -0
  23. package/dist/context/transaction.d.mts +25 -0
  24. package/dist/context/transaction.mjs +96 -0
  25. package/dist/context/transaction.mjs.map +1 -0
  26. package/dist/db/adapter/factory.d.mts +20 -0
  27. package/dist/db/adapter/factory.mjs +725 -0
  28. package/dist/db/adapter/factory.mjs.map +1 -0
  29. package/dist/db/adapter/get-default-field-name.d.mts +19 -0
  30. package/dist/db/adapter/get-default-field-name.mjs +39 -0
  31. package/dist/db/adapter/get-default-field-name.mjs.map +1 -0
  32. package/dist/db/adapter/get-default-model-name.d.mts +13 -0
  33. package/dist/db/adapter/get-default-model-name.mjs +33 -0
  34. package/dist/db/adapter/get-default-model-name.mjs.map +1 -0
  35. package/dist/db/adapter/get-field-attributes.d.mts +30 -0
  36. package/dist/db/adapter/get-field-attributes.mjs +40 -0
  37. package/dist/db/adapter/get-field-attributes.mjs.map +1 -0
  38. package/dist/db/adapter/get-field-name.d.mts +19 -0
  39. package/dist/db/adapter/get-field-name.mjs +34 -0
  40. package/dist/db/adapter/get-field-name.mjs.map +1 -0
  41. package/dist/db/adapter/get-id-field.d.mts +40 -0
  42. package/dist/db/adapter/get-id-field.mjs +68 -0
  43. package/dist/db/adapter/get-id-field.mjs.map +1 -0
  44. package/dist/db/adapter/get-model-name.d.mts +13 -0
  45. package/dist/db/adapter/get-model-name.mjs +24 -0
  46. package/dist/db/adapter/get-model-name.mjs.map +1 -0
  47. package/dist/db/adapter/index.d.mts +520 -2
  48. package/dist/db/adapter/index.mjs +27 -972
  49. package/dist/db/adapter/index.mjs.map +1 -0
  50. package/dist/db/adapter/types.d.mts +108 -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/{get-tables-CMc_Emww.mjs → db/get-tables.mjs} +52 -39
  56. package/dist/db/get-tables.mjs.map +1 -0
  57. package/dist/db/index.d.mts +10 -2
  58. package/dist/db/index.mjs +7 -60
  59. package/dist/db/plugin.d.mts +13 -0
  60. package/dist/db/schema/account.d.mts +30 -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 +20 -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 +25 -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 +24 -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 +23 -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 +169 -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 -2
  86. package/dist/env/index.mjs +3 -1
  87. package/dist/{index-BRBu0-5h.d.mts → env/logger.d.mts} +2 -35
  88. package/dist/env/logger.mjs +82 -0
  89. package/dist/env/logger.mjs.map +1 -0
  90. package/dist/error/codes.d.mts +69 -0
  91. package/dist/{error-C7mY-p0f.mjs → error/codes.mjs} +7 -30
  92. package/dist/error/codes.mjs.map +1 -0
  93. package/dist/error/index.d.mts +6 -187
  94. package/dist/error/index.mjs +29 -4
  95. package/dist/error/index.mjs.map +1 -0
  96. package/dist/index.d.mts +8 -2
  97. package/dist/oauth2/client-credentials-token.d.mts +58 -0
  98. package/dist/oauth2/client-credentials-token.mjs +67 -0
  99. package/dist/oauth2/client-credentials-token.mjs.map +1 -0
  100. package/dist/oauth2/create-authorization-url.d.mts +48 -0
  101. package/dist/oauth2/create-authorization-url.mjs +44 -0
  102. package/dist/oauth2/create-authorization-url.mjs.map +1 -0
  103. package/dist/oauth2/index.d.mts +8 -2
  104. package/dist/oauth2/index.mjs +7 -3
  105. package/dist/oauth2/oauth-provider.d.mts +195 -0
  106. package/dist/oauth2/refresh-access-token.d.mts +56 -0
  107. package/dist/oauth2/refresh-access-token.mjs +76 -0
  108. package/dist/oauth2/refresh-access-token.mjs.map +1 -0
  109. package/dist/oauth2/utils.d.mts +8 -0
  110. package/dist/oauth2/utils.mjs +28 -0
  111. package/dist/oauth2/utils.mjs.map +1 -0
  112. package/dist/oauth2/validate-authorization-code.d.mts +88 -0
  113. package/dist/oauth2/validate-authorization-code.mjs +83 -0
  114. package/dist/oauth2/validate-authorization-code.mjs.map +1 -0
  115. package/dist/oauth2/verify.d.mts +43 -0
  116. package/dist/oauth2/verify.mjs +96 -0
  117. package/dist/oauth2/verify.mjs.map +1 -0
  118. package/dist/social-providers/apple.d.mts +120 -0
  119. package/dist/social-providers/apple.mjs +105 -0
  120. package/dist/social-providers/apple.mjs.map +1 -0
  121. package/dist/social-providers/atlassian.d.mts +73 -0
  122. package/dist/social-providers/atlassian.mjs +84 -0
  123. package/dist/social-providers/atlassian.mjs.map +1 -0
  124. package/dist/social-providers/cognito.d.mts +88 -0
  125. package/dist/social-providers/cognito.mjs +166 -0
  126. package/dist/social-providers/cognito.mjs.map +1 -0
  127. package/dist/social-providers/discord.d.mts +127 -0
  128. package/dist/social-providers/discord.mjs +65 -0
  129. package/dist/social-providers/discord.mjs.map +1 -0
  130. package/dist/social-providers/dropbox.d.mts +72 -0
  131. package/dist/social-providers/dropbox.mjs +76 -0
  132. package/dist/social-providers/dropbox.mjs.map +1 -0
  133. package/dist/social-providers/facebook.d.mts +82 -0
  134. package/dist/social-providers/facebook.mjs +121 -0
  135. package/dist/social-providers/facebook.mjs.map +1 -0
  136. package/dist/social-providers/figma.d.mts +64 -0
  137. package/dist/social-providers/figma.mjs +87 -0
  138. package/dist/social-providers/figma.mjs.map +1 -0
  139. package/dist/social-providers/github.d.mts +105 -0
  140. package/dist/social-providers/github.mjs +97 -0
  141. package/dist/social-providers/github.mjs.map +1 -0
  142. package/dist/social-providers/gitlab.d.mts +126 -0
  143. package/dist/social-providers/gitlab.mjs +83 -0
  144. package/dist/social-providers/gitlab.mjs.map +1 -0
  145. package/dist/social-providers/google.d.mts +100 -0
  146. package/dist/social-providers/google.mjs +113 -0
  147. package/dist/social-providers/google.mjs.map +1 -0
  148. package/dist/social-providers/huggingface.d.mts +86 -0
  149. package/dist/social-providers/huggingface.mjs +76 -0
  150. package/dist/social-providers/huggingface.mjs.map +1 -0
  151. package/dist/social-providers/index.d.mts +1777 -2
  152. package/dist/social-providers/index.mjs +37 -2570
  153. package/dist/social-providers/index.mjs.map +1 -0
  154. package/dist/social-providers/kakao.d.mts +164 -0
  155. package/dist/social-providers/kakao.mjs +73 -0
  156. package/dist/social-providers/kakao.mjs.map +1 -0
  157. package/dist/social-providers/kick.d.mts +76 -0
  158. package/dist/social-providers/kick.mjs +72 -0
  159. package/dist/social-providers/kick.mjs.map +1 -0
  160. package/dist/social-providers/line.d.mts +108 -0
  161. package/dist/social-providers/line.mjs +114 -0
  162. package/dist/social-providers/line.mjs.map +1 -0
  163. package/dist/social-providers/linear.d.mts +71 -0
  164. package/dist/social-providers/linear.mjs +89 -0
  165. package/dist/social-providers/linear.mjs.map +1 -0
  166. package/dist/social-providers/linkedin.d.mts +70 -0
  167. package/dist/social-providers/linkedin.mjs +77 -0
  168. package/dist/social-providers/linkedin.mjs.map +1 -0
  169. package/dist/social-providers/microsoft-entra-id.d.mts +177 -0
  170. package/dist/social-providers/microsoft-entra-id.mjs +140 -0
  171. package/dist/social-providers/microsoft-entra-id.mjs.map +1 -0
  172. package/dist/social-providers/naver.d.mts +95 -0
  173. package/dist/social-providers/naver.mjs +68 -0
  174. package/dist/social-providers/naver.mjs.map +1 -0
  175. package/dist/social-providers/notion.d.mts +67 -0
  176. package/dist/social-providers/notion.mjs +76 -0
  177. package/dist/social-providers/notion.mjs.map +1 -0
  178. package/dist/social-providers/paybin.d.mts +74 -0
  179. package/dist/social-providers/paybin.mjs +86 -0
  180. package/dist/social-providers/paybin.mjs.map +1 -0
  181. package/dist/social-providers/paypal.d.mts +132 -0
  182. package/dist/social-providers/paypal.mjs +145 -0
  183. package/dist/social-providers/paypal.mjs.map +1 -0
  184. package/dist/social-providers/polar.d.mts +77 -0
  185. package/dist/social-providers/polar.mjs +74 -0
  186. package/dist/social-providers/polar.mjs.map +1 -0
  187. package/dist/social-providers/railway.d.mts +68 -0
  188. package/dist/social-providers/railway.mjs +78 -0
  189. package/dist/social-providers/railway.mjs.map +1 -0
  190. package/dist/social-providers/reddit.d.mts +65 -0
  191. package/dist/social-providers/reddit.mjs +84 -0
  192. package/dist/social-providers/reddit.mjs.map +1 -0
  193. package/dist/social-providers/roblox.d.mts +73 -0
  194. package/dist/social-providers/roblox.mjs +60 -0
  195. package/dist/social-providers/roblox.mjs.map +1 -0
  196. package/dist/social-providers/salesforce.d.mts +82 -0
  197. package/dist/social-providers/salesforce.mjs +92 -0
  198. package/dist/social-providers/salesforce.mjs.map +1 -0
  199. package/dist/social-providers/slack.d.mts +86 -0
  200. package/dist/social-providers/slack.mjs +69 -0
  201. package/dist/social-providers/slack.mjs.map +1 -0
  202. package/dist/social-providers/spotify.d.mts +66 -0
  203. package/dist/social-providers/spotify.mjs +72 -0
  204. package/dist/social-providers/spotify.mjs.map +1 -0
  205. package/dist/social-providers/tiktok.d.mts +171 -0
  206. package/dist/social-providers/tiktok.mjs +63 -0
  207. package/dist/social-providers/tiktok.mjs.map +1 -0
  208. package/dist/social-providers/twitch.d.mts +82 -0
  209. package/dist/social-providers/twitch.mjs +79 -0
  210. package/dist/social-providers/twitch.mjs.map +1 -0
  211. package/dist/social-providers/twitter.d.mts +129 -0
  212. package/dist/social-providers/twitter.mjs +88 -0
  213. package/dist/social-providers/twitter.mjs.map +1 -0
  214. package/dist/social-providers/vercel.d.mts +65 -0
  215. package/dist/social-providers/vercel.mjs +62 -0
  216. package/dist/social-providers/vercel.mjs.map +1 -0
  217. package/dist/social-providers/vk.d.mts +73 -0
  218. package/dist/social-providers/vk.mjs +84 -0
  219. package/dist/social-providers/vk.mjs.map +1 -0
  220. package/dist/social-providers/zoom.d.mts +166 -0
  221. package/dist/social-providers/zoom.mjs +73 -0
  222. package/dist/social-providers/zoom.mjs.map +1 -0
  223. package/dist/types/context.d.mts +274 -0
  224. package/dist/types/cookie.d.mts +16 -0
  225. package/dist/types/helper.d.mts +11 -0
  226. package/dist/types/index.d.mts +8 -0
  227. package/dist/types/init-options.d.mts +1298 -0
  228. package/dist/types/plugin-client.d.mts +113 -0
  229. package/dist/types/plugin.d.mts +125 -0
  230. package/dist/utils/db.d.mts +12 -0
  231. package/dist/utils/db.mjs +17 -0
  232. package/dist/utils/db.mjs.map +1 -0
  233. package/dist/utils/deprecate.d.mts +10 -0
  234. package/dist/utils/deprecate.mjs +18 -0
  235. package/dist/utils/deprecate.mjs.map +1 -0
  236. package/dist/utils/{index.d.mts → error-codes.d.mts} +9 -16
  237. package/dist/utils/error-codes.mjs +12 -0
  238. package/dist/utils/error-codes.mjs.map +1 -0
  239. package/dist/utils/id.d.mts +5 -0
  240. package/dist/utils/id.mjs +10 -0
  241. package/dist/utils/id.mjs.map +1 -0
  242. package/dist/utils/ip.d.mts +55 -0
  243. package/dist/utils/ip.mjs +119 -0
  244. package/dist/utils/ip.mjs.map +1 -0
  245. package/dist/utils/json.d.mts +5 -0
  246. package/dist/utils/json.mjs +26 -0
  247. package/dist/utils/json.mjs.map +1 -0
  248. package/dist/utils/string.d.mts +5 -0
  249. package/dist/utils/string.mjs +8 -0
  250. package/dist/utils/string.mjs.map +1 -0
  251. package/dist/utils/url.d.mts +21 -0
  252. package/dist/utils/url.mjs +33 -0
  253. package/dist/utils/url.mjs.map +1 -0
  254. package/package.json +36 -18
  255. package/src/context/endpoint-context.ts +7 -6
  256. package/src/context/global.ts +57 -0
  257. package/src/context/index.ts +2 -0
  258. package/src/context/request-state.ts +7 -6
  259. package/src/context/transaction.ts +77 -14
  260. package/src/db/adapter/factory.ts +54 -86
  261. package/src/db/adapter/get-default-model-name.ts +1 -1
  262. package/src/db/adapter/get-id-field.ts +4 -6
  263. package/src/db/adapter/index.ts +20 -15
  264. package/src/db/adapter/types.ts +2 -41
  265. package/src/db/get-tables.ts +54 -37
  266. package/src/db/index.ts +30 -5
  267. package/src/db/schema/account.ts +16 -3
  268. package/src/db/schema/rate-limit.ts +16 -1
  269. package/src/db/schema/session.ts +15 -3
  270. package/src/db/schema/user.ts +15 -3
  271. package/src/db/schema/verification.ts +16 -3
  272. package/src/db/test/get-tables.test.ts +33 -0
  273. package/src/db/type.ts +154 -1
  274. package/src/env/env-impl.ts +2 -2
  275. package/src/env/logger.ts +1 -1
  276. package/src/error/codes.ts +18 -1
  277. package/src/error/index.ts +2 -3
  278. package/src/oauth2/client-credentials-token.ts +26 -2
  279. package/src/oauth2/create-authorization-url.ts +4 -2
  280. package/src/oauth2/index.ts +3 -0
  281. package/src/oauth2/oauth-provider.ts +7 -1
  282. package/src/oauth2/refresh-access-token.test.ts +90 -0
  283. package/src/oauth2/refresh-access-token.ts +37 -4
  284. package/src/oauth2/validate-authorization-code.ts +55 -29
  285. package/src/oauth2/validate-token.test.ts +229 -0
  286. package/src/social-providers/apple.ts +38 -29
  287. package/src/social-providers/cognito.ts +6 -5
  288. package/src/social-providers/dropbox.ts +1 -1
  289. package/src/social-providers/facebook.ts +3 -3
  290. package/src/social-providers/figma.ts +5 -3
  291. package/src/social-providers/github.ts +26 -4
  292. package/src/social-providers/gitlab.ts +2 -2
  293. package/src/social-providers/google.ts +18 -14
  294. package/src/social-providers/huggingface.ts +1 -1
  295. package/src/social-providers/index.ts +9 -5
  296. package/src/social-providers/kakao.ts +1 -1
  297. package/src/social-providers/line.ts +1 -1
  298. package/src/social-providers/microsoft-entra-id.ts +84 -1
  299. package/src/social-providers/naver.ts +1 -1
  300. package/src/social-providers/notion.ts +1 -1
  301. package/src/social-providers/paybin.ts +1 -5
  302. package/src/social-providers/polar.ts +1 -1
  303. package/src/social-providers/railway.ts +100 -0
  304. package/src/social-providers/tiktok.ts +2 -1
  305. package/src/social-providers/vercel.ts +1 -1
  306. package/src/social-providers/zoom.ts +0 -8
  307. package/src/types/context.ts +241 -132
  308. package/src/types/cookie.ts +6 -4
  309. package/src/types/helper.ts +9 -0
  310. package/src/types/index.ts +19 -2
  311. package/src/types/init-options.ts +301 -186
  312. package/src/types/plugin-client.ts +1 -0
  313. package/src/types/plugin.ts +11 -6
  314. package/src/utils/db.ts +20 -0
  315. package/src/utils/deprecate.test.ts +71 -0
  316. package/src/utils/deprecate.ts +21 -0
  317. package/src/utils/error-codes.ts +12 -9
  318. package/src/utils/ip.test.ts +255 -0
  319. package/src/utils/ip.ts +211 -0
  320. package/src/utils/url.ts +43 -0
  321. package/.turbo/turbo-build.log +0 -43
  322. package/dist/context-DblZrIwO.mjs +0 -114
  323. package/dist/env-DbssmzoK.mjs +0 -245
  324. package/dist/index-BpRqx5_q.d.mts +0 -7833
  325. package/dist/oauth2-BjWM15hm.mjs +0 -326
  326. package/dist/utils/index.mjs +0 -4
  327. package/dist/utils-s65Fz0OM.mjs +0 -47
  328. package/src/utils/index.ts +0 -4
  329. package/tsconfig.json +0 -7
  330. package/tsdown.config.ts +0 -22
  331. package/vitest.config.ts +0 -3
@@ -1,7 +1,34 @@
1
1
  import { base64 } from "@better-auth/utils/base64";
2
2
  import { betterFetch } from "@better-fetch/fetch";
3
+ import type { AwaitableFunction } from "../types";
3
4
  import type { OAuth2Tokens, ProviderOptions } from "./oauth-provider";
4
5
 
6
+ export async function refreshAccessTokenRequest({
7
+ refreshToken,
8
+ options,
9
+ authentication,
10
+ extraParams,
11
+ resource,
12
+ }: {
13
+ refreshToken: string;
14
+ options: AwaitableFunction<Partial<ProviderOptions>>;
15
+ authentication?: ("basic" | "post") | undefined;
16
+ extraParams?: Record<string, string> | undefined;
17
+ resource?: (string | string[]) | undefined;
18
+ }) {
19
+ options = typeof options === "function" ? await options() : options;
20
+ return createRefreshAccessTokenRequest({
21
+ refreshToken,
22
+ options,
23
+ authentication,
24
+ extraParams,
25
+ resource,
26
+ });
27
+ }
28
+
29
+ /**
30
+ * @deprecated use async'd refreshAccessTokenRequest instead
31
+ */
5
32
  export function createRefreshAccessTokenRequest({
6
33
  refreshToken,
7
34
  options,
@@ -10,7 +37,7 @@ export function createRefreshAccessTokenRequest({
10
37
  resource,
11
38
  }: {
12
39
  refreshToken: string;
13
- options: Partial<ProviderOptions>;
40
+ options: ProviderOptions;
14
41
  authentication?: ("basic" | "post") | undefined;
15
42
  extraParams?: Record<string, string> | undefined;
16
43
  resource?: (string | string[]) | undefined;
@@ -80,10 +107,8 @@ export async function refreshAccessToken({
80
107
  tokenEndpoint: string;
81
108
  authentication?: ("basic" | "post") | undefined;
82
109
  extraParams?: Record<string, string> | undefined;
83
- /** @deprecated always "refresh_token" */
84
- grantType?: string | undefined;
85
110
  }): Promise<OAuth2Tokens> {
86
- const { body, headers } = createRefreshAccessTokenRequest({
111
+ const { body, headers } = await createRefreshAccessTokenRequest({
87
112
  refreshToken,
88
113
  options,
89
114
  authentication,
@@ -94,6 +119,7 @@ export async function refreshAccessToken({
94
119
  access_token: string;
95
120
  refresh_token?: string | undefined;
96
121
  expires_in?: number | undefined;
122
+ refresh_token_expires_in?: number | undefined;
97
123
  token_type?: string | undefined;
98
124
  scope?: string | undefined;
99
125
  id_token?: string | undefined;
@@ -120,5 +146,12 @@ export async function refreshAccessToken({
120
146
  );
121
147
  }
122
148
 
149
+ if (data.refresh_token_expires_in) {
150
+ const now = new Date();
151
+ tokens.refreshTokenExpiresAt = new Date(
152
+ now.getTime() + data.refresh_token_expires_in * 1000,
153
+ );
154
+ }
155
+
123
156
  return tokens;
124
157
  }
@@ -1,9 +1,48 @@
1
1
  import { base64 } from "@better-auth/utils/base64";
2
2
  import { betterFetch } from "@better-fetch/fetch";
3
- import { jwtVerify } from "jose";
3
+ import { createRemoteJWKSet, jwtVerify } from "jose";
4
+ import type { AwaitableFunction } from "../types";
4
5
  import type { ProviderOptions } from "./index";
5
6
  import { getOAuth2Tokens } from "./index";
6
7
 
8
+ export async function authorizationCodeRequest({
9
+ code,
10
+ codeVerifier,
11
+ redirectURI,
12
+ options,
13
+ authentication,
14
+ deviceId,
15
+ headers,
16
+ additionalParams = {},
17
+ resource,
18
+ }: {
19
+ code: string;
20
+ redirectURI: string;
21
+ options: AwaitableFunction<Partial<ProviderOptions>>;
22
+ codeVerifier?: string | undefined;
23
+ deviceId?: string | undefined;
24
+ authentication?: ("basic" | "post") | undefined;
25
+ headers?: Record<string, string> | undefined;
26
+ additionalParams?: Record<string, string> | undefined;
27
+ resource?: (string | string[]) | undefined;
28
+ }) {
29
+ options = typeof options === "function" ? await options() : options;
30
+ return createAuthorizationCodeRequest({
31
+ code,
32
+ codeVerifier,
33
+ redirectURI,
34
+ options,
35
+ authentication,
36
+ deviceId,
37
+ headers,
38
+ additionalParams,
39
+ resource,
40
+ });
41
+ }
42
+
43
+ /**
44
+ * @deprecated use async'd authorizationCodeRequest instead
45
+ */
7
46
  export function createAuthorizationCodeRequest({
8
47
  code,
9
48
  codeVerifier,
@@ -31,6 +70,7 @@ export function createAuthorizationCodeRequest({
31
70
  accept: "application/json",
32
71
  ...headers,
33
72
  };
73
+
34
74
  body.set("grant_type", "authorization_code");
35
75
  body.set("code", code);
36
76
  codeVerifier && body.set("code_verifier", codeVerifier);
@@ -90,7 +130,7 @@ export async function validateAuthorizationCode({
90
130
  }: {
91
131
  code: string;
92
132
  redirectURI: string;
93
- options: Partial<ProviderOptions>;
133
+ options: AwaitableFunction<Partial<ProviderOptions>>;
94
134
  codeVerifier?: string | undefined;
95
135
  deviceId?: string | undefined;
96
136
  tokenEndpoint: string;
@@ -99,7 +139,7 @@ export async function validateAuthorizationCode({
99
139
  additionalParams?: Record<string, string> | undefined;
100
140
  resource?: (string | string[]) | undefined;
101
141
  }) {
102
- const { body, headers: requestHeaders } = createAuthorizationCodeRequest({
142
+ const { body, headers: requestHeaders } = await authorizationCodeRequest({
103
143
  code,
104
144
  codeVerifier,
105
145
  redirectURI,
@@ -116,7 +156,6 @@ export async function validateAuthorizationCode({
116
156
  body: body,
117
157
  headers: requestHeaders,
118
158
  });
119
-
120
159
  if (error) {
121
160
  throw error;
122
161
  }
@@ -124,31 +163,18 @@ export async function validateAuthorizationCode({
124
163
  return tokens;
125
164
  }
126
165
 
127
- export async function validateToken(token: string, jwksEndpoint: string) {
128
- const { data, error } = await betterFetch<{
129
- keys: {
130
- kid: string;
131
- kty: string;
132
- use: string;
133
- n: string;
134
- e: string;
135
- x5c: string[];
136
- }[];
137
- }>(jwksEndpoint, {
138
- method: "GET",
139
- headers: {
140
- accept: "application/json",
141
- },
166
+ export async function validateToken(
167
+ token: string,
168
+ jwksEndpoint: string,
169
+ options?: {
170
+ audience?: string | string[];
171
+ issuer?: string | string[];
172
+ },
173
+ ) {
174
+ const jwks = createRemoteJWKSet(new URL(jwksEndpoint));
175
+ const verified = await jwtVerify(token, jwks, {
176
+ audience: options?.audience,
177
+ issuer: options?.issuer,
142
178
  });
143
- if (error) {
144
- throw error;
145
- }
146
- const keys = data["keys"];
147
- const header = JSON.parse(atob(token.split(".")[0]!));
148
- const key = keys.find((key) => key.kid === header.kid);
149
- if (!key) {
150
- throw new Error("Key not found");
151
- }
152
- const verified = await jwtVerify(token, key);
153
179
  return verified;
154
180
  }
@@ -0,0 +1,229 @@
1
+ import type { JWK } from "jose";
2
+ import { exportJWK, generateKeyPair, SignJWT } from "jose";
3
+ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
4
+ import { validateToken } from "./validate-authorization-code";
5
+
6
+ describe("validateToken", () => {
7
+ const originalFetch = globalThis.fetch;
8
+ const mockedFetch = vi.fn() as unknown as typeof fetch &
9
+ ReturnType<typeof vi.fn>;
10
+
11
+ beforeAll(() => {
12
+ globalThis.fetch = mockedFetch;
13
+ });
14
+
15
+ afterAll(() => {
16
+ globalThis.fetch = originalFetch;
17
+ });
18
+
19
+ async function createTestJWKS(alg: string, crv?: string) {
20
+ const { publicKey, privateKey } = await generateKeyPair(alg, {
21
+ crv,
22
+ extractable: true,
23
+ });
24
+ const publicJWK = await exportJWK(publicKey);
25
+ const privateJWK = await exportJWK(privateKey);
26
+ const kid = `test-key-${Date.now()}`;
27
+ publicJWK.kid = kid;
28
+ publicJWK.alg = alg;
29
+ privateJWK.kid = kid;
30
+ privateJWK.alg = alg;
31
+ return { publicJWK, privateJWK, kid, publicKey, privateKey };
32
+ }
33
+
34
+ async function createSignedToken(
35
+ privateKey: CryptoKey,
36
+ alg: string,
37
+ kid: string,
38
+ payload: Record<string, unknown> = {},
39
+ ) {
40
+ return await new SignJWT({
41
+ sub: "user-123",
42
+ email: "test@example.com",
43
+ iss: "https://example.com",
44
+ aud: "test-client",
45
+ ...payload,
46
+ })
47
+ .setProtectedHeader({ alg, kid })
48
+ .setIssuedAt()
49
+ .setExpirationTime("1h")
50
+ .sign(privateKey);
51
+ }
52
+
53
+ function mockJWKSResponse(...publicJWKs: JWK[]) {
54
+ mockedFetch.mockResolvedValueOnce(
55
+ new Response(JSON.stringify({ keys: publicJWKs }), {
56
+ status: 200,
57
+ headers: { "content-type": "application/json" },
58
+ }),
59
+ );
60
+ }
61
+
62
+ it("should verify RS256 signed token", async () => {
63
+ const { publicJWK, privateKey, kid } = await createTestJWKS("RS256");
64
+ const token = await createSignedToken(privateKey, "RS256", kid);
65
+ mockJWKSResponse(publicJWK);
66
+
67
+ const result = await validateToken(
68
+ token,
69
+ "https://example.com/.well-known/jwks",
70
+ );
71
+
72
+ expect(result).toBeDefined();
73
+ expect(result.payload.sub).toBe("user-123");
74
+ expect(result.payload.email).toBe("test@example.com");
75
+ });
76
+
77
+ it("should verify ES256 signed token", async () => {
78
+ const { publicJWK, privateKey, kid } = await createTestJWKS("ES256");
79
+ const token = await createSignedToken(privateKey, "ES256", kid);
80
+ mockJWKSResponse(publicJWK);
81
+
82
+ const result = await validateToken(
83
+ token,
84
+ "https://example.com/.well-known/jwks",
85
+ );
86
+
87
+ expect(result).toBeDefined();
88
+ expect(result.payload.sub).toBe("user-123");
89
+ });
90
+
91
+ it("should verify EdDSA (Ed25519) signed token", async () => {
92
+ const { publicJWK, privateKey, kid } = await createTestJWKS(
93
+ "EdDSA",
94
+ "Ed25519",
95
+ );
96
+ const token = await createSignedToken(privateKey, "EdDSA", kid);
97
+ mockJWKSResponse(publicJWK);
98
+
99
+ const result = await validateToken(
100
+ token,
101
+ "https://example.com/.well-known/jwks",
102
+ );
103
+
104
+ expect(result).toBeDefined();
105
+ expect(result.payload.sub).toBe("user-123");
106
+ });
107
+
108
+ it("should throw when kid doesn't match any key", async () => {
109
+ const { publicJWK, privateKey } = await createTestJWKS("RS256");
110
+ publicJWK.kid = "different-kid";
111
+ const token = await createSignedToken(privateKey, "RS256", "original-kid");
112
+ mockJWKSResponse(publicJWK);
113
+
114
+ await expect(
115
+ validateToken(token, "https://example.com/.well-known/jwks"),
116
+ ).rejects.toThrow();
117
+ });
118
+
119
+ it("should find correct key when multiple keys exist", async () => {
120
+ const key1 = await createTestJWKS("RS256");
121
+ const key2 = await createTestJWKS("RS256");
122
+ const key3 = await createTestJWKS("ES256");
123
+ const token = await createSignedToken(key2.privateKey, "RS256", key2.kid);
124
+ mockJWKSResponse(key1.publicJWK, key2.publicJWK, key3.publicJWK);
125
+
126
+ const result = await validateToken(
127
+ token,
128
+ "https://example.com/.well-known/jwks",
129
+ );
130
+
131
+ expect(result).toBeDefined();
132
+ expect(result.payload.sub).toBe("user-123");
133
+ });
134
+
135
+ it("should throw when JWKS returns empty keys array", async () => {
136
+ const { privateKey, kid } = await createTestJWKS("RS256");
137
+ const token = await createSignedToken(privateKey, "RS256", kid);
138
+ mockJWKSResponse();
139
+
140
+ await expect(
141
+ validateToken(token, "https://example.com/.well-known/jwks"),
142
+ ).rejects.toThrow();
143
+ });
144
+
145
+ it("should throw when JWKS fetch fails", async () => {
146
+ const { privateKey, kid } = await createTestJWKS("RS256");
147
+ const token = await createSignedToken(privateKey, "RS256", kid);
148
+ mockedFetch.mockResolvedValueOnce(
149
+ new Response("Internal Server Error", { status: 500 }),
150
+ );
151
+
152
+ await expect(
153
+ validateToken(token, "https://example.com/.well-known/jwks"),
154
+ ).rejects.toBeDefined();
155
+ });
156
+
157
+ it("should verify token with matching audience", async () => {
158
+ const { publicJWK, privateKey, kid } = await createTestJWKS("RS256");
159
+ const token = await createSignedToken(privateKey, "RS256", kid);
160
+ mockJWKSResponse(publicJWK);
161
+
162
+ const result = await validateToken(
163
+ token,
164
+ "https://example.com/.well-known/jwks",
165
+ { audience: "test-client" },
166
+ );
167
+
168
+ expect(result).toBeDefined();
169
+ expect(result.payload.aud).toBe("test-client");
170
+ });
171
+
172
+ it("should reject token with mismatched audience", async () => {
173
+ const { publicJWK, privateKey, kid } = await createTestJWKS("RS256");
174
+ const token = await createSignedToken(privateKey, "RS256", kid);
175
+ mockJWKSResponse(publicJWK);
176
+
177
+ await expect(
178
+ validateToken(token, "https://example.com/.well-known/jwks", {
179
+ audience: "wrong-client",
180
+ }),
181
+ ).rejects.toThrow();
182
+ });
183
+
184
+ it("should verify token with matching issuer", async () => {
185
+ const { publicJWK, privateKey, kid } = await createTestJWKS("RS256");
186
+ const token = await createSignedToken(privateKey, "RS256", kid);
187
+ mockJWKSResponse(publicJWK);
188
+
189
+ const result = await validateToken(
190
+ token,
191
+ "https://example.com/.well-known/jwks",
192
+ { issuer: "https://example.com" },
193
+ );
194
+
195
+ expect(result).toBeDefined();
196
+ expect(result.payload.iss).toBe("https://example.com");
197
+ });
198
+
199
+ it("should reject token with mismatched issuer", async () => {
200
+ const { publicJWK, privateKey, kid } = await createTestJWKS("RS256");
201
+ const token = await createSignedToken(privateKey, "RS256", kid);
202
+ mockJWKSResponse(publicJWK);
203
+
204
+ await expect(
205
+ validateToken(token, "https://example.com/.well-known/jwks", {
206
+ issuer: "https://wrong-issuer.com",
207
+ }),
208
+ ).rejects.toThrow();
209
+ });
210
+
211
+ it("should verify token with both audience and issuer", async () => {
212
+ const { publicJWK, privateKey, kid } = await createTestJWKS("RS256");
213
+ const token = await createSignedToken(privateKey, "RS256", kid);
214
+ mockJWKSResponse(publicJWK);
215
+
216
+ const result = await validateToken(
217
+ token,
218
+ "https://example.com/.well-known/jwks",
219
+ {
220
+ audience: "test-client",
221
+ issuer: "https://example.com",
222
+ },
223
+ );
224
+
225
+ expect(result).toBeDefined();
226
+ expect(result.payload.aud).toBe("test-client");
227
+ expect(result.payload.iss).toBe("https://example.com");
228
+ });
229
+ });
@@ -112,41 +112,41 @@ export const apple = (options: AppleOptions) => {
112
112
  if (options.verifyIdToken) {
113
113
  return options.verifyIdToken(token, nonce);
114
114
  }
115
- const decodedHeader = decodeProtectedHeader(token);
116
- const { kid, alg: jwtAlg } = decodedHeader;
117
- if (!kid || !jwtAlg) return false;
118
- const publicKey = await getApplePublicKey(kid);
119
- const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
120
- algorithms: [jwtAlg],
121
- issuer: "https://appleid.apple.com",
122
- audience:
123
- options.audience && options.audience.length
124
- ? options.audience
125
- : options.appBundleIdentifier
126
- ? options.appBundleIdentifier
127
- : options.clientId,
128
- maxTokenAge: "1h",
129
- });
130
- ["email_verified", "is_private_email"].forEach((field) => {
131
- if (jwtClaims[field] !== undefined) {
132
- jwtClaims[field] = Boolean(jwtClaims[field]);
115
+ try {
116
+ const decodedHeader = decodeProtectedHeader(token);
117
+ const { kid, alg: jwtAlg } = decodedHeader;
118
+ if (!kid || !jwtAlg) return false;
119
+ const publicKey = await getApplePublicKey(kid);
120
+ const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
121
+ algorithms: [jwtAlg],
122
+ issuer: "https://appleid.apple.com",
123
+ audience:
124
+ options.audience && options.audience.length
125
+ ? options.audience
126
+ : options.appBundleIdentifier
127
+ ? options.appBundleIdentifier
128
+ : options.clientId,
129
+ maxTokenAge: "1h",
130
+ });
131
+ ["email_verified", "is_private_email"].forEach((field) => {
132
+ if (jwtClaims[field] !== undefined) {
133
+ jwtClaims[field] = Boolean(jwtClaims[field]);
134
+ }
135
+ });
136
+ if (nonce && jwtClaims.nonce !== nonce) {
137
+ return false;
133
138
  }
134
- });
135
- if (nonce && jwtClaims.nonce !== nonce) {
139
+ return !!jwtClaims;
140
+ } catch {
136
141
  return false;
137
142
  }
138
- return !!jwtClaims;
139
143
  },
140
144
  refreshAccessToken: options.refreshAccessToken
141
145
  ? options.refreshAccessToken
142
146
  : async (refreshToken) => {
143
147
  return refreshAccessToken({
144
148
  refreshToken,
145
- options: {
146
- clientId: options.clientId,
147
- clientKey: options.clientKey,
148
- clientSecret: options.clientSecret,
149
- },
149
+ options,
150
150
  tokenEndpoint: "https://appleid.apple.com/auth/token",
151
151
  });
152
152
  },
@@ -161,9 +161,18 @@ export const apple = (options: AppleOptions) => {
161
161
  if (!profile) {
162
162
  return null;
163
163
  }
164
- const name = token.user
165
- ? `${token.user.name?.firstName} ${token.user.name?.lastName}`
166
- : profile.name || profile.email;
164
+
165
+ // TODO: "" masking will be removed when the name field is made optional
166
+ let name: string;
167
+ if (token.user?.name) {
168
+ const firstName = token.user.name.firstName || "";
169
+ const lastName = token.user.name.lastName || "";
170
+ const fullName = `${firstName} ${lastName}`.trim();
171
+ name = fullName;
172
+ } else {
173
+ name = profile.name || "";
174
+ }
175
+
167
176
  const emailVerified =
168
177
  typeof profile.email_verified === "boolean"
169
178
  ? profile.email_verified
@@ -178,10 +178,7 @@ export const cognito = (options: CognitoOptions) => {
178
178
  return null;
179
179
  }
180
180
  const name =
181
- profile.name ||
182
- profile.given_name ||
183
- profile.username ||
184
- profile.email;
181
+ profile.name || profile.given_name || profile.username || "";
185
182
  const enrichedProfile = {
186
183
  ...profile,
187
184
  name,
@@ -220,7 +217,11 @@ export const cognito = (options: CognitoOptions) => {
220
217
  return {
221
218
  user: {
222
219
  id: userInfo.sub,
223
- name: userInfo.name || userInfo.given_name || userInfo.username,
220
+ name:
221
+ userInfo.name ||
222
+ userInfo.given_name ||
223
+ userInfo.username ||
224
+ "",
224
225
  email: userInfo.email,
225
226
  image: userInfo.picture,
226
227
  emailVerified: userInfo.email_verified,
@@ -74,7 +74,7 @@ export const dropbox = (options: DropboxOptions) => {
74
74
  clientKey: options.clientKey,
75
75
  clientSecret: options.clientSecret,
76
76
  },
77
- tokenEndpoint: "https://api.dropbox.com/oauth2/token",
77
+ tokenEndpoint,
78
78
  });
79
79
  },
80
80
  async getUserInfo(token) {
@@ -49,7 +49,7 @@ export const facebook = (options: FacebookOptions) => {
49
49
  return await createAuthorizationURL({
50
50
  id: "facebook",
51
51
  options,
52
- authorizationEndpoint: "https://www.facebook.com/v21.0/dialog/oauth",
52
+ authorizationEndpoint: "https://www.facebook.com/v24.0/dialog/oauth",
53
53
  scopes: _scopes,
54
54
  state,
55
55
  redirectURI,
@@ -66,7 +66,7 @@ export const facebook = (options: FacebookOptions) => {
66
66
  code,
67
67
  redirectURI,
68
68
  options,
69
- tokenEndpoint: "https://graph.facebook.com/oauth/access_token",
69
+ tokenEndpoint: "https://graph.facebook.com/v24.0/oauth/access_token",
70
70
  });
71
71
  },
72
72
  async verifyIdToken(token, nonce) {
@@ -121,7 +121,7 @@ export const facebook = (options: FacebookOptions) => {
121
121
  clientSecret: options.clientSecret,
122
122
  },
123
123
  tokenEndpoint:
124
- "https://graph.facebook.com/v18.0/oauth/access_token",
124
+ "https://graph.facebook.com/v24.0/oauth/access_token",
125
125
  });
126
126
  },
127
127
  async getUserInfo(token) {
@@ -34,7 +34,7 @@ export const figma = (options: FigmaOptions) => {
34
34
  throw new BetterAuthError("codeVerifier is required for Figma");
35
35
  }
36
36
 
37
- const _scopes = options.disableDefaultScope ? [] : ["file_read"];
37
+ const _scopes = options.disableDefaultScope ? [] : ["current_user:read"];
38
38
  if (options.scope) _scopes.push(...options.scope);
39
39
  if (scopes) _scopes.push(...scopes);
40
40
 
@@ -56,7 +56,8 @@ export const figma = (options: FigmaOptions) => {
56
56
  codeVerifier,
57
57
  redirectURI,
58
58
  options,
59
- tokenEndpoint: "https://www.figma.com/api/oauth/token",
59
+ tokenEndpoint: "https://api.figma.com/v1/oauth/token",
60
+ authentication: "basic",
60
61
  });
61
62
  },
62
63
  refreshAccessToken: options.refreshAccessToken
@@ -69,7 +70,8 @@ export const figma = (options: FigmaOptions) => {
69
70
  clientKey: options.clientKey,
70
71
  clientSecret: options.clientSecret,
71
72
  },
72
- tokenEndpoint: "https://www.figma.com/api/oauth/token",
73
+ tokenEndpoint: "https://api.figma.com/v1/oauth/token",
74
+ authentication: "basic",
73
75
  });
74
76
  },
75
77
  async getUserInfo(token) {
@@ -1,10 +1,12 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
+ import { logger } from "../env";
2
3
  import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
4
  import {
4
5
  createAuthorizationURL,
6
+ getOAuth2Tokens,
5
7
  refreshAccessToken,
6
- validateAuthorizationCode,
7
8
  } from "../oauth2";
9
+ import { createAuthorizationCodeRequest } from "../oauth2/validate-authorization-code";
8
10
 
9
11
  export interface GithubProfile {
10
12
  login: string;
@@ -86,13 +88,33 @@ export const github = (options: GithubOptions) => {
86
88
  });
87
89
  },
88
90
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
89
- return validateAuthorizationCode({
91
+ const { body, headers: requestHeaders } = createAuthorizationCodeRequest({
90
92
  code,
91
93
  codeVerifier,
92
94
  redirectURI,
93
95
  options,
94
- tokenEndpoint,
95
96
  });
97
+
98
+ const { data, error } = await betterFetch<
99
+ | { access_token: string; token_type: string; scope: string }
100
+ | { error: string; error_description?: string; error_uri?: string }
101
+ >(tokenEndpoint, {
102
+ method: "POST",
103
+ body: body,
104
+ headers: requestHeaders,
105
+ });
106
+
107
+ if (error) {
108
+ logger.error("GitHub OAuth token exchange failed:", error);
109
+ return null;
110
+ }
111
+
112
+ if ("error" in data) {
113
+ logger.error("GitHub OAuth token exchange failed:", data);
114
+ return null;
115
+ }
116
+
117
+ return getOAuth2Tokens(data);
96
118
  },
97
119
  refreshAccessToken: options.refreshAccessToken
98
120
  ? options.refreshAccessToken
@@ -148,7 +170,7 @@ export const github = (options: GithubOptions) => {
148
170
  return {
149
171
  user: {
150
172
  id: profile.id,
151
- name: profile.name || profile.login,
173
+ name: profile.name || profile.login || "",
152
174
  email: profile.email,
153
175
  image: profile.avatar_url,
154
176
  emailVerified,
@@ -65,7 +65,7 @@ const cleanDoubleSlashes = (input: string = "") => {
65
65
  };
66
66
 
67
67
  const issuerToEndpoints = (issuer?: string | undefined) => {
68
- let baseUrl = issuer || "https://gitlab.com";
68
+ const baseUrl = issuer || "https://gitlab.com";
69
69
  return {
70
70
  authorizationEndpoint: cleanDoubleSlashes(`${baseUrl}/oauth/authorize`),
71
71
  tokenEndpoint: cleanDoubleSlashes(`${baseUrl}/oauth/token`),
@@ -141,7 +141,7 @@ export const gitlab = (options: GitlabOptions) => {
141
141
  return {
142
142
  user: {
143
143
  id: profile.id,
144
- name: profile.name ?? profile.username,
144
+ name: profile.name ?? profile.username ?? "",
145
145
  email: profile.email,
146
146
  image: profile.avatar_url,
147
147
  emailVerified: profile.email_verified ?? false,