@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,249 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+
3
+ import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from "jose";
4
+ import { logger } from "../env";
5
+ import { APIError, BetterAuthError } from "../error";
6
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
7
+ import {
8
+ createAuthorizationURL,
9
+ getPrimaryClientId,
10
+ refreshAccessToken,
11
+ validateAuthorizationCode,
12
+ } from "../oauth2";
13
+ export interface AppleProfile {
14
+ /**
15
+ * The subject registered claim identifies the principal that’s the subject
16
+ * of the identity token. Because this token is for your app, the value is
17
+ * the unique identifier for the user.
18
+ */
19
+ sub: string;
20
+ /**
21
+ * A String value representing the user's email address.
22
+ * The email address is either the user's real email address or the proxy
23
+ * address, depending on their status private email relay service.
24
+ */
25
+ email?: string;
26
+ /**
27
+ * A string or Boolean value that indicates whether the service verifies
28
+ * the email. The value can either be a string ("true" or "false") or a
29
+ * Boolean (true or false). The system may not verify email addresses for
30
+ * Sign in with Apple at Work & School users, and this claim is "false" or
31
+ * false for those users.
32
+ */
33
+ email_verified: true | "true";
34
+ /**
35
+ * A string or Boolean value that indicates whether the email that the user
36
+ * shares is the proxy address. The value can either be a string ("true" or
37
+ * "false") or a Boolean (true or false).
38
+ */
39
+ is_private_email: boolean;
40
+ /**
41
+ * An Integer value that indicates whether the user appears to be a real
42
+ * person. Use the value of this claim to mitigate fraud. The possible
43
+ * values are: 0 (or Unsupported), 1 (or Unknown), 2 (or LikelyReal). For
44
+ * more information, see ASUserDetectionStatus. This claim is present only
45
+ * in iOS 14 and later, macOS 11 and later, watchOS 7 and later, tvOS 14
46
+ * and later. The claim isn’t present or supported for web-based apps.
47
+ */
48
+ real_user_status: number;
49
+ /**
50
+ * The user’s full name in the format provided during the authorization
51
+ * process.
52
+ */
53
+ name: string;
54
+ /**
55
+ * The URL to the user's profile picture.
56
+ */
57
+ picture: string;
58
+ user?: AppleNonConformUser | undefined;
59
+ }
60
+
61
+ /**
62
+ * This is the shape of the `user` query parameter that Apple sends the first
63
+ * time the user consents to the app.
64
+ * @see https://developer.apple.com/documentation/signinwithapplerestapi/request-an-authorization-to-the-sign-in-with-apple-server./
65
+ */
66
+ export interface AppleNonConformUser {
67
+ name: {
68
+ firstName: string;
69
+ lastName: string;
70
+ };
71
+ email: string;
72
+ }
73
+
74
+ export interface AppleOptions extends ProviderOptions<AppleProfile> {
75
+ clientId: string | string[];
76
+ appBundleIdentifier?: string | undefined;
77
+ audience?: (string | string[]) | undefined;
78
+ }
79
+
80
+ async function sha256Hex(value: string) {
81
+ const data = new TextEncoder().encode(value);
82
+ const digest = await crypto.subtle.digest("SHA-256", data);
83
+ return Array.from(new Uint8Array(digest))
84
+ .map((byte) => byte.toString(16).padStart(2, "0"))
85
+ .join("");
86
+ }
87
+
88
+ async function nonceMatches(jwtNonce: unknown, nonce: string) {
89
+ if (typeof jwtNonce !== "string") {
90
+ return false;
91
+ }
92
+ if (jwtNonce === nonce) {
93
+ return true;
94
+ }
95
+ return jwtNonce === (await sha256Hex(nonce));
96
+ }
97
+
98
+ export const apple = (options: AppleOptions) => {
99
+ const tokenEndpoint = "https://appleid.apple.com/auth/token";
100
+ return {
101
+ id: "apple",
102
+ name: "Apple",
103
+ async createAuthorizationURL({ state, scopes, redirectURI }) {
104
+ if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
105
+ logger.error(
106
+ "Client ID and client secret are required for Apple. Make sure to provide them in the options.",
107
+ );
108
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
109
+ }
110
+ const _scope = options.disableDefaultScope ? [] : ["email", "name"];
111
+ if (options.scope) _scope.push(...options.scope);
112
+ if (scopes) _scope.push(...scopes);
113
+ const url = await createAuthorizationURL({
114
+ id: "apple",
115
+ options,
116
+ authorizationEndpoint: "https://appleid.apple.com/auth/authorize",
117
+ scopes: _scope,
118
+ state,
119
+ redirectURI,
120
+ responseMode: "form_post",
121
+ responseType: "code id_token",
122
+ });
123
+ return url;
124
+ },
125
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
126
+ return validateAuthorizationCode({
127
+ code,
128
+ codeVerifier,
129
+ redirectURI,
130
+ options,
131
+ tokenEndpoint,
132
+ });
133
+ },
134
+ async verifyIdToken(token, nonce) {
135
+ if (options.disableIdTokenSignIn) {
136
+ return false;
137
+ }
138
+ if (options.verifyIdToken) {
139
+ return options.verifyIdToken(token, nonce);
140
+ }
141
+ try {
142
+ const decodedHeader = decodeProtectedHeader(token);
143
+ const { kid, alg: jwtAlg } = decodedHeader;
144
+ if (!kid || !jwtAlg) return false;
145
+ const publicKey = await getApplePublicKey(kid);
146
+ const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
147
+ algorithms: [jwtAlg],
148
+ issuer: "https://appleid.apple.com",
149
+ audience:
150
+ options.audience && options.audience.length
151
+ ? options.audience
152
+ : options.appBundleIdentifier
153
+ ? options.appBundleIdentifier
154
+ : options.clientId,
155
+ maxTokenAge: "1h",
156
+ });
157
+ ["email_verified", "is_private_email"].forEach((field) => {
158
+ if (jwtClaims[field] !== undefined) {
159
+ jwtClaims[field] = Boolean(jwtClaims[field]);
160
+ }
161
+ });
162
+ if (nonce && !(await nonceMatches(jwtClaims.nonce, nonce))) {
163
+ return false;
164
+ }
165
+ return !!jwtClaims;
166
+ } catch {
167
+ return false;
168
+ }
169
+ },
170
+ refreshAccessToken: options.refreshAccessToken
171
+ ? options.refreshAccessToken
172
+ : async (refreshToken) => {
173
+ return refreshAccessToken({
174
+ refreshToken,
175
+ options,
176
+ tokenEndpoint,
177
+ });
178
+ },
179
+ async getUserInfo(token) {
180
+ if (options.getUserInfo) {
181
+ return options.getUserInfo(token);
182
+ }
183
+ if (!token.idToken) {
184
+ return null;
185
+ }
186
+ const profile = decodeJwt<AppleProfile>(token.idToken);
187
+ if (!profile) {
188
+ return null;
189
+ }
190
+
191
+ // TODO: "" masking will be removed when the name field is made optional
192
+ let name: string;
193
+ if (token.user?.name) {
194
+ const firstName = token.user.name.firstName || "";
195
+ const lastName = token.user.name.lastName || "";
196
+ const fullName = `${firstName} ${lastName}`.trim();
197
+ name = fullName;
198
+ } else {
199
+ name = profile.name || "";
200
+ }
201
+
202
+ const emailVerified =
203
+ typeof profile.email_verified === "boolean"
204
+ ? profile.email_verified
205
+ : profile.email_verified === "true";
206
+ const enrichedProfile = {
207
+ ...profile,
208
+ name,
209
+ };
210
+ const userMap = await options.mapProfileToUser?.(enrichedProfile);
211
+ return {
212
+ user: {
213
+ id: profile.sub,
214
+ name: enrichedProfile.name,
215
+ emailVerified: emailVerified,
216
+ email: profile.email,
217
+ ...userMap,
218
+ },
219
+ data: enrichedProfile,
220
+ };
221
+ },
222
+ options,
223
+ } satisfies OAuthProvider<AppleProfile>;
224
+ };
225
+
226
+ export const getApplePublicKey = async (kid: string) => {
227
+ const APPLE_BASE_URL = "https://appleid.apple.com";
228
+ const JWKS_APPLE_URI = "/auth/keys";
229
+ const { data } = await betterFetch<{
230
+ keys: Array<{
231
+ kid: string;
232
+ alg: string;
233
+ kty: string;
234
+ use: string;
235
+ n: string;
236
+ e: string;
237
+ }>;
238
+ }>(`${APPLE_BASE_URL}${JWKS_APPLE_URI}`);
239
+ if (!data?.keys) {
240
+ throw new APIError("BAD_REQUEST", {
241
+ message: "Keys not found",
242
+ });
243
+ }
244
+ const jwk = data.keys.find((key) => key.kid === kid);
245
+ if (!jwk) {
246
+ throw new Error(`JWK with kid ${kid} not found`);
247
+ }
248
+ return await importJWK(jwk, jwk.alg);
249
+ };
@@ -0,0 +1,133 @@
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 AtlassianProfile {
12
+ account_type?: string | undefined;
13
+ account_id: string;
14
+ email?: string | undefined;
15
+ name: string;
16
+ picture?: string | undefined;
17
+ nickname?: string | undefined;
18
+ locale?: string | undefined;
19
+ extended_profile?:
20
+ | {
21
+ job_title?: string;
22
+ organization?: string;
23
+ department?: string;
24
+ location?: string;
25
+ }
26
+ | undefined;
27
+ }
28
+ export interface AtlassianOptions extends ProviderOptions<AtlassianProfile> {
29
+ clientId: string;
30
+ }
31
+
32
+ export const atlassian = (options: AtlassianOptions) => {
33
+ const tokenEndpoint = "https://auth.atlassian.com/oauth/token";
34
+ return {
35
+ id: "atlassian",
36
+ name: "Atlassian",
37
+
38
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
39
+ if (!options.clientId || !options.clientSecret) {
40
+ logger.error("Client Id and Secret are required for Atlassian");
41
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
42
+ }
43
+ if (!codeVerifier) {
44
+ throw new BetterAuthError("codeVerifier is required for Atlassian");
45
+ }
46
+
47
+ const _scopes = options.disableDefaultScope
48
+ ? []
49
+ : ["read:jira-user", "offline_access"];
50
+ if (options.scope) _scopes.push(...options.scope);
51
+ if (scopes) _scopes.push(...scopes);
52
+
53
+ return createAuthorizationURL({
54
+ id: "atlassian",
55
+ options,
56
+ authorizationEndpoint: "https://auth.atlassian.com/authorize",
57
+ scopes: _scopes,
58
+ state,
59
+ codeVerifier,
60
+ redirectURI,
61
+ additionalParams: {
62
+ audience: "api.atlassian.com",
63
+ },
64
+ prompt: options.prompt,
65
+ });
66
+ },
67
+
68
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
69
+ return validateAuthorizationCode({
70
+ code,
71
+ codeVerifier,
72
+ redirectURI,
73
+ options,
74
+ tokenEndpoint,
75
+ });
76
+ },
77
+
78
+ refreshAccessToken: options.refreshAccessToken
79
+ ? options.refreshAccessToken
80
+ : async (refreshToken) => {
81
+ return refreshAccessToken({
82
+ refreshToken,
83
+ options: {
84
+ clientId: options.clientId,
85
+ clientSecret: options.clientSecret,
86
+ },
87
+ tokenEndpoint,
88
+ });
89
+ },
90
+
91
+ async getUserInfo(token) {
92
+ if (options.getUserInfo) {
93
+ return options.getUserInfo(token);
94
+ }
95
+
96
+ if (!token.accessToken) {
97
+ return null;
98
+ }
99
+
100
+ try {
101
+ const { data: profile } = await betterFetch<{
102
+ account_id: string;
103
+ name: string;
104
+ email?: string | undefined;
105
+ picture?: string | undefined;
106
+ }>("https://api.atlassian.com/me", {
107
+ headers: { Authorization: `Bearer ${token.accessToken}` },
108
+ });
109
+
110
+ if (!profile) return null;
111
+
112
+ const userMap = await options.mapProfileToUser?.(profile);
113
+
114
+ return {
115
+ user: {
116
+ id: profile.account_id,
117
+ name: profile.name,
118
+ email: profile.email,
119
+ image: profile.picture,
120
+ emailVerified: false,
121
+ ...userMap,
122
+ },
123
+ data: profile,
124
+ };
125
+ } catch (error) {
126
+ logger.error("Failed to fetch user info from Figma:", error);
127
+ return null;
128
+ }
129
+ },
130
+
131
+ options,
132
+ } satisfies OAuthProvider<AtlassianProfile>;
133
+ };
@@ -0,0 +1,281 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+ import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from "jose";
3
+ import { logger } from "../env";
4
+ import { APIError, BetterAuthError } from "../error";
5
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
6
+ import {
7
+ createAuthorizationURL,
8
+ getPrimaryClientId,
9
+ refreshAccessToken,
10
+ validateAuthorizationCode,
11
+ } from "../oauth2";
12
+
13
+ export interface CognitoProfile {
14
+ sub: string;
15
+ email: string;
16
+ email_verified: boolean;
17
+ name: string;
18
+ given_name?: string | undefined;
19
+ family_name?: string | undefined;
20
+ picture?: string | undefined;
21
+ username?: string | undefined;
22
+ locale?: string | undefined;
23
+ phone_number?: string | undefined;
24
+ phone_number_verified?: boolean | undefined;
25
+ aud: string;
26
+ iss: string;
27
+ exp: number;
28
+ iat: number;
29
+ // Custom attributes from Cognito can be added here
30
+ [key: string]: any;
31
+ }
32
+
33
+ export interface CognitoOptions extends ProviderOptions<CognitoProfile> {
34
+ clientId: string | string[];
35
+ /**
36
+ * The Cognito domain (e.g., "your-app.auth.us-east-1.amazoncognito.com")
37
+ */
38
+ domain: string;
39
+ /**
40
+ * AWS region where User Pool is hosted (e.g., "us-east-1")
41
+ */
42
+ region: string;
43
+ userPoolId: string;
44
+ requireClientSecret?: boolean | undefined;
45
+ }
46
+
47
+ export const cognito = (options: CognitoOptions) => {
48
+ if (!options.domain || !options.region || !options.userPoolId) {
49
+ logger.error(
50
+ "Domain, region and userPoolId are required for Amazon Cognito. Make sure to provide them in the options.",
51
+ );
52
+ throw new BetterAuthError("DOMAIN_AND_REGION_REQUIRED");
53
+ }
54
+
55
+ const cleanDomain = options.domain.replace(/^https?:\/\//, "");
56
+ const authorizationEndpoint = `https://${cleanDomain}/oauth2/authorize`;
57
+ const tokenEndpoint = `https://${cleanDomain}/oauth2/token`;
58
+ const userInfoEndpoint = `https://${cleanDomain}/oauth2/userinfo`;
59
+
60
+ return {
61
+ id: "cognito",
62
+ name: "Cognito",
63
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
64
+ if (!getPrimaryClientId(options.clientId)) {
65
+ logger.error(
66
+ "ClientId is required for Amazon Cognito. Make sure to provide them in the options.",
67
+ );
68
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
69
+ }
70
+
71
+ if (options.requireClientSecret && !options.clientSecret) {
72
+ logger.error(
73
+ "Client Secret is required when requireClientSecret is true. Make sure to provide it in the options.",
74
+ );
75
+ throw new BetterAuthError("CLIENT_SECRET_REQUIRED");
76
+ }
77
+ const _scopes = options.disableDefaultScope
78
+ ? []
79
+ : ["openid", "profile", "email"];
80
+ if (options.scope) _scopes.push(...options.scope);
81
+ if (scopes) _scopes.push(...scopes);
82
+
83
+ const url = await createAuthorizationURL({
84
+ id: "cognito",
85
+ options: {
86
+ ...options,
87
+ },
88
+ authorizationEndpoint,
89
+ scopes: _scopes,
90
+ state,
91
+ codeVerifier,
92
+ redirectURI,
93
+ prompt: options.prompt,
94
+ });
95
+ // AWS Cognito requires scopes to be encoded with %20 instead of +
96
+ // URLSearchParams encodes spaces as + by default, so we need to fix this
97
+ const scopeValue = url.searchParams.get("scope");
98
+ if (scopeValue) {
99
+ url.searchParams.delete("scope");
100
+ const encodedScope = encodeURIComponent(scopeValue);
101
+ // Manually append the scope with proper encoding to the URL
102
+ const urlString = url.toString();
103
+ const separator = urlString.includes("?") ? "&" : "?";
104
+ return new URL(`${urlString}${separator}scope=${encodedScope}`);
105
+ }
106
+ return url;
107
+ },
108
+
109
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
110
+ return validateAuthorizationCode({
111
+ code,
112
+ codeVerifier,
113
+ redirectURI,
114
+ options,
115
+ tokenEndpoint,
116
+ });
117
+ },
118
+
119
+ refreshAccessToken: options.refreshAccessToken
120
+ ? options.refreshAccessToken
121
+ : async (refreshToken) => {
122
+ return refreshAccessToken({
123
+ refreshToken,
124
+ options: {
125
+ clientId: options.clientId,
126
+ clientKey: options.clientKey,
127
+ clientSecret: options.clientSecret,
128
+ },
129
+ tokenEndpoint,
130
+ });
131
+ },
132
+
133
+ async verifyIdToken(token, nonce) {
134
+ if (options.disableIdTokenSignIn) {
135
+ return false;
136
+ }
137
+ if (options.verifyIdToken) {
138
+ return options.verifyIdToken(token, nonce);
139
+ }
140
+
141
+ try {
142
+ const decodedHeader = decodeProtectedHeader(token);
143
+ const { kid, alg: jwtAlg } = decodedHeader;
144
+ if (!kid || !jwtAlg) return false;
145
+
146
+ const publicKey = await getCognitoPublicKey(
147
+ kid,
148
+ options.region,
149
+ options.userPoolId,
150
+ );
151
+ const expectedIssuer = `https://cognito-idp.${options.region}.amazonaws.com/${options.userPoolId}`;
152
+
153
+ const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
154
+ algorithms: [jwtAlg],
155
+ issuer: expectedIssuer,
156
+ audience: options.clientId,
157
+ maxTokenAge: "1h",
158
+ });
159
+
160
+ if (nonce && jwtClaims.nonce !== nonce) {
161
+ return false;
162
+ }
163
+ return true;
164
+ } catch (error) {
165
+ logger.error("Failed to verify ID token:", error);
166
+ return false;
167
+ }
168
+ },
169
+
170
+ async getUserInfo(token) {
171
+ if (options.getUserInfo) {
172
+ return options.getUserInfo(token);
173
+ }
174
+
175
+ if (token.idToken) {
176
+ try {
177
+ const profile = decodeJwt<CognitoProfile>(token.idToken);
178
+ if (!profile) {
179
+ return null;
180
+ }
181
+ const name =
182
+ profile.name || profile.given_name || profile.username || "";
183
+ const enrichedProfile = {
184
+ ...profile,
185
+ name,
186
+ };
187
+ const userMap = await options.mapProfileToUser?.(enrichedProfile);
188
+
189
+ return {
190
+ user: {
191
+ id: profile.sub,
192
+ name: enrichedProfile.name,
193
+ email: profile.email,
194
+ image: profile.picture,
195
+ emailVerified: profile.email_verified,
196
+ ...userMap,
197
+ },
198
+ data: enrichedProfile,
199
+ };
200
+ } catch (error) {
201
+ logger.error("Failed to decode ID token:", error);
202
+ }
203
+ }
204
+
205
+ if (token.accessToken) {
206
+ try {
207
+ const { data: userInfo } = await betterFetch<CognitoProfile>(
208
+ userInfoEndpoint,
209
+ {
210
+ headers: {
211
+ Authorization: `Bearer ${token.accessToken}`,
212
+ },
213
+ },
214
+ );
215
+
216
+ if (userInfo) {
217
+ const userMap = await options.mapProfileToUser?.(userInfo);
218
+ return {
219
+ user: {
220
+ id: userInfo.sub,
221
+ name:
222
+ userInfo.name ||
223
+ userInfo.given_name ||
224
+ userInfo.username ||
225
+ "",
226
+ email: userInfo.email,
227
+ image: userInfo.picture,
228
+ emailVerified: userInfo.email_verified,
229
+ ...userMap,
230
+ },
231
+ data: userInfo,
232
+ };
233
+ }
234
+ } catch (error) {
235
+ logger.error("Failed to fetch user info from Cognito:", error);
236
+ }
237
+ }
238
+
239
+ return null;
240
+ },
241
+
242
+ options,
243
+ } satisfies OAuthProvider<CognitoProfile>;
244
+ };
245
+
246
+ export const getCognitoPublicKey = async (
247
+ kid: string,
248
+ region: string,
249
+ userPoolId: string,
250
+ ) => {
251
+ const COGNITO_JWKS_URI = `https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json`;
252
+
253
+ try {
254
+ const { data } = await betterFetch<{
255
+ keys: Array<{
256
+ kid: string;
257
+ alg: string;
258
+ kty: string;
259
+ use: string;
260
+ n: string;
261
+ e: string;
262
+ }>;
263
+ }>(COGNITO_JWKS_URI);
264
+
265
+ if (!data?.keys) {
266
+ throw new APIError("BAD_REQUEST", {
267
+ message: "Keys not found",
268
+ });
269
+ }
270
+
271
+ const jwk = data.keys.find((key) => key.kid === kid);
272
+ if (!jwk) {
273
+ throw new Error(`JWK with kid ${kid} not found`);
274
+ }
275
+
276
+ return await importJWK(jwk, jwk.alg);
277
+ } catch (error) {
278
+ logger.error("Failed to fetch Cognito public key:", error);
279
+ throw error;
280
+ }
281
+ };