@rio.js/enterprise 1.4.0

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 (41) hide show
  1. package/README.md +89 -0
  2. package/dist/adapter-factory-BTRALCLD-kJBwe70v.mjs +836 -0
  3. package/dist/better-auth-CStoaWiq.d.mts +10 -0
  4. package/dist/better-auth-CqfhQJYE.mjs +558 -0
  5. package/dist/better-auth.d.mts +2 -0
  6. package/dist/better-auth.mjs +17 -0
  7. package/dist/bun-sqlite-dialect-2R9nCsVF-DFs6tpGr.mjs +155 -0
  8. package/dist/client--1_AEBPu-8Ae9icC9.mjs +125 -0
  9. package/dist/client.d.mts +17 -0
  10. package/dist/client.mjs +381 -0
  11. package/dist/db-BVXTgOd3.mjs +681 -0
  12. package/dist/db-BadqSwVl.d.mts +9542 -0
  13. package/dist/db-schema.final-DWleoQm0.mjs +785 -0
  14. package/dist/db.d.mts +2 -0
  15. package/dist/db.mjs +3 -0
  16. package/dist/dialect-C6_pK3V9-CPJHWkYR.mjs +72 -0
  17. package/dist/dist-CygcgJYk.mjs +422 -0
  18. package/dist/env-DwlNAN_D-C1zHd0cf-Cdlw8sNp.mjs +289 -0
  19. package/dist/esm-C5TuvtGn.mjs +15816 -0
  20. package/dist/index.d.mts +6 -0
  21. package/dist/index.mjs +17 -0
  22. package/dist/init-D8lwWc90.mjs +27 -0
  23. package/dist/json-oFuWgANh-O1U6k3bL.mjs +3811 -0
  24. package/dist/kysely-adapter-D_seG51p.mjs +297 -0
  25. package/dist/memory-adapter-CY-oDozb.mjs +215 -0
  26. package/dist/misc-CbURQDlR-sLtUwwQY.mjs +7 -0
  27. package/dist/node-sqlite-dialect-CdC7L-ji-QLbJGmDc.mjs +155 -0
  28. package/dist/parser-bL7W2mQ0-YdTgjtji.mjs +140 -0
  29. package/dist/plugins-BNFht2HW.mjs +23358 -0
  30. package/dist/plugins.d.mts +1 -0
  31. package/dist/plugins.mjs +13 -0
  32. package/dist/react--VZQu7s1.mjs +560 -0
  33. package/dist/react.d.mts +1 -0
  34. package/dist/react.mjs +6 -0
  35. package/dist/server.d.mts +10 -0
  36. package/dist/server.mjs +45 -0
  37. package/dist/social-providers-DNfE9Ak7-Be5zMAEe.mjs +2920 -0
  38. package/dist/social-providers.d.mts +1 -0
  39. package/dist/social-providers.mjs +6 -0
  40. package/dist/verify-CN5Qc0e-.mjs +1183 -0
  41. package/package.json +98 -0
