@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,125 @@
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 VkProfile {
10
+ user: {
11
+ user_id: string;
12
+ first_name: string;
13
+ last_name: string;
14
+ email?: string | undefined;
15
+ phone?: number | undefined;
16
+ avatar?: string | undefined;
17
+ sex?: number | undefined;
18
+ verified?: boolean | undefined;
19
+ birthday: string;
20
+ };
21
+ }
22
+
23
+ export interface VkOption extends ProviderOptions {
24
+ clientId: string;
25
+ scheme?: ("light" | "dark") | undefined;
26
+ }
27
+
28
+ export const vk = (options: VkOption) => {
29
+ const tokenEndpoint = "https://id.vk.com/oauth2/auth";
30
+ return {
31
+ id: "vk",
32
+ name: "VK",
33
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
34
+ const _scopes = options.disableDefaultScope ? [] : ["email", "phone"];
35
+ if (options.scope) _scopes.push(...options.scope);
36
+ if (scopes) _scopes.push(...scopes);
37
+ const authorizationEndpoint = "https://id.vk.com/authorize";
38
+
39
+ return createAuthorizationURL({
40
+ id: "vk",
41
+ options,
42
+ authorizationEndpoint,
43
+ scopes: _scopes,
44
+ state,
45
+ redirectURI,
46
+ codeVerifier,
47
+ });
48
+ },
49
+ validateAuthorizationCode: async ({
50
+ code,
51
+ codeVerifier,
52
+ redirectURI,
53
+ deviceId,
54
+ }) => {
55
+ return validateAuthorizationCode({
56
+ code,
57
+ codeVerifier,
58
+ redirectURI: options.redirectURI || redirectURI,
59
+ options,
60
+ deviceId,
61
+ tokenEndpoint,
62
+ });
63
+ },
64
+ refreshAccessToken: options.refreshAccessToken
65
+ ? options.refreshAccessToken
66
+ : async (refreshToken) => {
67
+ return refreshAccessToken({
68
+ refreshToken,
69
+ options: {
70
+ clientId: options.clientId,
71
+ clientKey: options.clientKey,
72
+ clientSecret: options.clientSecret,
73
+ },
74
+ tokenEndpoint,
75
+ });
76
+ },
77
+ async getUserInfo(data) {
78
+ if (options.getUserInfo) {
79
+ return options.getUserInfo(data);
80
+ }
81
+ if (!data.accessToken) {
82
+ return null;
83
+ }
84
+ const formBody = new URLSearchParams({
85
+ access_token: data.accessToken,
86
+ client_id: options.clientId,
87
+ }).toString();
88
+ const { data: profile, error } = await betterFetch<VkProfile>(
89
+ "https://id.vk.com/oauth2/user_info",
90
+ {
91
+ method: "POST",
92
+ headers: {
93
+ "Content-Type": "application/x-www-form-urlencoded",
94
+ },
95
+ body: formBody,
96
+ },
97
+ );
98
+ if (error) {
99
+ return null;
100
+ }
101
+
102
+ const userMap = await options.mapProfileToUser?.(profile);
103
+ if (!profile.user.email && !userMap?.email) {
104
+ return null;
105
+ }
106
+
107
+ return {
108
+ user: {
109
+ id: profile.user.user_id,
110
+ first_name: profile.user.first_name,
111
+ last_name: profile.user.last_name,
112
+ email: profile.user.email,
113
+ image: profile.user.avatar,
114
+ emailVerified: false,
115
+ birthday: profile.user.birthday,
116
+ sex: profile.user.sex,
117
+ name: `${profile.user.first_name} ${profile.user.last_name}`,
118
+ ...userMap,
119
+ },
120
+ data: profile,
121
+ };
122
+ },
123
+ options,
124
+ } satisfies OAuthProvider<VkProfile>;
125
+ };
@@ -0,0 +1,220 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+ import type { OAuth2Tokens, OAuthProvider, ProviderOptions } from "../oauth2";
3
+
4
+ /**
5
+ * WeChat user profile information
6
+ * @see https://developers.weixin.qq.com/doc/oplatform/en/Website_App/WeChat_Login/Wechat_Login.html
7
+ */
8
+ export interface WeChatProfile extends Record<string, any> {
9
+ /**
10
+ * User's unique OpenID
11
+ */
12
+ openid: string;
13
+ /**
14
+ * User's nickname
15
+ */
16
+ nickname: string;
17
+ /**
18
+ * User's avatar image URL
19
+ */
20
+ headimgurl: string;
21
+ /**
22
+ * User's privileges
23
+ */
24
+ privilege: string[];
25
+ /**
26
+ * User's UnionID (unique across the developer's various applications)
27
+ */
28
+ unionid?: string;
29
+ /** @note Email is currently unsupported by WeChat */
30
+ email?: string;
31
+ }
32
+
33
+ export interface WeChatOptions extends ProviderOptions<WeChatProfile> {
34
+ /**
35
+ * WeChat App ID
36
+ */
37
+ clientId: string;
38
+ /**
39
+ * WeChat App Secret
40
+ */
41
+ clientSecret: string;
42
+ /**
43
+ * Platform type for WeChat login
44
+ * - Currently only supports "WebsiteApp" for WeChat Website Application (网站应用)
45
+ * @default "WebsiteApp"
46
+ */
47
+ platformType?: "WebsiteApp";
48
+
49
+ /**
50
+ * UI language for the WeChat login page
51
+ * cn for Simplified Chinese, en for English
52
+ * @default "cn" if left undefined
53
+ */
54
+ lang?: "cn" | "en";
55
+ }
56
+
57
+ export const wechat = (options: WeChatOptions) => {
58
+ return {
59
+ id: "wechat",
60
+ name: "WeChat",
61
+ createAuthorizationURL({ state, scopes, redirectURI }) {
62
+ const _scopes = options.disableDefaultScope ? [] : ["snsapi_login"];
63
+ options.scope && _scopes.push(...options.scope);
64
+ scopes && _scopes.push(...scopes);
65
+
66
+ // WeChat uses non-standard OAuth2 parameters (appid instead of client_id)
67
+ // and requires a fragment (#wechat_redirect), so we construct the URL manually.
68
+ const url = new URL("https://open.weixin.qq.com/connect/qrconnect");
69
+ url.searchParams.set("scope", _scopes.join(","));
70
+ url.searchParams.set("response_type", "code");
71
+ url.searchParams.set("appid", options.clientId);
72
+ url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
73
+ url.searchParams.set("state", state);
74
+ url.searchParams.set("lang", options.lang || "cn");
75
+ url.hash = "wechat_redirect";
76
+
77
+ return url;
78
+ },
79
+
80
+ // WeChat uses non-standard token exchange (appid/secret instead of
81
+ // client_id/client_secret, GET instead of POST), so shared helpers
82
+ // like validateAuthorizationCode/getOAuth2Tokens cannot be used directly.
83
+ validateAuthorizationCode: async ({ code }) => {
84
+ const params = new URLSearchParams({
85
+ appid: options.clientId,
86
+ secret: options.clientSecret,
87
+ code: code,
88
+ grant_type: "authorization_code",
89
+ });
90
+
91
+ const { data: tokenData, error } = await betterFetch<{
92
+ access_token: string;
93
+ expires_in: number;
94
+ refresh_token: string;
95
+ openid: string;
96
+ scope: string;
97
+ unionid?: string;
98
+ errcode?: number;
99
+ errmsg?: string;
100
+ }>(
101
+ "https://api.weixin.qq.com/sns/oauth2/access_token?" +
102
+ params.toString(),
103
+ {
104
+ method: "GET",
105
+ },
106
+ );
107
+
108
+ if (error || !tokenData || tokenData.errcode) {
109
+ throw new Error(
110
+ `Failed to validate authorization code: ${tokenData?.errmsg || error?.message || "Unknown error"}`,
111
+ );
112
+ }
113
+
114
+ return {
115
+ tokenType: "Bearer" as const,
116
+ accessToken: tokenData.access_token,
117
+ refreshToken: tokenData.refresh_token,
118
+ accessTokenExpiresAt: new Date(
119
+ Date.now() + tokenData.expires_in * 1000,
120
+ ),
121
+ scopes: tokenData.scope.split(","),
122
+ // WeChat requires openid for the userinfo endpoint, which is
123
+ // returned alongside the access token.
124
+ openid: tokenData.openid,
125
+ unionid: tokenData.unionid,
126
+ };
127
+ },
128
+
129
+ refreshAccessToken: options.refreshAccessToken
130
+ ? options.refreshAccessToken
131
+ : async (refreshToken) => {
132
+ const params = new URLSearchParams({
133
+ appid: options.clientId,
134
+ grant_type: "refresh_token",
135
+ refresh_token: refreshToken,
136
+ });
137
+
138
+ const { data: tokenData, error } = await betterFetch<{
139
+ access_token: string;
140
+ expires_in: number;
141
+ refresh_token: string;
142
+ openid: string;
143
+ scope: string;
144
+ errcode?: number;
145
+ errmsg?: string;
146
+ }>(
147
+ "https://api.weixin.qq.com/sns/oauth2/refresh_token?" +
148
+ params.toString(),
149
+ {
150
+ method: "GET",
151
+ },
152
+ );
153
+
154
+ if (error || !tokenData || tokenData.errcode) {
155
+ throw new Error(
156
+ `Failed to refresh access token: ${tokenData?.errmsg || error?.message || "Unknown error"}`,
157
+ );
158
+ }
159
+
160
+ return {
161
+ tokenType: "Bearer" as const,
162
+ accessToken: tokenData.access_token,
163
+ refreshToken: tokenData.refresh_token,
164
+ accessTokenExpiresAt: new Date(
165
+ Date.now() + tokenData.expires_in * 1000,
166
+ ),
167
+ scopes: tokenData.scope.split(","),
168
+ };
169
+ },
170
+
171
+ async getUserInfo(token) {
172
+ if (options.getUserInfo) {
173
+ return options.getUserInfo(token);
174
+ }
175
+
176
+ const openid = (token as OAuth2Tokens & { openid?: string }).openid;
177
+
178
+ if (!openid) {
179
+ return null;
180
+ }
181
+
182
+ const params = new URLSearchParams({
183
+ access_token: token.accessToken || "",
184
+ openid: openid,
185
+ lang: "zh_CN",
186
+ });
187
+
188
+ const { data: profile, error } = await betterFetch<
189
+ WeChatProfile & { errcode?: number; errmsg?: string }
190
+ >("https://api.weixin.qq.com/sns/userinfo?" + params.toString(), {
191
+ method: "GET",
192
+ });
193
+
194
+ if (error || !profile || profile.errcode) {
195
+ return null;
196
+ }
197
+
198
+ const userMap = await options.mapProfileToUser?.(profile);
199
+ return {
200
+ user: {
201
+ id: profile.unionid || profile.openid || openid,
202
+ name: profile.nickname,
203
+ // WeChat does not return an email, and the OAuth callback rejects a
204
+ // missing one, so the default sign-in would always fail. Synthesize a
205
+ // stable, non-routable placeholder (RFC 2606 `.invalid`) keyed to the
206
+ // user's WeChat id, left unverified. Applications that collect a real
207
+ // email override it via `mapProfileToUser`.
208
+ email:
209
+ profile.email ||
210
+ `${profile.unionid || profile.openid || openid}@wechat.invalid`,
211
+ image: profile.headimgurl,
212
+ emailVerified: false,
213
+ ...userMap,
214
+ },
215
+ data: profile,
216
+ };
217
+ },
218
+ options,
219
+ } satisfies OAuthProvider<WeChatProfile, WeChatOptions>;
220
+ };
@@ -0,0 +1,230 @@
1
+ import { betterFetch } from "@better-fetch/fetch";
2
+ import type { OAuthProvider, ProviderOptions } from "../oauth2";
3
+ import {
4
+ generateCodeChallenge,
5
+ refreshAccessToken,
6
+ validateAuthorizationCode,
7
+ } from "../oauth2";
8
+
9
+ export type LoginType =
10
+ | 0 /** Facebook OAuth */
11
+ | 1 /** Google OAuth */
12
+ | 24 /** Apple OAuth */
13
+ | 27 /** Microsoft OAuth */
14
+ | 97 /** Mobile device */
15
+ | 98 /** RingCentral OAuth */
16
+ | 99 /** API user */
17
+ | 100 /** Zoom Work email */
18
+ | 101; /** Single Sign-On (SSO) */
19
+
20
+ export type AccountStatus = "pending" | "active" | "inactive";
21
+
22
+ export type PronounOption =
23
+ | 1 /** Ask the user every time */
24
+ | 2 /** Always display */
25
+ | 3; /** Do not display */
26
+
27
+ export interface PhoneNumber {
28
+ /** The country code of the phone number (Example: "+1") */
29
+ code: string;
30
+
31
+ /** The country of the phone number (Example: "US") */
32
+ country: string;
33
+
34
+ /** The label for the phone number (Example: "Mobile") */
35
+ label: string;
36
+
37
+ /** The phone number itself (Example: "800000000") */
38
+ number: string;
39
+
40
+ /** Whether the phone number has been verified (Example: true) */
41
+ verified: boolean;
42
+ }
43
+
44
+ /**
45
+ * See the full documentation below:
46
+ * https://developers.zoom.us/docs/api/users/#tag/users/GET/users/{userId}
47
+ */
48
+ export interface ZoomProfile extends Record<string, any> {
49
+ /* cspell:disable-next-line */
50
+ /** The user's account ID (Example: "q6gBJVO5TzexKYTb_I2rpg") */
51
+ account_id: string;
52
+ /** The user's account number (Example: 10009239) */
53
+ account_number: number;
54
+ /** The user's cluster (Example: "us04") */
55
+ cluster: string;
56
+ /** The user's CMS ID. Only enabled for Kaltura integration (Example: "KDcuGIm1QgePTO8WbOqwIQ") */
57
+ cms_user_id: string;
58
+ /** The user's cost center (Example: "cost center") */
59
+ cost_center: string;
60
+ /** User create time (Example: "2018-10-31T04:32:37Z") */
61
+ created_at: string;
62
+ /** Department (Example: "Developers") */
63
+ dept: string;
64
+ /** User's display name (Example: "Jill Chill") */
65
+ display_name: string;
66
+ /** User's email address (Example: "jchill@example.com") */
67
+ email: string;
68
+ /** User's first name (Example: "Jill") */
69
+ first_name: string;
70
+ /* cspell:disable-next-line */
71
+ /** IDs of the web groups that the user belongs to (Example: ["RSMaSp8sTEGK0_oamiA2_w"]) */
72
+ group_ids: string[];
73
+ /* cspell:disable-next-line */
74
+ /** User ID (Example: "zJKyaiAyTNC-MWjiWC18KQ") */
75
+ id: string;
76
+ /* cspell:disable-next-line */
77
+ /** IM IDs of the groups that the user belongs to (Example: ["t-_-d56CSWG-7BF15LLrOw"]) */
78
+ im_group_ids: string[];
79
+ /** The user's JID (Example: "jchill@example.com") */
80
+ jid: string;
81
+ /** The user's job title (Example: "API Developer") */
82
+ job_title: string;
83
+ /** Default language for the Zoom Web Portal (Example: "en-US") */
84
+ language: string;
85
+ /** User last login client version (Example: "5.9.6.4993(mac)") */
86
+ last_client_version: string;
87
+ /** User last login time (Example: "2021-05-05T20:40:30Z") */
88
+ last_login_time: string;
89
+ /** User's last name (Example: "Chill") */
90
+ last_name: string;
91
+ /** The time zone of the user (Example: "Asia/Shanghai") */
92
+ timezone: string;
93
+ /** User's location (Example: "Paris") */
94
+ location: string;
95
+ /** The user's login method (Example: 101) */
96
+ login_types: LoginType[];
97
+ /** User's personal meeting URL (Example: "example.com") */
98
+ personal_meeting_url: string;
99
+ /** The URL for user's profile picture (Example: "example.com") */
100
+ pic_url: string;
101
+ /** Personal Meeting ID (PMI) (Example: 3542471135) */
102
+ pmi: number;
103
+ /** Unique identifier of the user's assigned role (Example: "0") */
104
+ role_id: string;
105
+ /** User's role name (Example: "Admin") */
106
+ role_name: string;
107
+ /** Status of user's account (Example: "pending") */
108
+ status: AccountStatus;
109
+ /** Use the personal meeting ID (PMI) for instant meetings (Example: false) */
110
+ use_pmi: boolean;
111
+ /** The time and date when the user was created (Example: "2018-10-31T04:32:37Z") */
112
+ user_created_at: string;
113
+ /** Displays whether user is verified or not (Example: 1) */
114
+ verified: number;
115
+ /** The user's Zoom Workplace plan option (Example: 64) */
116
+ zoom_one_type: number;
117
+ /** The user's company (Example: "Jill") */
118
+ company?: string | undefined;
119
+ /* cspell:disable-next-line */
120
+ /** Custom attributes that have been assigned to the user (Example: [{ "key": "cbf_cywdkexrtqc73f97gd4w6g", "name": "A1", "value": "1" }]) */
121
+ custom_attributes?:
122
+ | { key: string; name: string; value: string }[]
123
+ | undefined;
124
+ /* cspell:disable-next-line */
125
+ /** The employee's unique ID. This field only returns when SAML single sign-on (SSO) is enabled. The `login_type` value is `101` (SSO) (Example: "HqDyI037Qjili1kNsSIrIg") */
126
+ employee_unique_id?: string | undefined;
127
+ /** The manager for the user (Example: "thill@example.com") */
128
+ manager?: string | undefined;
129
+ /** The phone number's ISO country code (Example: "+1") */
130
+ phone_numbers?: PhoneNumber[] | undefined;
131
+ /** The user's plan type (Example: "1") */
132
+ plan_united_type?: string | undefined;
133
+ /** The user's pronouns (Example: "3123") */
134
+ pronouns?: string | undefined;
135
+ /** The user's display pronouns setting (Example: 1) */
136
+ pronouns_option?: PronounOption | undefined;
137
+ /** Personal meeting room URL, if the user has one (Example: "example.com") */
138
+ vanity_url?: string | undefined;
139
+ }
140
+
141
+ export interface ZoomOptions extends ProviderOptions<ZoomProfile> {
142
+ clientId: string;
143
+ pkce?: boolean | undefined;
144
+ }
145
+
146
+ export const zoom = (userOptions: ZoomOptions) => {
147
+ const options = {
148
+ pkce: true,
149
+ ...userOptions,
150
+ };
151
+
152
+ return {
153
+ id: "zoom",
154
+ name: "Zoom",
155
+ createAuthorizationURL: async ({ state, redirectURI, codeVerifier }) => {
156
+ const params = new URLSearchParams({
157
+ response_type: "code",
158
+ redirect_uri: options.redirectURI ? options.redirectURI : redirectURI,
159
+ client_id: options.clientId,
160
+ state,
161
+ });
162
+
163
+ if (options.pkce) {
164
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
165
+ params.set("code_challenge_method", "S256");
166
+ params.set("code_challenge", codeChallenge);
167
+ }
168
+
169
+ const url = new URL("https://zoom.us/oauth/authorize");
170
+ url.search = params.toString();
171
+
172
+ return url;
173
+ },
174
+ validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
175
+ return validateAuthorizationCode({
176
+ code,
177
+ redirectURI: options.redirectURI || redirectURI,
178
+ codeVerifier,
179
+ options,
180
+ tokenEndpoint: "https://zoom.us/oauth/token",
181
+ authentication: "post",
182
+ });
183
+ },
184
+ refreshAccessToken: options.refreshAccessToken
185
+ ? options.refreshAccessToken
186
+ : async (refreshToken) =>
187
+ refreshAccessToken({
188
+ refreshToken,
189
+ options: {
190
+ clientId: options.clientId,
191
+ clientKey: options.clientKey,
192
+ clientSecret: options.clientSecret,
193
+ },
194
+ tokenEndpoint: "https://zoom.us/oauth/token",
195
+ }),
196
+ async getUserInfo(token) {
197
+ if (options.getUserInfo) {
198
+ return options.getUserInfo(token);
199
+ }
200
+ const { data: profile, error } = await betterFetch<ZoomProfile>(
201
+ "https://api.zoom.us/v2/users/me",
202
+ {
203
+ headers: {
204
+ authorization: `Bearer ${token.accessToken}`,
205
+ },
206
+ },
207
+ );
208
+
209
+ if (error) {
210
+ return null;
211
+ }
212
+
213
+ const userMap = await options.mapProfileToUser?.(profile);
214
+
215
+ return {
216
+ user: {
217
+ id: profile.id,
218
+ name: profile.display_name,
219
+ image: profile.pic_url,
220
+ email: profile.email,
221
+ emailVerified: Boolean(profile.verified),
222
+ ...userMap,
223
+ },
224
+ data: {
225
+ ...profile,
226
+ },
227
+ };
228
+ },
229
+ } satisfies OAuthProvider<ZoomProfile>;
230
+ };