@@ -0,0 +1,2920 @@
1
+ import { f as logger, g as BetterAuthError } from "./env-DwlNAN_D-C1zHd0cf-Cdlw8sNp.mjs";
2
+ import { A as JWKSTimeout, C as JOSENotSupported, D as JWKSInvalid, F as decode, H as base64, O as JWKSMultipleMatchingKeys, P as JWTInvalid, R as decoder, S as JOSEError, U as base64Url, d as importJWK, k as JWKSNoMatchingKey, p as isObject, t as jwtVerify } from "./verify-CN5Qc0e-.mjs";
3
+ import { t as betterFetch } from "./dist-CygcgJYk.mjs";
4
+ import { APIError } from "better-call";
5
+ import * as z$2 from "zod";
6
+
7
+ //#region ../../node_modules/.pnpm/jose@6.1.2/node_modules/jose/dist/webapi/jwks/local.js
8
+ function getKtyFromAlg(alg) {
9
+ switch (typeof alg === "string" && alg.slice(0, 2)) {
10
+ case "RS":
11
+ case "PS": return "RSA";
12
+ case "ES": return "EC";
13
+ case "Ed": return "OKP";
14
+ case "ML": return "AKP";
15
+ default: throw new JOSENotSupported("Unsupported \"alg\" value for a JSON Web Key Set");
16
+ }
17
+ }
18
+ function isJWKSLike(jwks) {
19
+ return jwks && typeof jwks === "object" && Array.isArray(jwks.keys) && jwks.keys.every(isJWKLike);
20
+ }
21
+ function isJWKLike(key) {
22
+ return isObject(key);
23
+ }
24
+ var LocalJWKSet = class {
25
+ #jwks;
26
+ #cached = /* @__PURE__ */ new WeakMap();
27
+ constructor(jwks) {
28
+ if (!isJWKSLike(jwks)) throw new JWKSInvalid("JSON Web Key Set malformed");
29
+ this.#jwks = structuredClone(jwks);
30
+ }
31
+ jwks() {
32
+ return this.#jwks;
33
+ }
34
+ async getKey(protectedHeader, token) {
35
+ const { alg, kid } = {
36
+ ...protectedHeader,
37
+ ...token?.header
38
+ };
39
+ const kty = getKtyFromAlg(alg);
40
+ const candidates = this.#jwks.keys.filter((jwk$1) => {
41
+ let candidate = kty === jwk$1.kty;
42
+ if (candidate && typeof kid === "string") candidate = kid === jwk$1.kid;
43
+ if (candidate && (typeof jwk$1.alg === "string" || kty === "AKP")) candidate = alg === jwk$1.alg;
44
+ if (candidate && typeof jwk$1.use === "string") candidate = jwk$1.use === "sig";
45
+ if (candidate && Array.isArray(jwk$1.key_ops)) candidate = jwk$1.key_ops.includes("verify");
46
+ if (candidate) switch (alg) {
47
+ case "ES256":
48
+ candidate = jwk$1.crv === "P-256";
49
+ break;
50
+ case "ES384":
51
+ candidate = jwk$1.crv === "P-384";
52
+ break;
53
+ case "ES512":
54
+ candidate = jwk$1.crv === "P-521";
55
+ break;
56
+ case "Ed25519":
57
+ case "EdDSA":
58
+ candidate = jwk$1.crv === "Ed25519";
59
+ break;
60
+ }
61
+ return candidate;
62
+ });
63
+ const { 0: jwk, length } = candidates;
64
+ if (length === 0) throw new JWKSNoMatchingKey();
65
+ if (length !== 1) {
66
+ const error = new JWKSMultipleMatchingKeys();
67
+ const _cached = this.#cached;
68
+ error[Symbol.asyncIterator] = async function* () {
69
+ for (const jwk$1 of candidates) try {
70
+ yield await importWithAlgCache(_cached, jwk$1, alg);
71
+ } catch {}
72
+ };
73
+ throw error;
74
+ }
75
+ return importWithAlgCache(this.#cached, jwk, alg);
76
+ }
77
+ };
78
+ async function importWithAlgCache(cache, jwk, alg) {
79
+ const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);
80
+ if (cached[alg] === void 0) {
81
+ const key = await importJWK({
82
+ ...jwk,
83
+ ext: true
84
+ }, alg);
85
+ if (key instanceof Uint8Array || key.type !== "public") throw new JWKSInvalid("JSON Web Key Set members must be public keys");
86
+ cached[alg] = key;
87
+ }
88
+ return cached[alg];
89
+ }
90
+ function createLocalJWKSet(jwks) {
91
+ const set = new LocalJWKSet(jwks);
92
+ const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
93
+ Object.defineProperties(localJWKSet, { jwks: {
94
+ value: () => structuredClone(set.jwks()),
95
+ enumerable: false,
96
+ configurable: false,
97
+ writable: false
98
+ } });
99
+ return localJWKSet;
100
+ }
101
+
102
+ //#endregion
103
+ //#region ../../node_modules/.pnpm/jose@6.1.2/node_modules/jose/dist/webapi/jwks/remote.js
104
+ function isCloudflareWorkers() {
105
+ return typeof WebSocketPair !== "undefined" || typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers" || typeof EdgeRuntime !== "undefined" && EdgeRuntime === "vercel";
106
+ }
107
+ let USER_AGENT;
108
+ if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) USER_AGENT = `jose/v6.1.2`;
109
+ const customFetch = Symbol();
110
+ async function fetchJwks(url, headers, signal, fetchImpl = fetch) {
111
+ const response = await fetchImpl(url, {
112
+ method: "GET",
113
+ signal,
114
+ redirect: "manual",
115
+ headers
116
+ }).catch((err) => {
117
+ if (err.name === "TimeoutError") throw new JWKSTimeout();
118
+ throw err;
119
+ });
120
+ if (response.status !== 200) throw new JOSEError("Expected 200 OK from the JSON Web Key Set HTTP response");
121
+ try {
122
+ return await response.json();
123
+ } catch {
124
+ throw new JOSEError("Failed to parse the JSON Web Key Set HTTP response as JSON");
125
+ }
126
+ }
127
+ const jwksCache = Symbol();
128
+ function isFreshJwksCache(input, cacheMaxAge) {
129
+ if (typeof input !== "object" || input === null) return false;
130
+ if (!("uat" in input) || typeof input.uat !== "number" || Date.now() - input.uat >= cacheMaxAge) return false;
131
+ if (!("jwks" in input) || !isObject(input.jwks) || !Array.isArray(input.jwks.keys) || !Array.prototype.every.call(input.jwks.keys, isObject)) return false;
132
+ return true;
133
+ }
134
+ var RemoteJWKSet = class {
135
+ #url;
136
+ #timeoutDuration;
137
+ #cooldownDuration;
138
+ #cacheMaxAge;
139
+ #jwksTimestamp;
140
+ #pendingFetch;
141
+ #headers;
142
+ #customFetch;
143
+ #local;
144
+ #cache;
145
+ constructor(url, options) {
146
+ if (!(url instanceof URL)) throw new TypeError("url must be an instance of URL");
147
+ this.#url = new URL(url.href);
148
+ this.#timeoutDuration = typeof options?.timeoutDuration === "number" ? options?.timeoutDuration : 5e3;
149
+ this.#cooldownDuration = typeof options?.cooldownDuration === "number" ? options?.cooldownDuration : 3e4;
150
+ this.#cacheMaxAge = typeof options?.cacheMaxAge === "number" ? options?.cacheMaxAge : 6e5;
151
+ this.#headers = new Headers(options?.headers);
152
+ if (USER_AGENT && !this.#headers.has("User-Agent")) this.#headers.set("User-Agent", USER_AGENT);
153
+ if (!this.#headers.has("accept")) {
154
+ this.#headers.set("accept", "application/json");
155
+ this.#headers.append("accept", "application/jwk-set+json");
156
+ }
157
+ this.#customFetch = options?.[customFetch];
158
+ if (options?.[jwksCache] !== void 0) {
159
+ this.#cache = options?.[jwksCache];
160
+ if (isFreshJwksCache(options?.[jwksCache], this.#cacheMaxAge)) {
161
+ this.#jwksTimestamp = this.#cache.uat;
162
+ this.#local = createLocalJWKSet(this.#cache.jwks);
163
+ }
164
+ }
165
+ }
166
+ pendingFetch() {
167
+ return !!this.#pendingFetch;
168
+ }
169
+ coolingDown() {
170
+ return typeof this.#jwksTimestamp === "number" ? Date.now() < this.#jwksTimestamp + this.#cooldownDuration : false;
171
+ }
172
+ fresh() {
173
+ return typeof this.#jwksTimestamp === "number" ? Date.now() < this.#jwksTimestamp + this.#cacheMaxAge : false;
174
+ }
175
+ jwks() {
176
+ return this.#local?.jwks();
177
+ }
178
+ async getKey(protectedHeader, token) {
179
+ if (!this.#local || !this.fresh()) await this.reload();
180
+ try {
181
+ return await this.#local(protectedHeader, token);
182
+ } catch (err) {
183
+ if (err instanceof JWKSNoMatchingKey) {
184
+ if (this.coolingDown() === false) {
185
+ await this.reload();
186
+ return this.#local(protectedHeader, token);
187
+ }
188
+ }
189
+ throw err;
190
+ }
191
+ }
192
+ async reload() {
193
+ if (this.#pendingFetch && isCloudflareWorkers()) this.#pendingFetch = void 0;
194
+ this.#pendingFetch ||= fetchJwks(this.#url.href, this.#headers, AbortSignal.timeout(this.#timeoutDuration), this.#customFetch).then((json) => {
195
+ this.#local = createLocalJWKSet(json);
196
+ if (this.#cache) {
197
+ this.#cache.uat = Date.now();
198
+ this.#cache.jwks = json;
199
+ }
200
+ this.#jwksTimestamp = Date.now();
201
+ this.#pendingFetch = void 0;
202
+ }).catch((err) => {
203
+ this.#pendingFetch = void 0;
204
+ throw err;
205
+ });
206
+ await this.#pendingFetch;
207
+ }
208
+ };
209
+ function createRemoteJWKSet(url, options) {
210
+ const set = new RemoteJWKSet(url, options);
211
+ const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);
212
+ Object.defineProperties(remoteJWKSet, {
213
+ coolingDown: {
214
+ get: () => set.coolingDown(),
215
+ enumerable: true,
216
+ configurable: false
217
+ },
218
+ fresh: {
219
+ get: () => set.fresh(),
220
+ enumerable: true,
221
+ configurable: false
222
+ },
223
+ reload: {
224
+ value: () => set.reload(),
225
+ enumerable: true,
226
+ configurable: false,
227
+ writable: false
228
+ },
229
+ reloading: {
230
+ get: () => set.pendingFetch(),
231
+ enumerable: true,
232
+ configurable: false
233
+ },
234
+ jwks: {
235
+ value: () => set.jwks(),
236
+ enumerable: true,
237
+ configurable: false,
238
+ writable: false
239
+ }
240
+ });
241
+ return remoteJWKSet;
242
+ }
243
+
244
+ //#endregion
245
+ //#region ../../node_modules/.pnpm/jose@6.1.2/node_modules/jose/dist/webapi/util/decode_protected_header.js
246
+ function decodeProtectedHeader(token) {
247
+ let protectedB64u;
248
+ if (typeof token === "string") {
249
+ const parts = token.split(".");
250
+ if (parts.length === 3 || parts.length === 5) [protectedB64u] = parts;
251
+ } else if (typeof token === "object" && token) if ("protected" in token) protectedB64u = token.protected;
252
+ else throw new TypeError("Token does not contain a Protected Header");
253
+ try {
254
+ if (typeof protectedB64u !== "string" || !protectedB64u) throw new Error();
255
+ const result = JSON.parse(decoder.decode(decode(protectedB64u)));
256
+ if (!isObject(result)) throw new Error();
257
+ return result;
258
+ } catch {
259
+ throw new TypeError("Invalid Token or Protected Header formatting");
260
+ }
261
+ }
262
+
263
+ //#endregion
264
+ //#region ../../node_modules/.pnpm/jose@6.1.2/node_modules/jose/dist/webapi/util/decode_jwt.js
265
+ function decodeJwt(jwt) {
266
+ if (typeof jwt !== "string") throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");
267
+ const { 1: payload, length } = jwt.split(".");
268
+ if (length === 5) throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded");
269
+ if (length !== 3) throw new JWTInvalid("Invalid JWT");
270
+ if (!payload) throw new JWTInvalid("JWTs must contain a payload");
271
+ let decoded;
272
+ try {
273
+ decoded = decode(payload);
274
+ } catch {
275
+ throw new JWTInvalid("Failed to base64url decode the payload");
276
+ }
277
+ let result;
278
+ try {
279
+ result = JSON.parse(decoder.decode(decoded));
280
+ } catch {
281
+ throw new JWTInvalid("Failed to parse the decoded payload as JSON");
282
+ }
283
+ if (!isObject(result)) throw new JWTInvalid("Invalid JWT Claims Set");
284
+ return result;
285
+ }
286
+
287
+ //#endregion
288
+ //#region ../better-auth/dist/social-providers-DNfE9Ak7.mjs
289
+ function createClientCredentialsTokenRequest({ options, scope, authentication, resource }) {
290
+ const body = new URLSearchParams();
291
+ const headers = {
292
+ "content-type": "application/x-www-form-urlencoded",
293
+ accept: "application/json"
294
+ };
295
+ body.set("grant_type", "client_credentials");
296
+ scope && body.set("scope", scope);
297
+ if (resource) if (typeof resource === "string") body.append("resource", resource);
298
+ else for (const _resource of resource) body.append("resource", _resource);
299
+ if (authentication === "basic") {
300
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
301
+ headers["authorization"] = `Basic ${base64Url.encode(`${primaryClientId}:${options.clientSecret}`)}`;
302
+ } else {
303
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
304
+ body.set("client_id", primaryClientId);
305
+ body.set("client_secret", options.clientSecret);
306
+ }
307
+ return {
308
+ body,
309
+ headers
310
+ };
311
+ }
312
+ async function clientCredentialsToken({ options, tokenEndpoint, scope, authentication, resource }) {
313
+ const { body, headers } = createClientCredentialsTokenRequest({
314
+ options,
315
+ scope,
316
+ authentication,
317
+ resource
318
+ });
319
+ const { data, error } = await betterFetch(tokenEndpoint, {
320
+ method: "POST",
321
+ body,
322
+ headers
323
+ });
324
+ if (error) throw error;
325
+ const tokens = {
326
+ accessToken: data.access_token,
327
+ tokenType: data.token_type,
328
+ scopes: data.scope?.split(" ")
329
+ };
330
+ if (data.expires_in) {
331
+ const now = /* @__PURE__ */ new Date();
332
+ tokens.accessTokenExpiresAt = new Date(now.getTime() + data.expires_in * 1e3);
333
+ }
334
+ return tokens;
335
+ }
336
+ function getOAuth2Tokens(data) {
337
+ const getDate = (seconds) => {
338
+ const now = /* @__PURE__ */ new Date();
339
+ return new Date(now.getTime() + seconds * 1e3);
340
+ };
341
+ return {
342
+ tokenType: data.token_type,
343
+ accessToken: data.access_token,
344
+ refreshToken: data.refresh_token,
345
+ accessTokenExpiresAt: data.expires_in ? getDate(data.expires_in) : void 0,
346
+ refreshTokenExpiresAt: data.refresh_token_expires_in ? getDate(data.refresh_token_expires_in) : void 0,
347
+ scopes: data?.scope ? typeof data.scope === "string" ? data.scope.split(" ") : data.scope : [],
348
+ idToken: data.id_token,
349
+ raw: data
350
+ };
351
+ }
352
+ async function generateCodeChallenge(codeVerifier) {
353
+ const data = new TextEncoder().encode(codeVerifier);
354
+ const hash = await crypto.subtle.digest("SHA-256", data);
355
+ return base64Url.encode(new Uint8Array(hash), { padding: false });
356
+ }
357
+ async function createAuthorizationURL({ id, options, authorizationEndpoint, state, codeVerifier, scopes, claims, redirectURI, duration, prompt, accessType, responseType, display, loginHint, hd, responseMode, additionalParams, scopeJoiner }) {
358
+ const url = new URL(authorizationEndpoint);
359
+ url.searchParams.set("response_type", responseType || "code");
360
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
361
+ url.searchParams.set("client_id", primaryClientId);
362
+ url.searchParams.set("state", state);
363
+ url.searchParams.set("scope", scopes.join(scopeJoiner || " "));
364
+ url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
365
+ duration && url.searchParams.set("duration", duration);
366
+ display && url.searchParams.set("display", display);
367
+ loginHint && url.searchParams.set("login_hint", loginHint);
368
+ prompt && url.searchParams.set("prompt", prompt);
369
+ hd && url.searchParams.set("hd", hd);
370
+ accessType && url.searchParams.set("access_type", accessType);
371
+ responseMode && url.searchParams.set("response_mode", responseMode);
372
+ if (codeVerifier) {
373
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
374
+ url.searchParams.set("code_challenge_method", "S256");
375
+ url.searchParams.set("code_challenge", codeChallenge);
376
+ }
377
+ if (claims) {
378
+ const claimsObj = claims.reduce((acc, claim) => {
379
+ acc[claim] = null;
380
+ return acc;
381
+ }, {});
382
+ url.searchParams.set("claims", JSON.stringify({ id_token: {
383
+ email: null,
384
+ email_verified: null,
385
+ ...claimsObj
386
+ } }));
387
+ }
388
+ if (additionalParams) Object.entries(additionalParams).forEach(([key, value]) => {
389
+ url.searchParams.set(key, value);
390
+ });
391
+ return url;
392
+ }
393
+ function createRefreshAccessTokenRequest({ refreshToken, options, authentication, extraParams, resource }) {
394
+ const body = new URLSearchParams();
395
+ const headers = {
396
+ "content-type": "application/x-www-form-urlencoded",
397
+ accept: "application/json"
398
+ };
399
+ body.set("grant_type", "refresh_token");
400
+ body.set("refresh_token", refreshToken);
401
+ if (authentication === "basic") {
402
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
403
+ if (primaryClientId) headers["authorization"] = "Basic " + base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`);
404
+ else headers["authorization"] = "Basic " + base64.encode(`:${options.clientSecret ?? ""}`);
405
+ } else {
406
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
407
+ body.set("client_id", primaryClientId);
408
+ if (options.clientSecret) body.set("client_secret", options.clientSecret);
409
+ }
410
+ if (resource) if (typeof resource === "string") body.append("resource", resource);
411
+ else for (const _resource of resource) body.append("resource", _resource);
412
+ if (extraParams) for (const [key, value] of Object.entries(extraParams)) body.set(key, value);
413
+ return {
414
+ body,
415
+ headers
416
+ };
417
+ }
418
+ async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authentication, extraParams }) {
419
+ const { body, headers } = createRefreshAccessTokenRequest({
420
+ refreshToken,
421
+ options,
422
+ authentication,
423
+ extraParams
424
+ });
425
+ const { data, error } = await betterFetch(tokenEndpoint, {
426
+ method: "POST",
427
+ body,
428
+ headers
429
+ });
430
+ if (error) throw error;
431
+ const tokens = {
432
+ accessToken: data.access_token,
433
+ refreshToken: data.refresh_token,
434
+ tokenType: data.token_type,
435
+ scopes: data.scope?.split(" "),
436
+ idToken: data.id_token
437
+ };
438
+ if (data.expires_in) {
439
+ const now = /* @__PURE__ */ new Date();
440
+ tokens.accessTokenExpiresAt = new Date(now.getTime() + data.expires_in * 1e3);
441
+ }
442
+ return tokens;
443
+ }
444
+ function createAuthorizationCodeRequest({ code, codeVerifier, redirectURI, options, authentication, deviceId, headers, additionalParams = {}, resource }) {
445
+ const body = new URLSearchParams();
446
+ const requestHeaders = {
447
+ "content-type": "application/x-www-form-urlencoded",
448
+ accept: "application/json",
449
+ ...headers
450
+ };
451
+ body.set("grant_type", "authorization_code");
452
+ body.set("code", code);
453
+ codeVerifier && body.set("code_verifier", codeVerifier);
454
+ options.clientKey && body.set("client_key", options.clientKey);
455
+ deviceId && body.set("device_id", deviceId);
456
+ body.set("redirect_uri", options.redirectURI || redirectURI);
457
+ if (resource) if (typeof resource === "string") body.append("resource", resource);
458
+ else for (const _resource of resource) body.append("resource", _resource);
459
+ if (authentication === "basic") {
460
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
461
+ requestHeaders["authorization"] = `Basic ${base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`)}`;
462
+ } else {
463
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
464
+ body.set("client_id", primaryClientId);
465
+ if (options.clientSecret) body.set("client_secret", options.clientSecret);
466
+ }
467
+ for (const [key, value] of Object.entries(additionalParams)) if (!body.has(key)) body.append(key, value);
468
+ return {
469
+ body,
470
+ headers: requestHeaders
471
+ };
472
+ }
473
+ async function validateAuthorizationCode({ code, codeVerifier, redirectURI, options, tokenEndpoint, authentication, deviceId, headers, additionalParams = {}, resource }) {
474
+ const { body, headers: requestHeaders } = createAuthorizationCodeRequest({
475
+ code,
476
+ codeVerifier,
477
+ redirectURI,
478
+ options,
479
+ authentication,
480
+ deviceId,
481
+ headers,
482
+ additionalParams,
483
+ resource
484
+ });
485
+ const { data, error } = await betterFetch(tokenEndpoint, {
486
+ method: "POST",
487
+ body,
488
+ headers: requestHeaders
489
+ });
490
+ if (error) throw error;
491
+ return getOAuth2Tokens(data);
492
+ }
493
+ async function validateToken(token, jwksEndpoint) {
494
+ const { data, error } = await betterFetch(jwksEndpoint, {
495
+ method: "GET",
496
+ headers: { accept: "application/json" }
497
+ });
498
+ if (error) throw error;
499
+ const keys = data["keys"];
500
+ const header = JSON.parse(atob(token.split(".")[0]));
501
+ const key = keys.find((key$1) => key$1.kid === header.kid);
502
+ if (!key) throw new Error("Key not found");
503
+ return await jwtVerify(token, key);
504
+ }
505
+ const apple = (options) => {
506
+ const tokenEndpoint = "https://appleid.apple.com/auth/token";
507
+ return {
508
+ id: "apple",
509
+ name: "Apple",
510
+ async createAuthorizationURL({ state, scopes, redirectURI }) {
511
+ const _scope = options.disableDefaultScope ? [] : ["email", "name"];
512
+ if (options.scope) _scope.push(...options.scope);
513
+ if (scopes) _scope.push(...scopes);
514
+ return await createAuthorizationURL({
515
+ id: "apple",
516
+ options,
517
+ authorizationEndpoint: "https://appleid.apple.com/auth/authorize",
518
+ scopes: _scope,
519
+ state,
520
+ redirectURI,
521
+ responseMode: "form_post",
522
+ responseType: "code id_token"
523
+ });
524
+ },
525
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
526
+ return validateAuthorizationCode({
527
+ code,
528
+ codeVerifier,
529
+ redirectURI,
530
+ options,
531
+ tokenEndpoint
532
+ });
533
+ },
534
+ async verifyIdToken(token, nonce) {
535
+ if (options.disableIdTokenSignIn) return false;
536
+ if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
537
+ const { kid, alg: jwtAlg } = decodeProtectedHeader(token);
538
+ if (!kid || !jwtAlg) return false;
539
+ const { payload: jwtClaims } = await jwtVerify(token, await getApplePublicKey(kid), {
540
+ algorithms: [jwtAlg],
541
+ issuer: "https://appleid.apple.com",
542
+ audience: options.audience && options.audience.length ? options.audience : options.appBundleIdentifier ? options.appBundleIdentifier : options.clientId,
543
+ maxTokenAge: "1h"
544
+ });
545
+ ["email_verified", "is_private_email"].forEach((field) => {
546
+ if (jwtClaims[field] !== void 0) jwtClaims[field] = Boolean(jwtClaims[field]);
547
+ });
548
+ if (nonce && jwtClaims.nonce !== nonce) return false;
549
+ return !!jwtClaims;
550
+ },
551
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
552
+ return refreshAccessToken({
553
+ refreshToken,
554
+ options: {
555
+ clientId: options.clientId,
556
+ clientKey: options.clientKey,
557
+ clientSecret: options.clientSecret
558
+ },
559
+ tokenEndpoint: "https://appleid.apple.com/auth/token"
560
+ });
561
+ },
562
+ async getUserInfo(token) {
563
+ if (options.getUserInfo) return options.getUserInfo(token);
564
+ if (!token.idToken) return null;
565
+ const profile = decodeJwt(token.idToken);
566
+ if (!profile) return null;
567
+ const name = token.user ? `${token.user.name?.firstName} ${token.user.name?.lastName}` : profile.name || profile.email;
568
+ const emailVerified = typeof profile.email_verified === "boolean" ? profile.email_verified : profile.email_verified === "true";
569
+ const enrichedProfile = {
570
+ ...profile,
571
+ name
572
+ };
573
+ const userMap = await options.mapProfileToUser?.(enrichedProfile);
574
+ return {
575
+ user: {
576
+ id: profile.sub,
577
+ name: enrichedProfile.name,
578
+ emailVerified,
579
+ email: profile.email,
580
+ ...userMap
581
+ },
582
+ data: enrichedProfile
583
+ };
584
+ },
585
+ options
586
+ };
587
+ };
588
+ const getApplePublicKey = async (kid) => {
589
+ const { data } = await betterFetch(`https://appleid.apple.com/auth/keys`);
590
+ if (!data?.keys) throw new APIError("BAD_REQUEST", { message: "Keys not found" });
591
+ const jwk = data.keys.find((key) => key.kid === kid);
592
+ if (!jwk) throw new Error(`JWK with kid ${kid} not found`);
593
+ return await importJWK(jwk, jwk.alg);
594
+ };
595
+ const atlassian = (options) => {
596
+ return {
597
+ id: "atlassian",
598
+ name: "Atlassian",
599
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
600
+ if (!options.clientId || !options.clientSecret) {
601
+ logger.error("Client Id and Secret are required for Atlassian");
602
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
603
+ }
604
+ if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Atlassian");
605
+ const _scopes = options.disableDefaultScope ? [] : ["read:jira-user", "offline_access"];
606
+ if (options.scope) _scopes.push(...options.scope);
607
+ if (scopes) _scopes.push(...scopes);
608
+ return createAuthorizationURL({
609
+ id: "atlassian",
610
+ options,
611
+ authorizationEndpoint: "https://auth.atlassian.com/authorize",
612
+ scopes: _scopes,
613
+ state,
614
+ codeVerifier,
615
+ redirectURI,
616
+ additionalParams: { audience: "api.atlassian.com" },
617
+ prompt: options.prompt
618
+ });
619
+ },
620
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
621
+ return validateAuthorizationCode({
622
+ code,
623
+ codeVerifier,
624
+ redirectURI,
625
+ options,
626
+ tokenEndpoint: "https://auth.atlassian.com/oauth/token"
627
+ });
628
+ },
629
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
630
+ return refreshAccessToken({
631
+ refreshToken,
632
+ options: {
633
+ clientId: options.clientId,
634
+ clientSecret: options.clientSecret
635
+ },
636
+ tokenEndpoint: "https://auth.atlassian.com/oauth/token"
637
+ });
638
+ },
639
+ async getUserInfo(token) {
640
+ if (options.getUserInfo) return options.getUserInfo(token);
641
+ if (!token.accessToken) return null;
642
+ try {
643
+ const { data: profile } = await betterFetch("https://api.atlassian.com/me", { headers: { Authorization: `Bearer ${token.accessToken}` } });
644
+ if (!profile) return null;
645
+ const userMap = await options.mapProfileToUser?.(profile);
646
+ return {
647
+ user: {
648
+ id: profile.account_id,
649
+ name: profile.name,
650
+ email: profile.email,
651
+ image: profile.picture,
652
+ emailVerified: false,
653
+ ...userMap
654
+ },
655
+ data: profile
656
+ };
657
+ } catch (error) {
658
+ logger.error("Failed to fetch user info from Figma:", error);
659
+ return null;
660
+ }
661
+ },
662
+ options
663
+ };
664
+ };
665
+ const cognito = (options) => {
666
+ if (!options.domain || !options.region || !options.userPoolId) {
667
+ logger.error("Domain, region and userPoolId are required for Amazon Cognito. Make sure to provide them in the options.");
668
+ throw new BetterAuthError("DOMAIN_AND_REGION_REQUIRED");
669
+ }
670
+ const cleanDomain = options.domain.replace(/^https?:\/\//, "");
671
+ const authorizationEndpoint = `https://${cleanDomain}/oauth2/authorize`;
672
+ const tokenEndpoint = `https://${cleanDomain}/oauth2/token`;
673
+ const userInfoEndpoint = `https://${cleanDomain}/oauth2/userinfo`;
674
+ return {
675
+ id: "cognito",
676
+ name: "Cognito",
677
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
678
+ if (!options.clientId) {
679
+ logger.error("ClientId is required for Amazon Cognito. Make sure to provide them in the options.");
680
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
681
+ }
682
+ if (options.requireClientSecret && !options.clientSecret) {
683
+ logger.error("Client Secret is required when requireClientSecret is true. Make sure to provide it in the options.");
684
+ throw new BetterAuthError("CLIENT_SECRET_REQUIRED");
685
+ }
686
+ const _scopes = options.disableDefaultScope ? [] : [
687
+ "openid",
688
+ "profile",
689
+ "email"
690
+ ];
691
+ if (options.scope) _scopes.push(...options.scope);
692
+ if (scopes) _scopes.push(...scopes);
693
+ return await createAuthorizationURL({
694
+ id: "cognito",
695
+ options: { ...options },
696
+ authorizationEndpoint,
697
+ scopes: _scopes,
698
+ state,
699
+ codeVerifier,
700
+ redirectURI,
701
+ prompt: options.prompt
702
+ });
703
+ },
704
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
705
+ return validateAuthorizationCode({
706
+ code,
707
+ codeVerifier,
708
+ redirectURI,
709
+ options,
710
+ tokenEndpoint
711
+ });
712
+ },
713
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
714
+ return refreshAccessToken({
715
+ refreshToken,
716
+ options: {
717
+ clientId: options.clientId,
718
+ clientKey: options.clientKey,
719
+ clientSecret: options.clientSecret
720
+ },
721
+ tokenEndpoint
722
+ });
723
+ },
724
+ async verifyIdToken(token, nonce) {
725
+ if (options.disableIdTokenSignIn) return false;
726
+ if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
727
+ try {
728
+ const { kid, alg: jwtAlg } = decodeProtectedHeader(token);
729
+ if (!kid || !jwtAlg) return false;
730
+ const publicKey = await getCognitoPublicKey(kid, options.region, options.userPoolId);
731
+ const expectedIssuer = `https://cognito-idp.${options.region}.amazonaws.com/${options.userPoolId}`;
732
+ const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
733
+ algorithms: [jwtAlg],
734
+ issuer: expectedIssuer,
735
+ audience: options.clientId,
736
+ maxTokenAge: "1h"
737
+ });
738
+ if (nonce && jwtClaims.nonce !== nonce) return false;
739
+ return true;
740
+ } catch (error) {
741
+ logger.error("Failed to verify ID token:", error);
742
+ return false;
743
+ }
744
+ },
745
+ async getUserInfo(token) {
746
+ if (options.getUserInfo) return options.getUserInfo(token);
747
+ if (token.idToken) try {
748
+ const profile = decodeJwt(token.idToken);
749
+ if (!profile) return null;
750
+ const name = profile.name || profile.given_name || profile.username || profile.email;
751
+ const enrichedProfile = {
752
+ ...profile,
753
+ name
754
+ };
755
+ const userMap = await options.mapProfileToUser?.(enrichedProfile);
756
+ return {
757
+ user: {
758
+ id: profile.sub,
759
+ name: enrichedProfile.name,
760
+ email: profile.email,
761
+ image: profile.picture,
762
+ emailVerified: profile.email_verified,
763
+ ...userMap
764
+ },
765
+ data: enrichedProfile
766
+ };
767
+ } catch (error) {
768
+ logger.error("Failed to decode ID token:", error);
769
+ }
770
+ if (token.accessToken) try {
771
+ const { data: userInfo } = await betterFetch(userInfoEndpoint, { headers: { Authorization: `Bearer ${token.accessToken}` } });
772
+ if (userInfo) {
773
+ const userMap = await options.mapProfileToUser?.(userInfo);
774
+ return {
775
+ user: {
776
+ id: userInfo.sub,
777
+ name: userInfo.name || userInfo.given_name || userInfo.username,
778
+ email: userInfo.email,
779
+ image: userInfo.picture,
780
+ emailVerified: userInfo.email_verified,
781
+ ...userMap
782
+ },
783
+ data: userInfo
784
+ };
785
+ }
786
+ } catch (error) {
787
+ logger.error("Failed to fetch user info from Cognito:", error);
788
+ }
789
+ return null;
790
+ },
791
+ options
792
+ };
793
+ };
794
+ const getCognitoPublicKey = async (kid, region, userPoolId) => {
795
+ const COGNITO_JWKS_URI = `https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json`;
796
+ try {
797
+ const { data } = await betterFetch(COGNITO_JWKS_URI);
798
+ if (!data?.keys) throw new APIError("BAD_REQUEST", { message: "Keys not found" });
799
+ const jwk = data.keys.find((key) => key.kid === kid);
800
+ if (!jwk) throw new Error(`JWK with kid ${kid} not found`);
801
+ return await importJWK(jwk, jwk.alg);
802
+ } catch (error) {
803
+ logger.error("Failed to fetch Cognito public key:", error);
804
+ throw error;
805
+ }
806
+ };
807
+ const discord = (options) => {
808
+ return {
809
+ id: "discord",
810
+ name: "Discord",
811
+ createAuthorizationURL({ state, scopes, redirectURI }) {
812
+ const _scopes = options.disableDefaultScope ? [] : ["identify", "email"];
813
+ if (scopes) _scopes.push(...scopes);
814
+ if (options.scope) _scopes.push(...options.scope);
815
+ const permissionsParam = _scopes.includes("bot") && options.permissions !== void 0 ? `&permissions=${options.permissions}` : "";
816
+ return new URL(`https://discord.com/api/oauth2/authorize?scope=${_scopes.join("+")}&response_type=code&client_id=${options.clientId}&redirect_uri=${encodeURIComponent(options.redirectURI || redirectURI)}&state=${state}&prompt=${options.prompt || "none"}${permissionsParam}`);
817
+ },
818
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
819
+ return validateAuthorizationCode({
820
+ code,
821
+ redirectURI,
822
+ options,
823
+ tokenEndpoint: "https://discord.com/api/oauth2/token"
824
+ });
825
+ },
826
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
827
+ return refreshAccessToken({
828
+ refreshToken,
829
+ options: {
830
+ clientId: options.clientId,
831
+ clientKey: options.clientKey,
832
+ clientSecret: options.clientSecret
833
+ },
834
+ tokenEndpoint: "https://discord.com/api/oauth2/token"
835
+ });
836
+ },
837
+ async getUserInfo(token) {
838
+ if (options.getUserInfo) return options.getUserInfo(token);
839
+ const { data: profile, error } = await betterFetch("https://discord.com/api/users/@me", { headers: { authorization: `Bearer ${token.accessToken}` } });
840
+ if (error) return null;
841
+ if (profile.avatar === null) profile.image_url = `https://cdn.discordapp.com/embed/avatars/${profile.discriminator === "0" ? Number(BigInt(profile.id) >> BigInt(22)) % 6 : parseInt(profile.discriminator) % 5}.png`;
842
+ else {
843
+ const format = profile.avatar.startsWith("a_") ? "gif" : "png";
844
+ profile.image_url = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`;
845
+ }
846
+ const userMap = await options.mapProfileToUser?.(profile);
847
+ return {
848
+ user: {
849
+ id: profile.id,
850
+ name: profile.global_name || profile.username || "",
851
+ email: profile.email,
852
+ emailVerified: profile.verified,
853
+ image: profile.image_url,
854
+ ...userMap
855
+ },
856
+ data: profile
857
+ };
858
+ },
859
+ options
860
+ };
861
+ };
862
+ const dropbox = (options) => {
863
+ const tokenEndpoint = "https://api.dropboxapi.com/oauth2/token";
864
+ return {
865
+ id: "dropbox",
866
+ name: "Dropbox",
867
+ createAuthorizationURL: async ({ state, scopes, codeVerifier, redirectURI }) => {
868
+ const _scopes = options.disableDefaultScope ? [] : ["account_info.read"];
869
+ if (options.scope) _scopes.push(...options.scope);
870
+ if (scopes) _scopes.push(...scopes);
871
+ const additionalParams = {};
872
+ if (options.accessType) additionalParams.token_access_type = options.accessType;
873
+ return await createAuthorizationURL({
874
+ id: "dropbox",
875
+ options,
876
+ authorizationEndpoint: "https://www.dropbox.com/oauth2/authorize",
877
+ scopes: _scopes,
878
+ state,
879
+ redirectURI,
880
+ codeVerifier,
881
+ additionalParams
882
+ });
883
+ },
884
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
885
+ return await validateAuthorizationCode({
886
+ code,
887
+ codeVerifier,
888
+ redirectURI,
889
+ options,
890
+ tokenEndpoint
891
+ });
892
+ },
893
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
894
+ return refreshAccessToken({
895
+ refreshToken,
896
+ options: {
897
+ clientId: options.clientId,
898
+ clientKey: options.clientKey,
899
+ clientSecret: options.clientSecret
900
+ },
901
+ tokenEndpoint: "https://api.dropbox.com/oauth2/token"
902
+ });
903
+ },
904
+ async getUserInfo(token) {
905
+ if (options.getUserInfo) return options.getUserInfo(token);
906
+ const { data: profile, error } = await betterFetch("https://api.dropboxapi.com/2/users/get_current_account", {
907
+ method: "POST",
908
+ headers: { Authorization: `Bearer ${token.accessToken}` }
909
+ });
910
+ if (error) return null;
911
+ const userMap = await options.mapProfileToUser?.(profile);
912
+ return {
913
+ user: {
914
+ id: profile.account_id,
915
+ name: profile.name?.display_name,
916
+ email: profile.email,
917
+ emailVerified: profile.email_verified || false,
918
+ image: profile.profile_photo_url,
919
+ ...userMap
920
+ },
921
+ data: profile
922
+ };
923
+ },
924
+ options
925
+ };
926
+ };
927
+ const facebook = (options) => {
928
+ return {
929
+ id: "facebook",
930
+ name: "Facebook",
931
+ async createAuthorizationURL({ state, scopes, redirectURI, loginHint }) {
932
+ const _scopes = options.disableDefaultScope ? [] : ["email", "public_profile"];
933
+ if (options.scope) _scopes.push(...options.scope);
934
+ if (scopes) _scopes.push(...scopes);
935
+ return await createAuthorizationURL({
936
+ id: "facebook",
937
+ options,
938
+ authorizationEndpoint: "https://www.facebook.com/v21.0/dialog/oauth",
939
+ scopes: _scopes,
940
+ state,
941
+ redirectURI,
942
+ loginHint,
943
+ additionalParams: options.configId ? { config_id: options.configId } : {}
944
+ });
945
+ },
946
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
947
+ return validateAuthorizationCode({
948
+ code,
949
+ redirectURI,
950
+ options,
951
+ tokenEndpoint: "https://graph.facebook.com/oauth/access_token"
952
+ });
953
+ },
954
+ async verifyIdToken(token, nonce) {
955
+ if (options.disableIdTokenSignIn) return false;
956
+ if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
957
+ if (token.split(".").length === 3) try {
958
+ const { payload: jwtClaims } = await jwtVerify(token, createRemoteJWKSet(new URL("https://limited.facebook.com/.well-known/oauth/openid/jwks/")), {
959
+ algorithms: ["RS256"],
960
+ audience: options.clientId,
961
+ issuer: "https://www.facebook.com"
962
+ });
963
+ if (nonce && jwtClaims.nonce !== nonce) return false;
964
+ return !!jwtClaims;
965
+ } catch (error) {
966
+ return false;
967
+ }
968
+ return true;
969
+ },
970
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
971
+ return refreshAccessToken({
972
+ refreshToken,
973
+ options: {
974
+ clientId: options.clientId,
975
+ clientKey: options.clientKey,
976
+ clientSecret: options.clientSecret
977
+ },
978
+ tokenEndpoint: "https://graph.facebook.com/v18.0/oauth/access_token"
979
+ });
980
+ },
981
+ async getUserInfo(token) {
982
+ if (options.getUserInfo) return options.getUserInfo(token);
983
+ if (token.idToken && token.idToken.split(".").length === 3) {
984
+ const profile$1 = decodeJwt(token.idToken);
985
+ const user = {
986
+ id: profile$1.sub,
987
+ name: profile$1.name,
988
+ email: profile$1.email,
989
+ picture: { data: {
990
+ url: profile$1.picture,
991
+ height: 100,
992
+ width: 100,
993
+ is_silhouette: false
994
+ } }
995
+ };
996
+ const userMap$1 = await options.mapProfileToUser?.({
997
+ ...user,
998
+ email_verified: false
999
+ });
1000
+ return {
1001
+ user: {
1002
+ ...user,
1003
+ emailVerified: false,
1004
+ ...userMap$1
1005
+ },
1006
+ data: profile$1
1007
+ };
1008
+ }
1009
+ const { data: profile, error } = await betterFetch("https://graph.facebook.com/me?fields=" + [
1010
+ "id",
1011
+ "name",
1012
+ "email",
1013
+ "picture",
1014
+ ...options?.fields || []
1015
+ ].join(","), { auth: {
1016
+ type: "Bearer",
1017
+ token: token.accessToken
1018
+ } });
1019
+ if (error) return null;
1020
+ const userMap = await options.mapProfileToUser?.(profile);
1021
+ return {
1022
+ user: {
1023
+ id: profile.id,
1024
+ name: profile.name,
1025
+ email: profile.email,
1026
+ image: profile.picture.data.url,
1027
+ emailVerified: profile.email_verified,
1028
+ ...userMap
1029
+ },
1030
+ data: profile
1031
+ };
1032
+ },
1033
+ options
1034
+ };
1035
+ };
1036
+ const figma = (options) => {
1037
+ return {
1038
+ id: "figma",
1039
+ name: "Figma",
1040
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
1041
+ if (!options.clientId || !options.clientSecret) {
1042
+ logger.error("Client Id and Client Secret are required for Figma. Make sure to provide them in the options.");
1043
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
1044
+ }
1045
+ if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Figma");
1046
+ const _scopes = options.disableDefaultScope ? [] : ["file_read"];
1047
+ if (options.scope) _scopes.push(...options.scope);
1048
+ if (scopes) _scopes.push(...scopes);
1049
+ return await createAuthorizationURL({
1050
+ id: "figma",
1051
+ options,
1052
+ authorizationEndpoint: "https://www.figma.com/oauth",
1053
+ scopes: _scopes,
1054
+ state,
1055
+ codeVerifier,
1056
+ redirectURI
1057
+ });
1058
+ },
1059
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1060
+ return validateAuthorizationCode({
1061
+ code,
1062
+ codeVerifier,
1063
+ redirectURI,
1064
+ options,
1065
+ tokenEndpoint: "https://www.figma.com/api/oauth/token"
1066
+ });
1067
+ },
1068
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1069
+ return refreshAccessToken({
1070
+ refreshToken,
1071
+ options: {
1072
+ clientId: options.clientId,
1073
+ clientKey: options.clientKey,
1074
+ clientSecret: options.clientSecret
1075
+ },
1076
+ tokenEndpoint: "https://www.figma.com/api/oauth/token"
1077
+ });
1078
+ },
1079
+ async getUserInfo(token) {
1080
+ if (options.getUserInfo) return options.getUserInfo(token);
1081
+ try {
1082
+ const { data: profile } = await betterFetch("https://api.figma.com/v1/me", { headers: { Authorization: `Bearer ${token.accessToken}` } });
1083
+ if (!profile) {
1084
+ logger.error("Failed to fetch user from Figma");
1085
+ return null;
1086
+ }
1087
+ const userMap = await options.mapProfileToUser?.(profile);
1088
+ return {
1089
+ user: {
1090
+ id: profile.id,
1091
+ name: profile.handle,
1092
+ email: profile.email,
1093
+ image: profile.img_url,
1094
+ emailVerified: !!profile.email,
1095
+ ...userMap
1096
+ },
1097
+ data: profile
1098
+ };
1099
+ } catch (error) {
1100
+ logger.error("Failed to fetch user info from Figma:", error);
1101
+ return null;
1102
+ }
1103
+ },
1104
+ options
1105
+ };
1106
+ };
1107
+ const github = (options) => {
1108
+ const tokenEndpoint = "https://github.com/login/oauth/access_token";
1109
+ return {
1110
+ id: "github",
1111
+ name: "GitHub",
1112
+ createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
1113
+ const _scopes = options.disableDefaultScope ? [] : ["read:user", "user:email"];
1114
+ if (options.scope) _scopes.push(...options.scope);
1115
+ if (scopes) _scopes.push(...scopes);
1116
+ return createAuthorizationURL({
1117
+ id: "github",
1118
+ options,
1119
+ authorizationEndpoint: "https://github.com/login/oauth/authorize",
1120
+ scopes: _scopes,
1121
+ state,
1122
+ redirectURI,
1123
+ loginHint,
1124
+ prompt: options.prompt
1125
+ });
1126
+ },
1127
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1128
+ return validateAuthorizationCode({
1129
+ code,
1130
+ redirectURI,
1131
+ options,
1132
+ tokenEndpoint
1133
+ });
1134
+ },
1135
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1136
+ return refreshAccessToken({
1137
+ refreshToken,
1138
+ options: {
1139
+ clientId: options.clientId,
1140
+ clientKey: options.clientKey,
1141
+ clientSecret: options.clientSecret
1142
+ },
1143
+ tokenEndpoint: "https://github.com/login/oauth/access_token"
1144
+ });
1145
+ },
1146
+ async getUserInfo(token) {
1147
+ if (options.getUserInfo) return options.getUserInfo(token);
1148
+ const { data: profile, error } = await betterFetch("https://api.github.com/user", { headers: {
1149
+ "User-Agent": "better-auth",
1150
+ authorization: `Bearer ${token.accessToken}`
1151
+ } });
1152
+ if (error) return null;
1153
+ const { data: emails } = await betterFetch("https://api.github.com/user/emails", { headers: {
1154
+ Authorization: `Bearer ${token.accessToken}`,
1155
+ "User-Agent": "better-auth"
1156
+ } });
1157
+ if (!profile.email && emails) profile.email = (emails.find((e) => e.primary) ?? emails[0])?.email;
1158
+ const emailVerified = emails?.find((e) => e.email === profile.email)?.verified ?? false;
1159
+ const userMap = await options.mapProfileToUser?.(profile);
1160
+ return {
1161
+ user: {
1162
+ id: profile.id,
1163
+ name: profile.name || profile.login,
1164
+ email: profile.email,
1165
+ image: profile.avatar_url,
1166
+ emailVerified,
1167
+ ...userMap
1168
+ },
1169
+ data: profile
1170
+ };
1171
+ },
1172
+ options
1173
+ };
1174
+ };
1175
+ const cleanDoubleSlashes = (input = "") => {
1176
+ return input.split("://").map((str) => str.replace(/\/{2,}/g, "/")).join("://");
1177
+ };
1178
+ const issuerToEndpoints = (issuer) => {
1179
+ let baseUrl = issuer || "https://gitlab.com";
1180
+ return {
1181
+ authorizationEndpoint: cleanDoubleSlashes(`${baseUrl}/oauth/authorize`),
1182
+ tokenEndpoint: cleanDoubleSlashes(`${baseUrl}/oauth/token`),
1183
+ userinfoEndpoint: cleanDoubleSlashes(`${baseUrl}/api/v4/user`)
1184
+ };
1185
+ };
1186
+ const gitlab = (options) => {
1187
+ const { authorizationEndpoint, tokenEndpoint, userinfoEndpoint } = issuerToEndpoints(options.issuer);
1188
+ const issuerId = "gitlab";
1189
+ return {
1190
+ id: issuerId,
1191
+ name: "Gitlab",
1192
+ createAuthorizationURL: async ({ state, scopes, codeVerifier, loginHint, redirectURI }) => {
1193
+ const _scopes = options.disableDefaultScope ? [] : ["read_user"];
1194
+ if (options.scope) _scopes.push(...options.scope);
1195
+ if (scopes) _scopes.push(...scopes);
1196
+ return await createAuthorizationURL({
1197
+ id: issuerId,
1198
+ options,
1199
+ authorizationEndpoint,
1200
+ scopes: _scopes,
1201
+ state,
1202
+ redirectURI,
1203
+ codeVerifier,
1204
+ loginHint
1205
+ });
1206
+ },
1207
+ validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
1208
+ return validateAuthorizationCode({
1209
+ code,
1210
+ redirectURI,
1211
+ options,
1212
+ codeVerifier,
1213
+ tokenEndpoint
1214
+ });
1215
+ },
1216
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1217
+ return refreshAccessToken({
1218
+ refreshToken,
1219
+ options: {
1220
+ clientId: options.clientId,
1221
+ clientKey: options.clientKey,
1222
+ clientSecret: options.clientSecret
1223
+ },
1224
+ tokenEndpoint
1225
+ });
1226
+ },
1227
+ async getUserInfo(token) {
1228
+ if (options.getUserInfo) return options.getUserInfo(token);
1229
+ const { data: profile, error } = await betterFetch(userinfoEndpoint, { headers: { authorization: `Bearer ${token.accessToken}` } });
1230
+ if (error || profile.state !== "active" || profile.locked) return null;
1231
+ const userMap = await options.mapProfileToUser?.(profile);
1232
+ return {
1233
+ user: {
1234
+ id: profile.id,
1235
+ name: profile.name ?? profile.username,
1236
+ email: profile.email,
1237
+ image: profile.avatar_url,
1238
+ emailVerified: profile.email_verified ?? false,
1239
+ ...userMap
1240
+ },
1241
+ data: profile
1242
+ };
1243
+ },
1244
+ options
1245
+ };
1246
+ };
1247
+ const google = (options) => {
1248
+ return {
1249
+ id: "google",
1250
+ name: "Google",
1251
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, display }) {
1252
+ if (!options.clientId || !options.clientSecret) {
1253
+ logger.error("Client Id and Client Secret is required for Google. Make sure to provide them in the options.");
1254
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
1255
+ }
1256
+ if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Google");
1257
+ const _scopes = options.disableDefaultScope ? [] : [
1258
+ "email",
1259
+ "profile",
1260
+ "openid"
1261
+ ];
1262
+ if (options.scope) _scopes.push(...options.scope);
1263
+ if (scopes) _scopes.push(...scopes);
1264
+ return await createAuthorizationURL({
1265
+ id: "google",
1266
+ options,
1267
+ authorizationEndpoint: "https://accounts.google.com/o/oauth2/auth",
1268
+ scopes: _scopes,
1269
+ state,
1270
+ codeVerifier,
1271
+ redirectURI,
1272
+ prompt: options.prompt,
1273
+ accessType: options.accessType,
1274
+ display: display || options.display,
1275
+ loginHint,
1276
+ hd: options.hd,
1277
+ additionalParams: { include_granted_scopes: "true" }
1278
+ });
1279
+ },
1280
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1281
+ return validateAuthorizationCode({
1282
+ code,
1283
+ codeVerifier,
1284
+ redirectURI,
1285
+ options,
1286
+ tokenEndpoint: "https://oauth2.googleapis.com/token"
1287
+ });
1288
+ },
1289
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1290
+ return refreshAccessToken({
1291
+ refreshToken,
1292
+ options: {
1293
+ clientId: options.clientId,
1294
+ clientKey: options.clientKey,
1295
+ clientSecret: options.clientSecret
1296
+ },
1297
+ tokenEndpoint: "https://www.googleapis.com/oauth2/v4/token"
1298
+ });
1299
+ },
1300
+ async verifyIdToken(token, nonce) {
1301
+ if (options.disableIdTokenSignIn) return false;
1302
+ if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
1303
+ const { data: tokenInfo } = await betterFetch(`https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token}`);
1304
+ if (!tokenInfo) return false;
1305
+ return tokenInfo.aud === options.clientId && (tokenInfo.iss === "https://accounts.google.com" || tokenInfo.iss === "accounts.google.com");
1306
+ },
1307
+ async getUserInfo(token) {
1308
+ if (options.getUserInfo) return options.getUserInfo(token);
1309
+ if (!token.idToken) return null;
1310
+ const user = decodeJwt(token.idToken);
1311
+ const userMap = await options.mapProfileToUser?.(user);
1312
+ return {
1313
+ user: {
1314
+ id: user.sub,
1315
+ name: user.name,
1316
+ email: user.email,
1317
+ image: user.picture,
1318
+ emailVerified: user.email_verified,
1319
+ ...userMap
1320
+ },
1321
+ data: user
1322
+ };
1323
+ },
1324
+ options
1325
+ };
1326
+ };
1327
+ const huggingface = (options) => {
1328
+ return {
1329
+ id: "huggingface",
1330
+ name: "Hugging Face",
1331
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
1332
+ const _scopes = options.disableDefaultScope ? [] : [
1333
+ "openid",
1334
+ "profile",
1335
+ "email"
1336
+ ];
1337
+ if (options.scope) _scopes.push(...options.scope);
1338
+ if (scopes) _scopes.push(...scopes);
1339
+ return createAuthorizationURL({
1340
+ id: "huggingface",
1341
+ options,
1342
+ authorizationEndpoint: "https://huggingface.co/oauth/authorize",
1343
+ scopes: _scopes,
1344
+ state,
1345
+ codeVerifier,
1346
+ redirectURI
1347
+ });
1348
+ },
1349
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1350
+ return validateAuthorizationCode({
1351
+ code,
1352
+ codeVerifier,
1353
+ redirectURI,
1354
+ options,
1355
+ tokenEndpoint: "https://huggingface.co/oauth/token"
1356
+ });
1357
+ },
1358
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1359
+ return refreshAccessToken({
1360
+ refreshToken,
1361
+ options: {
1362
+ clientId: options.clientId,
1363
+ clientKey: options.clientKey,
1364
+ clientSecret: options.clientSecret
1365
+ },
1366
+ tokenEndpoint: "https://huggingface.co/oauth/token"
1367
+ });
1368
+ },
1369
+ async getUserInfo(token) {
1370
+ if (options.getUserInfo) return options.getUserInfo(token);
1371
+ const { data: profile, error } = await betterFetch("https://huggingface.co/oauth/userinfo", {
1372
+ method: "GET",
1373
+ headers: { Authorization: `Bearer ${token.accessToken}` }
1374
+ });
1375
+ if (error) return null;
1376
+ const userMap = await options.mapProfileToUser?.(profile);
1377
+ return {
1378
+ user: {
1379
+ id: profile.sub,
1380
+ name: profile.name || profile.preferred_username,
1381
+ email: profile.email,
1382
+ image: profile.picture,
1383
+ emailVerified: profile.email_verified ?? false,
1384
+ ...userMap
1385
+ },
1386
+ data: profile
1387
+ };
1388
+ },
1389
+ options
1390
+ };
1391
+ };
1392
+ const kakao = (options) => {
1393
+ return {
1394
+ id: "kakao",
1395
+ name: "Kakao",
1396
+ createAuthorizationURL({ state, scopes, redirectURI }) {
1397
+ const _scopes = options.disableDefaultScope ? [] : [
1398
+ "account_email",
1399
+ "profile_image",
1400
+ "profile_nickname"
1401
+ ];
1402
+ if (options.scope) _scopes.push(...options.scope);
1403
+ if (scopes) _scopes.push(...scopes);
1404
+ return createAuthorizationURL({
1405
+ id: "kakao",
1406
+ options,
1407
+ authorizationEndpoint: "https://kauth.kakao.com/oauth/authorize",
1408
+ scopes: _scopes,
1409
+ state,
1410
+ redirectURI
1411
+ });
1412
+ },
1413
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1414
+ return validateAuthorizationCode({
1415
+ code,
1416
+ redirectURI,
1417
+ options,
1418
+ tokenEndpoint: "https://kauth.kakao.com/oauth/token"
1419
+ });
1420
+ },
1421
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1422
+ return refreshAccessToken({
1423
+ refreshToken,
1424
+ options: {
1425
+ clientId: options.clientId,
1426
+ clientKey: options.clientKey,
1427
+ clientSecret: options.clientSecret
1428
+ },
1429
+ tokenEndpoint: "https://kauth.kakao.com/oauth/token"
1430
+ });
1431
+ },
1432
+ async getUserInfo(token) {
1433
+ if (options.getUserInfo) return options.getUserInfo(token);
1434
+ const { data: profile, error } = await betterFetch("https://kapi.kakao.com/v2/user/me", { headers: { Authorization: `Bearer ${token.accessToken}` } });
1435
+ if (error || !profile) return null;
1436
+ const userMap = await options.mapProfileToUser?.(profile);
1437
+ const account = profile.kakao_account || {};
1438
+ const kakaoProfile = account.profile || {};
1439
+ return {
1440
+ user: {
1441
+ id: String(profile.id),
1442
+ name: kakaoProfile.nickname || account.name || void 0,
1443
+ email: account.email,
1444
+ image: kakaoProfile.profile_image_url || kakaoProfile.thumbnail_image_url,
1445
+ emailVerified: !!account.is_email_valid && !!account.is_email_verified,
1446
+ ...userMap
1447
+ },
1448
+ data: profile
1449
+ };
1450
+ },
1451
+ options
1452
+ };
1453
+ };
1454
+ const kick = (options) => {
1455
+ return {
1456
+ id: "kick",
1457
+ name: "Kick",
1458
+ createAuthorizationURL({ state, scopes, redirectURI, codeVerifier }) {
1459
+ const _scopes = options.disableDefaultScope ? [] : ["user:read"];
1460
+ if (options.scope) _scopes.push(...options.scope);
1461
+ if (scopes) _scopes.push(...scopes);
1462
+ return createAuthorizationURL({
1463
+ id: "kick",
1464
+ redirectURI,
1465
+ options,
1466
+ authorizationEndpoint: "https://id.kick.com/oauth/authorize",
1467
+ scopes: _scopes,
1468
+ codeVerifier,
1469
+ state
1470
+ });
1471
+ },
1472
+ async validateAuthorizationCode({ code, redirectURI, codeVerifier }) {
1473
+ return validateAuthorizationCode({
1474
+ code,
1475
+ redirectURI,
1476
+ options,
1477
+ tokenEndpoint: "https://id.kick.com/oauth/token",
1478
+ codeVerifier
1479
+ });
1480
+ },
1481
+ async getUserInfo(token) {
1482
+ if (options.getUserInfo) return options.getUserInfo(token);
1483
+ const { data, error } = await betterFetch("https://api.kick.com/public/v1/users", {
1484
+ method: "GET",
1485
+ headers: { Authorization: `Bearer ${token.accessToken}` }
1486
+ });
1487
+ if (error) return null;
1488
+ const profile = data.data[0];
1489
+ const userMap = await options.mapProfileToUser?.(profile);
1490
+ return {
1491
+ user: {
1492
+ id: profile.user_id,
1493
+ name: profile.name,
1494
+ email: profile.email,
1495
+ image: profile.profile_picture,
1496
+ emailVerified: false,
1497
+ ...userMap
1498
+ },
1499
+ data: profile
1500
+ };
1501
+ },
1502
+ options
1503
+ };
1504
+ };
1505
+ /**
1506
+ * LINE Login v2.1
1507
+ * - Authorization endpoint: https://access.line.me/oauth2/v2.1/authorize
1508
+ * - Token endpoint: https://api.line.me/oauth2/v2.1/token
1509
+ * - UserInfo endpoint: https://api.line.me/oauth2/v2.1/userinfo
1510
+ * - Verify ID token: https://api.line.me/oauth2/v2.1/verify
1511
+ *
1512
+ * Docs: https://developers.line.biz/en/reference/line-login/#issue-access-token
1513
+ */
1514
+ const line = (options) => {
1515
+ const authorizationEndpoint = "https://access.line.me/oauth2/v2.1/authorize";
1516
+ const tokenEndpoint = "https://api.line.me/oauth2/v2.1/token";
1517
+ const userInfoEndpoint = "https://api.line.me/oauth2/v2.1/userinfo";
1518
+ const verifyIdTokenEndpoint = "https://api.line.me/oauth2/v2.1/verify";
1519
+ return {
1520
+ id: "line",
1521
+ name: "LINE",
1522
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint }) {
1523
+ const _scopes = options.disableDefaultScope ? [] : [
1524
+ "openid",
1525
+ "profile",
1526
+ "email"
1527
+ ];
1528
+ if (options.scope) _scopes.push(...options.scope);
1529
+ if (scopes) _scopes.push(...scopes);
1530
+ return await createAuthorizationURL({
1531
+ id: "line",
1532
+ options,
1533
+ authorizationEndpoint,
1534
+ scopes: _scopes,
1535
+ state,
1536
+ codeVerifier,
1537
+ redirectURI,
1538
+ loginHint
1539
+ });
1540
+ },
1541
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1542
+ return validateAuthorizationCode({
1543
+ code,
1544
+ codeVerifier,
1545
+ redirectURI,
1546
+ options,
1547
+ tokenEndpoint
1548
+ });
1549
+ },
1550
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1551
+ return refreshAccessToken({
1552
+ refreshToken,
1553
+ options: {
1554
+ clientId: options.clientId,
1555
+ clientSecret: options.clientSecret
1556
+ },
1557
+ tokenEndpoint
1558
+ });
1559
+ },
1560
+ async verifyIdToken(token, nonce) {
1561
+ if (options.disableIdTokenSignIn) return false;
1562
+ if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
1563
+ const body = new URLSearchParams();
1564
+ body.set("id_token", token);
1565
+ body.set("client_id", options.clientId);
1566
+ if (nonce) body.set("nonce", nonce);
1567
+ const { data, error } = await betterFetch(verifyIdTokenEndpoint, {
1568
+ method: "POST",
1569
+ headers: { "content-type": "application/x-www-form-urlencoded" },
1570
+ body
1571
+ });
1572
+ if (error || !data) return false;
1573
+ if (data.aud !== options.clientId) return false;
1574
+ if (nonce && data.nonce && data.nonce !== nonce) return false;
1575
+ return true;
1576
+ },
1577
+ async getUserInfo(token) {
1578
+ if (options.getUserInfo) return options.getUserInfo(token);
1579
+ let profile = null;
1580
+ if (token.idToken) try {
1581
+ profile = decodeJwt(token.idToken);
1582
+ } catch {}
1583
+ if (!profile) {
1584
+ const { data } = await betterFetch(userInfoEndpoint, { headers: { authorization: `Bearer ${token.accessToken}` } });
1585
+ profile = data || null;
1586
+ }
1587
+ if (!profile) return null;
1588
+ const userMap = await options.mapProfileToUser?.(profile);
1589
+ const id = profile.sub || profile.userId;
1590
+ const name = profile.name || profile.displayName;
1591
+ const image = profile.picture || profile.pictureUrl || void 0;
1592
+ return {
1593
+ user: {
1594
+ id,
1595
+ name,
1596
+ email: profile.email,
1597
+ image,
1598
+ emailVerified: false,
1599
+ ...userMap
1600
+ },
1601
+ data: profile
1602
+ };
1603
+ },
1604
+ options
1605
+ };
1606
+ };
1607
+ const linear = (options) => {
1608
+ const tokenEndpoint = "https://api.linear.app/oauth/token";
1609
+ return {
1610
+ id: "linear",
1611
+ name: "Linear",
1612
+ createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
1613
+ const _scopes = options.disableDefaultScope ? [] : ["read"];
1614
+ if (options.scope) _scopes.push(...options.scope);
1615
+ if (scopes) _scopes.push(...scopes);
1616
+ return createAuthorizationURL({
1617
+ id: "linear",
1618
+ options,
1619
+ authorizationEndpoint: "https://linear.app/oauth/authorize",
1620
+ scopes: _scopes,
1621
+ state,
1622
+ redirectURI,
1623
+ loginHint
1624
+ });
1625
+ },
1626
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1627
+ return validateAuthorizationCode({
1628
+ code,
1629
+ redirectURI,
1630
+ options,
1631
+ tokenEndpoint
1632
+ });
1633
+ },
1634
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1635
+ return refreshAccessToken({
1636
+ refreshToken,
1637
+ options: {
1638
+ clientId: options.clientId,
1639
+ clientKey: options.clientKey,
1640
+ clientSecret: options.clientSecret
1641
+ },
1642
+ tokenEndpoint
1643
+ });
1644
+ },
1645
+ async getUserInfo(token) {
1646
+ if (options.getUserInfo) return options.getUserInfo(token);
1647
+ const { data: profile, error } = await betterFetch("https://api.linear.app/graphql", {
1648
+ method: "POST",
1649
+ headers: {
1650
+ "Content-Type": "application/json",
1651
+ Authorization: `Bearer ${token.accessToken}`
1652
+ },
1653
+ body: JSON.stringify({ query: `
1654
+ query {
1655
+ viewer {
1656
+ id
1657
+ name
1658
+ email
1659
+ avatarUrl
1660
+ active
1661
+ createdAt
1662
+ updatedAt
1663
+ }
1664
+ }
1665
+ ` })
1666
+ });
1667
+ if (error || !profile?.data?.viewer) return null;
1668
+ const userData = profile.data.viewer;
1669
+ const userMap = await options.mapProfileToUser?.(userData);
1670
+ return {
1671
+ user: {
1672
+ id: profile.data.viewer.id,
1673
+ name: profile.data.viewer.name,
1674
+ email: profile.data.viewer.email,
1675
+ image: profile.data.viewer.avatarUrl,
1676
+ emailVerified: false,
1677
+ ...userMap
1678
+ },
1679
+ data: userData
1680
+ };
1681
+ },
1682
+ options
1683
+ };
1684
+ };
1685
+ const linkedin = (options) => {
1686
+ const authorizationEndpoint = "https://www.linkedin.com/oauth/v2/authorization";
1687
+ const tokenEndpoint = "https://www.linkedin.com/oauth/v2/accessToken";
1688
+ return {
1689
+ id: "linkedin",
1690
+ name: "Linkedin",
1691
+ createAuthorizationURL: async ({ state, scopes, redirectURI, loginHint }) => {
1692
+ const _scopes = options.disableDefaultScope ? [] : [
1693
+ "profile",
1694
+ "email",
1695
+ "openid"
1696
+ ];
1697
+ if (options.scope) _scopes.push(...options.scope);
1698
+ if (scopes) _scopes.push(...scopes);
1699
+ return await createAuthorizationURL({
1700
+ id: "linkedin",
1701
+ options,
1702
+ authorizationEndpoint,
1703
+ scopes: _scopes,
1704
+ state,
1705
+ loginHint,
1706
+ redirectURI
1707
+ });
1708
+ },
1709
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1710
+ return await validateAuthorizationCode({
1711
+ code,
1712
+ redirectURI,
1713
+ options,
1714
+ tokenEndpoint
1715
+ });
1716
+ },
1717
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1718
+ return refreshAccessToken({
1719
+ refreshToken,
1720
+ options: {
1721
+ clientId: options.clientId,
1722
+ clientKey: options.clientKey,
1723
+ clientSecret: options.clientSecret
1724
+ },
1725
+ tokenEndpoint
1726
+ });
1727
+ },
1728
+ async getUserInfo(token) {
1729
+ if (options.getUserInfo) return options.getUserInfo(token);
1730
+ const { data: profile, error } = await betterFetch("https://api.linkedin.com/v2/userinfo", {
1731
+ method: "GET",
1732
+ headers: { Authorization: `Bearer ${token.accessToken}` }
1733
+ });
1734
+ if (error) return null;
1735
+ const userMap = await options.mapProfileToUser?.(profile);
1736
+ return {
1737
+ user: {
1738
+ id: profile.sub,
1739
+ name: profile.name,
1740
+ email: profile.email,
1741
+ emailVerified: profile.email_verified || false,
1742
+ image: profile.picture,
1743
+ ...userMap
1744
+ },
1745
+ data: profile
1746
+ };
1747
+ },
1748
+ options
1749
+ };
1750
+ };
1751
+ const microsoft = (options) => {
1752
+ const tenant = options.tenantId || "common";
1753
+ const authority = options.authority || "https://login.microsoftonline.com";
1754
+ const authorizationEndpoint = `${authority}/${tenant}/oauth2/v2.0/authorize`;
1755
+ const tokenEndpoint = `${authority}/${tenant}/oauth2/v2.0/token`;
1756
+ return {
1757
+ id: "microsoft",
1758
+ name: "Microsoft EntraID",
1759
+ createAuthorizationURL(data) {
1760
+ const scopes = options.disableDefaultScope ? [] : [
1761
+ "openid",
1762
+ "profile",
1763
+ "email",
1764
+ "User.Read",
1765
+ "offline_access"
1766
+ ];
1767
+ if (options.scope) scopes.push(...options.scope);
1768
+ if (data.scopes) scopes.push(...data.scopes);
1769
+ return createAuthorizationURL({
1770
+ id: "microsoft",
1771
+ options,
1772
+ authorizationEndpoint,
1773
+ state: data.state,
1774
+ codeVerifier: data.codeVerifier,
1775
+ scopes,
1776
+ redirectURI: data.redirectURI,
1777
+ prompt: options.prompt,
1778
+ loginHint: data.loginHint
1779
+ });
1780
+ },
1781
+ validateAuthorizationCode({ code, codeVerifier, redirectURI }) {
1782
+ return validateAuthorizationCode({
1783
+ code,
1784
+ codeVerifier,
1785
+ redirectURI,
1786
+ options,
1787
+ tokenEndpoint
1788
+ });
1789
+ },
1790
+ async getUserInfo(token) {
1791
+ if (options.getUserInfo) return options.getUserInfo(token);
1792
+ if (!token.idToken) return null;
1793
+ const user = decodeJwt(token.idToken);
1794
+ const profilePhotoSize = options.profilePhotoSize || 48;
1795
+ await betterFetch(`https://graph.microsoft.com/v1.0/me/photos/${profilePhotoSize}x${profilePhotoSize}/$value`, {
1796
+ headers: { Authorization: `Bearer ${token.accessToken}` },
1797
+ async onResponse(context) {
1798
+ if (options.disableProfilePhoto || !context.response.ok) return;
1799
+ try {
1800
+ const pictureBuffer = await context.response.clone().arrayBuffer();
1801
+ user.picture = `data:image/jpeg;base64, ${base64.encode(pictureBuffer)}`;
1802
+ } catch (e) {
1803
+ logger.error(e && typeof e === "object" && "name" in e ? e.name : "", e);
1804
+ }
1805
+ }
1806
+ });
1807
+ const userMap = await options.mapProfileToUser?.(user);
1808
+ const emailVerified = user.email_verified !== void 0 ? user.email_verified : user.email && (user.verified_primary_email?.includes(user.email) || user.verified_secondary_email?.includes(user.email)) ? true : false;
1809
+ return {
1810
+ user: {
1811
+ id: user.sub,
1812
+ name: user.name,
1813
+ email: user.email,
1814
+ image: user.picture,
1815
+ emailVerified,
1816
+ ...userMap
1817
+ },
1818
+ data: user
1819
+ };
1820
+ },
1821
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1822
+ const scopes = options.disableDefaultScope ? [] : [
1823
+ "openid",
1824
+ "profile",
1825
+ "email",
1826
+ "User.Read",
1827
+ "offline_access"
1828
+ ];
1829
+ if (options.scope) scopes.push(...options.scope);
1830
+ return refreshAccessToken({
1831
+ refreshToken,
1832
+ options: {
1833
+ clientId: options.clientId,
1834
+ clientSecret: options.clientSecret
1835
+ },
1836
+ extraParams: { scope: scopes.join(" ") },
1837
+ tokenEndpoint
1838
+ });
1839
+ },
1840
+ options
1841
+ };
1842
+ };
1843
+ const naver = (options) => {
1844
+ return {
1845
+ id: "naver",
1846
+ name: "Naver",
1847
+ createAuthorizationURL({ state, scopes, redirectURI }) {
1848
+ const _scopes = options.disableDefaultScope ? [] : ["profile", "email"];
1849
+ if (options.scope) _scopes.push(...options.scope);
1850
+ if (scopes) _scopes.push(...scopes);
1851
+ return createAuthorizationURL({
1852
+ id: "naver",
1853
+ options,
1854
+ authorizationEndpoint: "https://nid.naver.com/oauth2.0/authorize",
1855
+ scopes: _scopes,
1856
+ state,
1857
+ redirectURI
1858
+ });
1859
+ },
1860
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1861
+ return validateAuthorizationCode({
1862
+ code,
1863
+ redirectURI,
1864
+ options,
1865
+ tokenEndpoint: "https://nid.naver.com/oauth2.0/token"
1866
+ });
1867
+ },
1868
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1869
+ return refreshAccessToken({
1870
+ refreshToken,
1871
+ options: {
1872
+ clientId: options.clientId,
1873
+ clientKey: options.clientKey,
1874
+ clientSecret: options.clientSecret
1875
+ },
1876
+ tokenEndpoint: "https://nid.naver.com/oauth2.0/token"
1877
+ });
1878
+ },
1879
+ async getUserInfo(token) {
1880
+ if (options.getUserInfo) return options.getUserInfo(token);
1881
+ const { data: profile, error } = await betterFetch("https://openapi.naver.com/v1/nid/me", { headers: { Authorization: `Bearer ${token.accessToken}` } });
1882
+ if (error || !profile || profile.resultcode !== "00") return null;
1883
+ const userMap = await options.mapProfileToUser?.(profile);
1884
+ const res = profile.response || {};
1885
+ return {
1886
+ user: {
1887
+ id: res.id,
1888
+ name: res.name || res.nickname,
1889
+ email: res.email,
1890
+ image: res.profile_image,
1891
+ emailVerified: false,
1892
+ ...userMap
1893
+ },
1894
+ data: profile
1895
+ };
1896
+ },
1897
+ options
1898
+ };
1899
+ };
1900
+ const notion = (options) => {
1901
+ const tokenEndpoint = "https://api.notion.com/v1/oauth/token";
1902
+ return {
1903
+ id: "notion",
1904
+ name: "Notion",
1905
+ createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
1906
+ const _scopes = options.disableDefaultScope ? [] : [];
1907
+ if (options.scope) _scopes.push(...options.scope);
1908
+ if (scopes) _scopes.push(...scopes);
1909
+ return createAuthorizationURL({
1910
+ id: "notion",
1911
+ options,
1912
+ authorizationEndpoint: "https://api.notion.com/v1/oauth/authorize",
1913
+ scopes: _scopes,
1914
+ state,
1915
+ redirectURI,
1916
+ loginHint,
1917
+ additionalParams: { owner: "user" }
1918
+ });
1919
+ },
1920
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1921
+ return validateAuthorizationCode({
1922
+ code,
1923
+ redirectURI,
1924
+ options,
1925
+ tokenEndpoint,
1926
+ authentication: "basic"
1927
+ });
1928
+ },
1929
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1930
+ return refreshAccessToken({
1931
+ refreshToken,
1932
+ options: {
1933
+ clientId: options.clientId,
1934
+ clientKey: options.clientKey,
1935
+ clientSecret: options.clientSecret
1936
+ },
1937
+ tokenEndpoint
1938
+ });
1939
+ },
1940
+ async getUserInfo(token) {
1941
+ if (options.getUserInfo) return options.getUserInfo(token);
1942
+ const { data: profile, error } = await betterFetch("https://api.notion.com/v1/users/me", { headers: {
1943
+ Authorization: `Bearer ${token.accessToken}`,
1944
+ "Notion-Version": "2022-06-28"
1945
+ } });
1946
+ if (error || !profile) return null;
1947
+ const userProfile = profile.bot?.owner?.user;
1948
+ if (!userProfile) return null;
1949
+ const userMap = await options.mapProfileToUser?.(userProfile);
1950
+ return {
1951
+ user: {
1952
+ id: userProfile.id,
1953
+ name: userProfile.name || "Notion User",
1954
+ email: userProfile.person?.email || null,
1955
+ image: userProfile.avatar_url,
1956
+ emailVerified: !!userProfile.person?.email,
1957
+ ...userMap
1958
+ },
1959
+ data: userProfile
1960
+ };
1961
+ },
1962
+ options
1963
+ };
1964
+ };
1965
+ const paybin = (options) => {
1966
+ const issuer = options.issuer || "https://idp.paybin.io";
1967
+ const authorizationEndpoint = `${issuer}/oauth2/authorize`;
1968
+ const tokenEndpoint = `${issuer}/oauth2/token`;
1969
+ return {
1970
+ id: "paybin",
1971
+ name: "Paybin",
1972
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint }) {
1973
+ if (!options.clientId || !options.clientSecret) {
1974
+ logger.error("Client Id and Client Secret is required for Paybin. Make sure to provide them in the options.");
1975
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
1976
+ }
1977
+ if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Paybin");
1978
+ const _scopes = options.disableDefaultScope ? [] : [
1979
+ "openid",
1980
+ "email",
1981
+ "profile"
1982
+ ];
1983
+ if (options.scope) _scopes.push(...options.scope);
1984
+ if (scopes) _scopes.push(...scopes);
1985
+ return await createAuthorizationURL({
1986
+ id: "paybin",
1987
+ options,
1988
+ authorizationEndpoint,
1989
+ scopes: _scopes,
1990
+ state,
1991
+ codeVerifier,
1992
+ redirectURI,
1993
+ prompt: options.prompt,
1994
+ loginHint
1995
+ });
1996
+ },
1997
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1998
+ return validateAuthorizationCode({
1999
+ code,
2000
+ codeVerifier,
2001
+ redirectURI,
2002
+ options,
2003
+ tokenEndpoint
2004
+ });
2005
+ },
2006
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2007
+ return refreshAccessToken({
2008
+ refreshToken,
2009
+ options: {
2010
+ clientId: options.clientId,
2011
+ clientKey: options.clientKey,
2012
+ clientSecret: options.clientSecret
2013
+ },
2014
+ tokenEndpoint
2015
+ });
2016
+ },
2017
+ async getUserInfo(token) {
2018
+ if (options.getUserInfo) return options.getUserInfo(token);
2019
+ if (!token.idToken) return null;
2020
+ const user = decodeJwt(token.idToken);
2021
+ const userMap = await options.mapProfileToUser?.(user);
2022
+ return {
2023
+ user: {
2024
+ id: user.sub,
2025
+ name: user.name || user.preferred_username || (user.email ? user.email.split("@")[0] : "User") || "User",
2026
+ email: user.email,
2027
+ image: user.picture,
2028
+ emailVerified: user.email_verified || false,
2029
+ ...userMap
2030
+ },
2031
+ data: user
2032
+ };
2033
+ },
2034
+ options
2035
+ };
2036
+ };
2037
+ const paypal = (options) => {
2038
+ const isSandbox = (options.environment || "sandbox") === "sandbox";
2039
+ const authorizationEndpoint = isSandbox ? "https://www.sandbox.paypal.com/signin/authorize" : "https://www.paypal.com/signin/authorize";
2040
+ const tokenEndpoint = isSandbox ? "https://api-m.sandbox.paypal.com/v1/oauth2/token" : "https://api-m.paypal.com/v1/oauth2/token";
2041
+ const userInfoEndpoint = isSandbox ? "https://api-m.sandbox.paypal.com/v1/identity/oauth2/userinfo" : "https://api-m.paypal.com/v1/identity/oauth2/userinfo";
2042
+ return {
2043
+ id: "paypal",
2044
+ name: "PayPal",
2045
+ async createAuthorizationURL({ state, codeVerifier, redirectURI }) {
2046
+ if (!options.clientId || !options.clientSecret) {
2047
+ logger.error("Client Id and Client Secret is required for PayPal. Make sure to provide them in the options.");
2048
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
2049
+ }
2050
+ return await createAuthorizationURL({
2051
+ id: "paypal",
2052
+ options,
2053
+ authorizationEndpoint,
2054
+ scopes: [],
2055
+ state,
2056
+ codeVerifier,
2057
+ redirectURI,
2058
+ prompt: options.prompt
2059
+ });
2060
+ },
2061
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2062
+ /**
2063
+ * PayPal requires Basic Auth for token exchange
2064
+ **/
2065
+ const credentials = base64.encode(`${options.clientId}:${options.clientSecret}`);
2066
+ try {
2067
+ const response = await betterFetch(tokenEndpoint, {
2068
+ method: "POST",
2069
+ headers: {
2070
+ Authorization: `Basic ${credentials}`,
2071
+ Accept: "application/json",
2072
+ "Accept-Language": "en_US",
2073
+ "Content-Type": "application/x-www-form-urlencoded"
2074
+ },
2075
+ body: new URLSearchParams({
2076
+ grant_type: "authorization_code",
2077
+ code,
2078
+ redirect_uri: redirectURI
2079
+ }).toString()
2080
+ });
2081
+ if (!response.data) throw new BetterAuthError("FAILED_TO_GET_ACCESS_TOKEN");
2082
+ const data = response.data;
2083
+ return {
2084
+ accessToken: data.access_token,
2085
+ refreshToken: data.refresh_token,
2086
+ accessTokenExpiresAt: data.expires_in ? new Date(Date.now() + data.expires_in * 1e3) : void 0,
2087
+ idToken: data.id_token
2088
+ };
2089
+ } catch (error) {
2090
+ logger.error("PayPal token exchange failed:", error);
2091
+ throw new BetterAuthError("FAILED_TO_GET_ACCESS_TOKEN");
2092
+ }
2093
+ },
2094
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2095
+ const credentials = base64.encode(`${options.clientId}:${options.clientSecret}`);
2096
+ try {
2097
+ const response = await betterFetch(tokenEndpoint, {
2098
+ method: "POST",
2099
+ headers: {
2100
+ Authorization: `Basic ${credentials}`,
2101
+ Accept: "application/json",
2102
+ "Accept-Language": "en_US",
2103
+ "Content-Type": "application/x-www-form-urlencoded"
2104
+ },
2105
+ body: new URLSearchParams({
2106
+ grant_type: "refresh_token",
2107
+ refresh_token: refreshToken
2108
+ }).toString()
2109
+ });
2110
+ if (!response.data) throw new BetterAuthError("FAILED_TO_REFRESH_ACCESS_TOKEN");
2111
+ const data = response.data;
2112
+ return {
2113
+ accessToken: data.access_token,
2114
+ refreshToken: data.refresh_token,
2115
+ accessTokenExpiresAt: data.expires_in ? new Date(Date.now() + data.expires_in * 1e3) : void 0
2116
+ };
2117
+ } catch (error) {
2118
+ logger.error("PayPal token refresh failed:", error);
2119
+ throw new BetterAuthError("FAILED_TO_REFRESH_ACCESS_TOKEN");
2120
+ }
2121
+ },
2122
+ async verifyIdToken(token, nonce) {
2123
+ if (options.disableIdTokenSignIn) return false;
2124
+ if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
2125
+ try {
2126
+ return !!decodeJwt(token).sub;
2127
+ } catch (error) {
2128
+ logger.error("Failed to verify PayPal ID token:", error);
2129
+ return false;
2130
+ }
2131
+ },
2132
+ async getUserInfo(token) {
2133
+ if (options.getUserInfo) return options.getUserInfo(token);
2134
+ if (!token.accessToken) {
2135
+ logger.error("Access token is required to fetch PayPal user info");
2136
+ return null;
2137
+ }
2138
+ try {
2139
+ const response = await betterFetch(`${userInfoEndpoint}?schema=paypalv1.1`, { headers: {
2140
+ Authorization: `Bearer ${token.accessToken}`,
2141
+ Accept: "application/json"
2142
+ } });
2143
+ if (!response.data) {
2144
+ logger.error("Failed to fetch user info from PayPal");
2145
+ return null;
2146
+ }
2147
+ const userInfo = response.data;
2148
+ const userMap = await options.mapProfileToUser?.(userInfo);
2149
+ return {
2150
+ user: {
2151
+ id: userInfo.user_id,
2152
+ name: userInfo.name,
2153
+ email: userInfo.email,
2154
+ image: userInfo.picture,
2155
+ emailVerified: userInfo.email_verified,
2156
+ ...userMap
2157
+ },
2158
+ data: userInfo
2159
+ };
2160
+ } catch (error) {
2161
+ logger.error("Failed to fetch user info from PayPal:", error);
2162
+ return null;
2163
+ }
2164
+ },
2165
+ options
2166
+ };
2167
+ };
2168
+ const polar = (options) => {
2169
+ return {
2170
+ id: "polar",
2171
+ name: "Polar",
2172
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
2173
+ const _scopes = options.disableDefaultScope ? [] : [
2174
+ "openid",
2175
+ "profile",
2176
+ "email"
2177
+ ];
2178
+ if (options.scope) _scopes.push(...options.scope);
2179
+ if (scopes) _scopes.push(...scopes);
2180
+ return createAuthorizationURL({
2181
+ id: "polar",
2182
+ options,
2183
+ authorizationEndpoint: "https://polar.sh/oauth2/authorize",
2184
+ scopes: _scopes,
2185
+ state,
2186
+ codeVerifier,
2187
+ redirectURI,
2188
+ prompt: options.prompt
2189
+ });
2190
+ },
2191
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
2192
+ return validateAuthorizationCode({
2193
+ code,
2194
+ codeVerifier,
2195
+ redirectURI,
2196
+ options,
2197
+ tokenEndpoint: "https://api.polar.sh/v1/oauth2/token"
2198
+ });
2199
+ },
2200
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2201
+ return refreshAccessToken({
2202
+ refreshToken,
2203
+ options: {
2204
+ clientId: options.clientId,
2205
+ clientKey: options.clientKey,
2206
+ clientSecret: options.clientSecret
2207
+ },
2208
+ tokenEndpoint: "https://api.polar.sh/v1/oauth2/token"
2209
+ });
2210
+ },
2211
+ async getUserInfo(token) {
2212
+ if (options.getUserInfo) return options.getUserInfo(token);
2213
+ const { data: profile, error } = await betterFetch("https://api.polar.sh/v1/oauth2/userinfo", { headers: { Authorization: `Bearer ${token.accessToken}` } });
2214
+ if (error) return null;
2215
+ const userMap = await options.mapProfileToUser?.(profile);
2216
+ return {
2217
+ user: {
2218
+ id: profile.id,
2219
+ name: profile.public_name || profile.username,
2220
+ email: profile.email,
2221
+ image: profile.avatar_url,
2222
+ emailVerified: profile.email_verified ?? false,
2223
+ ...userMap
2224
+ },
2225
+ data: profile
2226
+ };
2227
+ },
2228
+ options
2229
+ };
2230
+ };
2231
+ const reddit = (options) => {
2232
+ return {
2233
+ id: "reddit",
2234
+ name: "Reddit",
2235
+ createAuthorizationURL({ state, scopes, redirectURI }) {
2236
+ const _scopes = options.disableDefaultScope ? [] : ["identity"];
2237
+ if (options.scope) _scopes.push(...options.scope);
2238
+ if (scopes) _scopes.push(...scopes);
2239
+ return createAuthorizationURL({
2240
+ id: "reddit",
2241
+ options,
2242
+ authorizationEndpoint: "https://www.reddit.com/api/v1/authorize",
2243
+ scopes: _scopes,
2244
+ state,
2245
+ redirectURI,
2246
+ duration: options.duration
2247
+ });
2248
+ },
2249
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2250
+ const body = new URLSearchParams({
2251
+ grant_type: "authorization_code",
2252
+ code,
2253
+ redirect_uri: options.redirectURI || redirectURI
2254
+ });
2255
+ const { data, error } = await betterFetch("https://www.reddit.com/api/v1/access_token", {
2256
+ method: "POST",
2257
+ headers: {
2258
+ "content-type": "application/x-www-form-urlencoded",
2259
+ accept: "text/plain",
2260
+ "user-agent": "better-auth",
2261
+ Authorization: `Basic ${base64.encode(`${options.clientId}:${options.clientSecret}`)}`
2262
+ },
2263
+ body: body.toString()
2264
+ });
2265
+ if (error) throw error;
2266
+ return getOAuth2Tokens(data);
2267
+ },
2268
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2269
+ return refreshAccessToken({
2270
+ refreshToken,
2271
+ options: {
2272
+ clientId: options.clientId,
2273
+ clientKey: options.clientKey,
2274
+ clientSecret: options.clientSecret
2275
+ },
2276
+ authentication: "basic",
2277
+ tokenEndpoint: "https://www.reddit.com/api/v1/access_token"
2278
+ });
2279
+ },
2280
+ async getUserInfo(token) {
2281
+ if (options.getUserInfo) return options.getUserInfo(token);
2282
+ const { data: profile, error } = await betterFetch("https://oauth.reddit.com/api/v1/me", { headers: {
2283
+ Authorization: `Bearer ${token.accessToken}`,
2284
+ "User-Agent": "better-auth"
2285
+ } });
2286
+ if (error) return null;
2287
+ const userMap = await options.mapProfileToUser?.(profile);
2288
+ return {
2289
+ user: {
2290
+ id: profile.id,
2291
+ name: profile.name,
2292
+ email: profile.oauth_client_id,
2293
+ emailVerified: profile.has_verified_email,
2294
+ image: profile.icon_img?.split("?")[0],
2295
+ ...userMap
2296
+ },
2297
+ data: profile
2298
+ };
2299
+ },
2300
+ options
2301
+ };
2302
+ };
2303
+ const roblox = (options) => {
2304
+ return {
2305
+ id: "roblox",
2306
+ name: "Roblox",
2307
+ createAuthorizationURL({ state, scopes, redirectURI }) {
2308
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile"];
2309
+ if (options.scope) _scopes.push(...options.scope);
2310
+ if (scopes) _scopes.push(...scopes);
2311
+ return new URL(`https://apis.roblox.com/oauth/v1/authorize?scope=${_scopes.join("+")}&response_type=code&client_id=${options.clientId}&redirect_uri=${encodeURIComponent(options.redirectURI || redirectURI)}&state=${state}&prompt=${options.prompt || "select_account consent"}`);
2312
+ },
2313
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2314
+ return validateAuthorizationCode({
2315
+ code,
2316
+ redirectURI: options.redirectURI || redirectURI,
2317
+ options,
2318
+ tokenEndpoint: "https://apis.roblox.com/oauth/v1/token",
2319
+ authentication: "post"
2320
+ });
2321
+ },
2322
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2323
+ return refreshAccessToken({
2324
+ refreshToken,
2325
+ options: {
2326
+ clientId: options.clientId,
2327
+ clientKey: options.clientKey,
2328
+ clientSecret: options.clientSecret
2329
+ },
2330
+ tokenEndpoint: "https://apis.roblox.com/oauth/v1/token"
2331
+ });
2332
+ },
2333
+ async getUserInfo(token) {
2334
+ if (options.getUserInfo) return options.getUserInfo(token);
2335
+ const { data: profile, error } = await betterFetch("https://apis.roblox.com/oauth/v1/userinfo", { headers: { authorization: `Bearer ${token.accessToken}` } });
2336
+ if (error) return null;
2337
+ const userMap = await options.mapProfileToUser?.(profile);
2338
+ return {
2339
+ user: {
2340
+ id: profile.sub,
2341
+ name: profile.nickname || profile.preferred_username || "",
2342
+ image: profile.picture,
2343
+ email: profile.preferred_username || null,
2344
+ emailVerified: false,
2345
+ ...userMap
2346
+ },
2347
+ data: { ...profile }
2348
+ };
2349
+ },
2350
+ options
2351
+ };
2352
+ };
2353
+ const salesforce = (options) => {
2354
+ const isSandbox = (options.environment ?? "production") === "sandbox";
2355
+ const authorizationEndpoint = options.loginUrl ? `https://${options.loginUrl}/services/oauth2/authorize` : isSandbox ? "https://test.salesforce.com/services/oauth2/authorize" : "https://login.salesforce.com/services/oauth2/authorize";
2356
+ const tokenEndpoint = options.loginUrl ? `https://${options.loginUrl}/services/oauth2/token` : isSandbox ? "https://test.salesforce.com/services/oauth2/token" : "https://login.salesforce.com/services/oauth2/token";
2357
+ const userInfoEndpoint = options.loginUrl ? `https://${options.loginUrl}/services/oauth2/userinfo` : isSandbox ? "https://test.salesforce.com/services/oauth2/userinfo" : "https://login.salesforce.com/services/oauth2/userinfo";
2358
+ return {
2359
+ id: "salesforce",
2360
+ name: "Salesforce",
2361
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
2362
+ if (!options.clientId || !options.clientSecret) {
2363
+ logger.error("Client Id and Client Secret are required for Salesforce. Make sure to provide them in the options.");
2364
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
2365
+ }
2366
+ if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Salesforce");
2367
+ const _scopes = options.disableDefaultScope ? [] : [
2368
+ "openid",
2369
+ "email",
2370
+ "profile"
2371
+ ];
2372
+ if (options.scope) _scopes.push(...options.scope);
2373
+ if (scopes) _scopes.push(...scopes);
2374
+ return createAuthorizationURL({
2375
+ id: "salesforce",
2376
+ options,
2377
+ authorizationEndpoint,
2378
+ scopes: _scopes,
2379
+ state,
2380
+ codeVerifier,
2381
+ redirectURI: options.redirectURI || redirectURI
2382
+ });
2383
+ },
2384
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
2385
+ return validateAuthorizationCode({
2386
+ code,
2387
+ codeVerifier,
2388
+ redirectURI: options.redirectURI || redirectURI,
2389
+ options,
2390
+ tokenEndpoint
2391
+ });
2392
+ },
2393
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2394
+ return refreshAccessToken({
2395
+ refreshToken,
2396
+ options: {
2397
+ clientId: options.clientId,
2398
+ clientSecret: options.clientSecret
2399
+ },
2400
+ tokenEndpoint
2401
+ });
2402
+ },
2403
+ async getUserInfo(token) {
2404
+ if (options.getUserInfo) return options.getUserInfo(token);
2405
+ try {
2406
+ const { data: user } = await betterFetch(userInfoEndpoint, { headers: { Authorization: `Bearer ${token.accessToken}` } });
2407
+ if (!user) {
2408
+ logger.error("Failed to fetch user info from Salesforce");
2409
+ return null;
2410
+ }
2411
+ const userMap = await options.mapProfileToUser?.(user);
2412
+ return {
2413
+ user: {
2414
+ id: user.user_id,
2415
+ name: user.name,
2416
+ email: user.email,
2417
+ image: user.photos?.picture || user.photos?.thumbnail,
2418
+ emailVerified: user.email_verified ?? false,
2419
+ ...userMap
2420
+ },
2421
+ data: user
2422
+ };
2423
+ } catch (error) {
2424
+ logger.error("Failed to fetch user info from Salesforce:", error);
2425
+ return null;
2426
+ }
2427
+ },
2428
+ options
2429
+ };
2430
+ };
2431
+ const slack = (options) => {
2432
+ return {
2433
+ id: "slack",
2434
+ name: "Slack",
2435
+ createAuthorizationURL({ state, scopes, redirectURI }) {
2436
+ const _scopes = options.disableDefaultScope ? [] : [
2437
+ "openid",
2438
+ "profile",
2439
+ "email"
2440
+ ];
2441
+ if (scopes) _scopes.push(...scopes);
2442
+ if (options.scope) _scopes.push(...options.scope);
2443
+ const url = new URL("https://slack.com/openid/connect/authorize");
2444
+ url.searchParams.set("scope", _scopes.join(" "));
2445
+ url.searchParams.set("response_type", "code");
2446
+ url.searchParams.set("client_id", options.clientId);
2447
+ url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
2448
+ url.searchParams.set("state", state);
2449
+ return url;
2450
+ },
2451
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2452
+ return validateAuthorizationCode({
2453
+ code,
2454
+ redirectURI,
2455
+ options,
2456
+ tokenEndpoint: "https://slack.com/api/openid.connect.token"
2457
+ });
2458
+ },
2459
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2460
+ return refreshAccessToken({
2461
+ refreshToken,
2462
+ options: {
2463
+ clientId: options.clientId,
2464
+ clientKey: options.clientKey,
2465
+ clientSecret: options.clientSecret
2466
+ },
2467
+ tokenEndpoint: "https://slack.com/api/openid.connect.token"
2468
+ });
2469
+ },
2470
+ async getUserInfo(token) {
2471
+ if (options.getUserInfo) return options.getUserInfo(token);
2472
+ const { data: profile, error } = await betterFetch("https://slack.com/api/openid.connect.userInfo", { headers: { authorization: `Bearer ${token.accessToken}` } });
2473
+ if (error) return null;
2474
+ const userMap = await options.mapProfileToUser?.(profile);
2475
+ return {
2476
+ user: {
2477
+ id: profile["https://slack.com/user_id"],
2478
+ name: profile.name || "",
2479
+ email: profile.email,
2480
+ emailVerified: profile.email_verified,
2481
+ image: profile.picture || profile["https://slack.com/user_image_512"],
2482
+ ...userMap
2483
+ },
2484
+ data: profile
2485
+ };
2486
+ },
2487
+ options
2488
+ };
2489
+ };
2490
+ const spotify = (options) => {
2491
+ return {
2492
+ id: "spotify",
2493
+ name: "Spotify",
2494
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
2495
+ const _scopes = options.disableDefaultScope ? [] : ["user-read-email"];
2496
+ if (options.scope) _scopes.push(...options.scope);
2497
+ if (scopes) _scopes.push(...scopes);
2498
+ return createAuthorizationURL({
2499
+ id: "spotify",
2500
+ options,
2501
+ authorizationEndpoint: "https://accounts.spotify.com/authorize",
2502
+ scopes: _scopes,
2503
+ state,
2504
+ codeVerifier,
2505
+ redirectURI
2506
+ });
2507
+ },
2508
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
2509
+ return validateAuthorizationCode({
2510
+ code,
2511
+ codeVerifier,
2512
+ redirectURI,
2513
+ options,
2514
+ tokenEndpoint: "https://accounts.spotify.com/api/token"
2515
+ });
2516
+ },
2517
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2518
+ return refreshAccessToken({
2519
+ refreshToken,
2520
+ options: {
2521
+ clientId: options.clientId,
2522
+ clientKey: options.clientKey,
2523
+ clientSecret: options.clientSecret
2524
+ },
2525
+ tokenEndpoint: "https://accounts.spotify.com/api/token"
2526
+ });
2527
+ },
2528
+ async getUserInfo(token) {
2529
+ if (options.getUserInfo) return options.getUserInfo(token);
2530
+ const { data: profile, error } = await betterFetch("https://api.spotify.com/v1/me", {
2531
+ method: "GET",
2532
+ headers: { Authorization: `Bearer ${token.accessToken}` }
2533
+ });
2534
+ if (error) return null;
2535
+ const userMap = await options.mapProfileToUser?.(profile);
2536
+ return {
2537
+ user: {
2538
+ id: profile.id,
2539
+ name: profile.display_name,
2540
+ email: profile.email,
2541
+ image: profile.images[0]?.url,
2542
+ emailVerified: false,
2543
+ ...userMap
2544
+ },
2545
+ data: profile
2546
+ };
2547
+ },
2548
+ options
2549
+ };
2550
+ };
2551
+ const tiktok = (options) => {
2552
+ return {
2553
+ id: "tiktok",
2554
+ name: "TikTok",
2555
+ createAuthorizationURL({ state, scopes, redirectURI }) {
2556
+ const _scopes = options.disableDefaultScope ? [] : ["user.info.profile"];
2557
+ if (options.scope) _scopes.push(...options.scope);
2558
+ if (scopes) _scopes.push(...scopes);
2559
+ return new URL(`https://www.tiktok.com/v2/auth/authorize?scope=${_scopes.join(",")}&response_type=code&client_key=${options.clientKey}&redirect_uri=${encodeURIComponent(options.redirectURI || redirectURI)}&state=${state}`);
2560
+ },
2561
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2562
+ return validateAuthorizationCode({
2563
+ code,
2564
+ redirectURI: options.redirectURI || redirectURI,
2565
+ options: {
2566
+ clientKey: options.clientKey,
2567
+ clientSecret: options.clientSecret
2568
+ },
2569
+ tokenEndpoint: "https://open.tiktokapis.com/v2/oauth/token/"
2570
+ });
2571
+ },
2572
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2573
+ return refreshAccessToken({
2574
+ refreshToken,
2575
+ options: { clientSecret: options.clientSecret },
2576
+ tokenEndpoint: "https://open.tiktokapis.com/v2/oauth/token/",
2577
+ authentication: "post",
2578
+ extraParams: { client_key: options.clientKey }
2579
+ });
2580
+ },
2581
+ async getUserInfo(token) {
2582
+ if (options.getUserInfo) return options.getUserInfo(token);
2583
+ const { data: profile, error } = await betterFetch(`https://open.tiktokapis.com/v2/user/info/?fields=${[
2584
+ "open_id",
2585
+ "avatar_large_url",
2586
+ "display_name",
2587
+ "username"
2588
+ ].join(",")}`, { headers: { authorization: `Bearer ${token.accessToken}` } });
2589
+ if (error) return null;
2590
+ return {
2591
+ user: {
2592
+ email: profile.data.user.email || profile.data.user.username,
2593
+ id: profile.data.user.open_id,
2594
+ name: profile.data.user.display_name || profile.data.user.username,
2595
+ image: profile.data.user.avatar_large_url,
2596
+ emailVerified: profile.data.user.email ? true : false
2597
+ },
2598
+ data: profile
2599
+ };
2600
+ },
2601
+ options
2602
+ };
2603
+ };
2604
+ const twitch = (options) => {
2605
+ return {
2606
+ id: "twitch",
2607
+ name: "Twitch",
2608
+ createAuthorizationURL({ state, scopes, redirectURI }) {
2609
+ const _scopes = options.disableDefaultScope ? [] : ["user:read:email", "openid"];
2610
+ if (options.scope) _scopes.push(...options.scope);
2611
+ if (scopes) _scopes.push(...scopes);
2612
+ return createAuthorizationURL({
2613
+ id: "twitch",
2614
+ redirectURI,
2615
+ options,
2616
+ authorizationEndpoint: "https://id.twitch.tv/oauth2/authorize",
2617
+ scopes: _scopes,
2618
+ state,
2619
+ claims: options.claims || [
2620
+ "email",
2621
+ "email_verified",
2622
+ "preferred_username",
2623
+ "picture"
2624
+ ]
2625
+ });
2626
+ },
2627
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2628
+ return validateAuthorizationCode({
2629
+ code,
2630
+ redirectURI,
2631
+ options,
2632
+ tokenEndpoint: "https://id.twitch.tv/oauth2/token"
2633
+ });
2634
+ },
2635
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2636
+ return refreshAccessToken({
2637
+ refreshToken,
2638
+ options: {
2639
+ clientId: options.clientId,
2640
+ clientKey: options.clientKey,
2641
+ clientSecret: options.clientSecret
2642
+ },
2643
+ tokenEndpoint: "https://id.twitch.tv/oauth2/token"
2644
+ });
2645
+ },
2646
+ async getUserInfo(token) {
2647
+ if (options.getUserInfo) return options.getUserInfo(token);
2648
+ const idToken = token.idToken;
2649
+ if (!idToken) {
2650
+ logger.error("No idToken found in token");
2651
+ return null;
2652
+ }
2653
+ const profile = decodeJwt(idToken);
2654
+ const userMap = await options.mapProfileToUser?.(profile);
2655
+ return {
2656
+ user: {
2657
+ id: profile.sub,
2658
+ name: profile.preferred_username,
2659
+ email: profile.email,
2660
+ image: profile.picture,
2661
+ emailVerified: profile.email_verified,
2662
+ ...userMap
2663
+ },
2664
+ data: profile
2665
+ };
2666
+ },
2667
+ options
2668
+ };
2669
+ };
2670
+ const twitter = (options) => {
2671
+ return {
2672
+ id: "twitter",
2673
+ name: "Twitter",
2674
+ createAuthorizationURL(data) {
2675
+ const _scopes = options.disableDefaultScope ? [] : [
2676
+ "users.read",
2677
+ "tweet.read",
2678
+ "offline.access",
2679
+ "users.email"
2680
+ ];
2681
+ if (options.scope) _scopes.push(...options.scope);
2682
+ if (data.scopes) _scopes.push(...data.scopes);
2683
+ return createAuthorizationURL({
2684
+ id: "twitter",
2685
+ options,
2686
+ authorizationEndpoint: "https://x.com/i/oauth2/authorize",
2687
+ scopes: _scopes,
2688
+ state: data.state,
2689
+ codeVerifier: data.codeVerifier,
2690
+ redirectURI: data.redirectURI
2691
+ });
2692
+ },
2693
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
2694
+ return validateAuthorizationCode({
2695
+ code,
2696
+ codeVerifier,
2697
+ authentication: "basic",
2698
+ redirectURI,
2699
+ options,
2700
+ tokenEndpoint: "https://api.x.com/2/oauth2/token"
2701
+ });
2702
+ },
2703
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2704
+ return refreshAccessToken({
2705
+ refreshToken,
2706
+ options: {
2707
+ clientId: options.clientId,
2708
+ clientKey: options.clientKey,
2709
+ clientSecret: options.clientSecret
2710
+ },
2711
+ authentication: "basic",
2712
+ tokenEndpoint: "https://api.x.com/2/oauth2/token"
2713
+ });
2714
+ },
2715
+ async getUserInfo(token) {
2716
+ if (options.getUserInfo) return options.getUserInfo(token);
2717
+ const { data: profile, error: profileError } = await betterFetch("https://api.x.com/2/users/me?user.fields=profile_image_url", {
2718
+ method: "GET",
2719
+ headers: { Authorization: `Bearer ${token.accessToken}` }
2720
+ });
2721
+ if (profileError) return null;
2722
+ const { data: emailData, error: emailError } = await betterFetch("https://api.x.com/2/users/me?user.fields=confirmed_email", {
2723
+ method: "GET",
2724
+ headers: { Authorization: `Bearer ${token.accessToken}` }
2725
+ });
2726
+ let emailVerified = false;
2727
+ if (!emailError && emailData?.data?.confirmed_email) {
2728
+ profile.data.email = emailData.data.confirmed_email;
2729
+ emailVerified = true;
2730
+ }
2731
+ const userMap = await options.mapProfileToUser?.(profile);
2732
+ return {
2733
+ user: {
2734
+ id: profile.data.id,
2735
+ name: profile.data.name,
2736
+ email: profile.data.email || profile.data.username || null,
2737
+ image: profile.data.profile_image_url,
2738
+ emailVerified,
2739
+ ...userMap
2740
+ },
2741
+ data: profile
2742
+ };
2743
+ },
2744
+ options
2745
+ };
2746
+ };
2747
+ const vk = (options) => {
2748
+ return {
2749
+ id: "vk",
2750
+ name: "VK",
2751
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
2752
+ const _scopes = options.disableDefaultScope ? [] : ["email", "phone"];
2753
+ if (options.scope) _scopes.push(...options.scope);
2754
+ if (scopes) _scopes.push(...scopes);
2755
+ return createAuthorizationURL({
2756
+ id: "vk",
2757
+ options,
2758
+ authorizationEndpoint: "https://id.vk.com/authorize",
2759
+ scopes: _scopes,
2760
+ state,
2761
+ redirectURI,
2762
+ codeVerifier
2763
+ });
2764
+ },
2765
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI, deviceId }) => {
2766
+ return validateAuthorizationCode({
2767
+ code,
2768
+ codeVerifier,
2769
+ redirectURI: options.redirectURI || redirectURI,
2770
+ options,
2771
+ deviceId,
2772
+ tokenEndpoint: "https://id.vk.com/oauth2/auth"
2773
+ });
2774
+ },
2775
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2776
+ return refreshAccessToken({
2777
+ refreshToken,
2778
+ options: {
2779
+ clientId: options.clientId,
2780
+ clientKey: options.clientKey,
2781
+ clientSecret: options.clientSecret
2782
+ },
2783
+ tokenEndpoint: "https://id.vk.com/oauth2/auth"
2784
+ });
2785
+ },
2786
+ async getUserInfo(data) {
2787
+ if (options.getUserInfo) return options.getUserInfo(data);
2788
+ if (!data.accessToken) return null;
2789
+ const formBody = new URLSearchParams({
2790
+ access_token: data.accessToken,
2791
+ client_id: options.clientId
2792
+ }).toString();
2793
+ const { data: profile, error } = await betterFetch("https://id.vk.com/oauth2/user_info", {
2794
+ method: "POST",
2795
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
2796
+ body: formBody
2797
+ });
2798
+ if (error) return null;
2799
+ const userMap = await options.mapProfileToUser?.(profile);
2800
+ if (!profile.user.email && !userMap?.email) return null;
2801
+ return {
2802
+ user: {
2803
+ id: profile.user.user_id,
2804
+ first_name: profile.user.first_name,
2805
+ last_name: profile.user.last_name,
2806
+ email: profile.user.email,
2807
+ image: profile.user.avatar,
2808
+ emailVerified: !!profile.user.email,
2809
+ birthday: profile.user.birthday,
2810
+ sex: profile.user.sex,
2811
+ name: `${profile.user.first_name} ${profile.user.last_name}`,
2812
+ ...userMap
2813
+ },
2814
+ data: profile
2815
+ };
2816
+ },
2817
+ options
2818
+ };
2819
+ };
2820
+ const zoom = (userOptions) => {
2821
+ const options = {
2822
+ pkce: true,
2823
+ ...userOptions
2824
+ };
2825
+ return {
2826
+ id: "zoom",
2827
+ name: "Zoom",
2828
+ createAuthorizationURL: async ({ state, redirectURI, codeVerifier }) => {
2829
+ const params = new URLSearchParams({
2830
+ response_type: "code",
2831
+ redirect_uri: options.redirectURI ? options.redirectURI : redirectURI,
2832
+ client_id: options.clientId,
2833
+ state
2834
+ });
2835
+ if (options.pkce) {
2836
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
2837
+ params.set("code_challenge_method", "S256");
2838
+ params.set("code_challenge", codeChallenge);
2839
+ }
2840
+ const url = new URL("https://zoom.us/oauth/authorize");
2841
+ url.search = params.toString();
2842
+ return url;
2843
+ },
2844
+ validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
2845
+ return validateAuthorizationCode({
2846
+ code,
2847
+ redirectURI: options.redirectURI || redirectURI,
2848
+ codeVerifier,
2849
+ options,
2850
+ tokenEndpoint: "https://zoom.us/oauth/token",
2851
+ authentication: "post"
2852
+ });
2853
+ },
2854
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => refreshAccessToken({
2855
+ refreshToken,
2856
+ options: {
2857
+ clientId: options.clientId,
2858
+ clientKey: options.clientKey,
2859
+ clientSecret: options.clientSecret
2860
+ },
2861
+ tokenEndpoint: "https://zoom.us/oauth/token"
2862
+ }),
2863
+ async getUserInfo(token) {
2864
+ if (options.getUserInfo) return options.getUserInfo(token);
2865
+ const { data: profile, error } = await betterFetch("https://api.zoom.us/v2/users/me", { headers: { authorization: `Bearer ${token.accessToken}` } });
2866
+ if (error) return null;
2867
+ const userMap = await options.mapProfileToUser?.(profile);
2868
+ return {
2869
+ user: {
2870
+ id: profile.id,
2871
+ name: profile.display_name,
2872
+ image: profile.pic_url,
2873
+ email: profile.email,
2874
+ emailVerified: Boolean(profile.verified),
2875
+ ...userMap
2876
+ },
2877
+ data: { ...profile }
2878
+ };
2879
+ }
2880
+ };
2881
+ };
2882
+ const socialProviders = {
2883
+ apple,
2884
+ atlassian,
2885
+ cognito,
2886
+ discord,
2887
+ facebook,
2888
+ figma,
2889
+ github,
2890
+ microsoft,
2891
+ google,
2892
+ huggingface,
2893
+ slack,
2894
+ spotify,
2895
+ twitch,
2896
+ twitter,
2897
+ dropbox,
2898
+ kick,
2899
+ linear,
2900
+ linkedin,
2901
+ gitlab,
2902
+ tiktok,
2903
+ reddit,
2904
+ roblox,
2905
+ salesforce,
2906
+ vk,
2907
+ zoom,
2908
+ notion,
2909
+ kakao,
2910
+ naver,
2911
+ line,
2912
+ paybin,
2913
+ paypal,
2914
+ polar
2915
+ };
2916
+ const socialProviderList = Object.keys(socialProviders);
2917
+ const SocialProviderListEnum = z$2.enum(socialProviderList).or(z$2.string());
2918
+
2919
+ //#endregion
2920
+ export { paybin as A, spotify as B, kick as C, microsoft as D, linkedin as E, roblox as F, validateToken as G, twitch as H, salesforce as I, decodeJwt as J, vk as K, slack as L, polar as M, reddit as N, naver as O, refreshAccessToken as P, socialProviderList as R, kakao as S, linear as T, twitter as U, tiktok as V, validateAuthorizationCode as W, createRemoteJWKSet as Y, getOAuth2Tokens as _, cognito as a, google as b, createClientCredentialsTokenRequest as c, dropbox as d, facebook as f, getCognitoPublicKey as g, getApplePublicKey as h, clientCredentialsToken as i, paypal as j, notion as k, createRefreshAccessTokenRequest as l, generateCodeChallenge as m, apple as n, createAuthorizationCodeRequest as o, figma as p, zoom as q, atlassian as r, createAuthorizationURL as s, SocialProviderListEnum as t, discord as u, github as v, line as w, huggingface as x, gitlab as y, socialProviders as z };