@xylex-group/athena 2.12.0 → 2.13.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 (96) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1190 -1184
  3. package/bin/athena-js.js +104 -76
  4. package/dist/admin.cjs +45 -0
  5. package/dist/admin.cjs.map +1 -0
  6. package/dist/admin.d.cts +64 -0
  7. package/dist/admin.d.ts +64 -0
  8. package/dist/admin.js +41 -0
  9. package/dist/admin.js.map +1 -0
  10. package/dist/athena-auth-url-oLux_57a.d.cts +377 -0
  11. package/dist/athena-auth-url-oLux_57a.d.ts +377 -0
  12. package/dist/browser.cjs +33 -46
  13. package/dist/browser.cjs.map +1 -1
  14. package/dist/browser.d.cts +12 -10
  15. package/dist/browser.d.ts +12 -10
  16. package/dist/browser.js +33 -46
  17. package/dist/browser.js.map +1 -1
  18. package/dist/cli/index.cjs +150 -48
  19. package/dist/cli/index.cjs.map +1 -1
  20. package/dist/cli/index.d.cts +14 -5
  21. package/dist/cli/index.d.ts +14 -5
  22. package/dist/cli/index.js +150 -49
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/{client-QUbAs7E8.d.ts → client-BJjUwDSg.d.cts} +109 -1448
  25. package/dist/{client-C3x75Zgn.d.cts → client-DVOKSYDI.d.ts} +109 -1448
  26. package/dist/cookies.cjs +18 -0
  27. package/dist/cookies.cjs.map +1 -1
  28. package/dist/cookies.d.cts +2 -1
  29. package/dist/cookies.d.ts +2 -1
  30. package/dist/cookies.js +17 -1
  31. package/dist/cookies.js.map +1 -1
  32. package/dist/{index-CVcQCGyG.d.ts → index-CFL9xbFR.d.cts} +2 -0
  33. package/dist/{index-CVcQCGyG.d.cts → index-UKJpunc6.d.ts} +2 -0
  34. package/dist/index.cjs +101 -46
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +13 -10
  37. package/dist/index.d.ts +13 -10
  38. package/dist/index.js +101 -47
  39. package/dist/index.js.map +1 -1
  40. package/dist/{model-form-CD1uhWSh.d.cts → model-form-Dw4zEL5a.d.cts} +2 -2
  41. package/dist/{model-form-Dm69I-oO.d.ts → model-form-yRg71q3H.d.ts} +2 -2
  42. package/dist/{module-CW3tWJ10.d.ts → module-DBteUIob.d.ts} +12 -9
  43. package/dist/{module-Cxcurfes.d.cts → module-yoX49uQC.d.cts} +12 -9
  44. package/dist/next/client.cjs +452 -46
  45. package/dist/next/client.cjs.map +1 -1
  46. package/dist/next/client.d.cts +7 -4
  47. package/dist/next/client.d.ts +7 -4
  48. package/dist/next/client.js +430 -47
  49. package/dist/next/client.js.map +1 -1
  50. package/dist/next/server.cjs +292 -46
  51. package/dist/next/server.cjs.map +1 -1
  52. package/dist/next/server.d.cts +81 -5
  53. package/dist/next/server.d.ts +81 -5
  54. package/dist/next/server.js +280 -47
  55. package/dist/next/server.js.map +1 -1
  56. package/dist/organization.cjs +72 -0
  57. package/dist/organization.cjs.map +1 -0
  58. package/dist/organization.d.cts +43 -0
  59. package/dist/organization.d.ts +43 -0
  60. package/dist/organization.js +70 -0
  61. package/dist/organization.js.map +1 -0
  62. package/dist/payload-BzVbUgY_.d.cts +219 -0
  63. package/dist/payload-DEOWN_oo.d.ts +219 -0
  64. package/dist/{pipeline-BAwb6Vzm.d.ts → pipeline-Bj6RWt1A.d.ts} +1 -1
  65. package/dist/{pipeline-B14jVK7J.d.cts → pipeline-Dwck-wZO.d.cts} +1 -1
  66. package/dist/react.cjs +2 -10
  67. package/dist/react.cjs.map +1 -1
  68. package/dist/react.d.cts +26 -7
  69. package/dist/react.d.ts +26 -7
  70. package/dist/react.js +2 -10
  71. package/dist/react.js.map +1 -1
  72. package/dist/session-cookie-detection-BqOp9mO6.d.cts +46 -0
  73. package/dist/session-cookie-detection-BqOp9mO6.d.ts +46 -0
  74. package/dist/social-providers.cjs +4189 -0
  75. package/dist/social-providers.cjs.map +1 -0
  76. package/dist/social-providers.d.cts +4948 -0
  77. package/dist/social-providers.d.ts +4948 -0
  78. package/dist/social-providers.js +4117 -0
  79. package/dist/social-providers.js.map +1 -0
  80. package/dist/{types-Cq4-NoB4.d.ts → types-BRP7sfSF.d.ts} +50 -2
  81. package/dist/{types-Dr849HD6.d.cts → types-BU8uJH_b.d.cts} +50 -2
  82. package/dist/{types-CwJCPpLD.d.ts → types-CH78O90i.d.cts} +2 -2
  83. package/dist/{types-CwJCPpLD.d.cts → types-CH78O90i.d.ts} +2 -2
  84. package/dist/{types-DMOoYnPS.d.cts → types-CjC9_5ZQ.d.cts} +3 -3
  85. package/dist/{types-BcVmPBP-.d.ts → types-DPgejxYC.d.ts} +3 -3
  86. package/dist/types-eAKAh71s.d.cts +1476 -0
  87. package/dist/types-eAKAh71s.d.ts +1476 -0
  88. package/dist/utils.cjs +438 -12
  89. package/dist/utils.cjs.map +1 -1
  90. package/dist/utils.d.cts +76 -11
  91. package/dist/utils.d.ts +76 -11
  92. package/dist/utils.js +390 -13
  93. package/dist/utils.js.map +1 -1
  94. package/package.json +49 -22
  95. package/dist/shared-0Kdc74lu.d.ts +0 -35
  96. package/dist/shared-C0wVICRv.d.cts +0 -35
@@ -0,0 +1,4189 @@
1
+ 'use strict';
2
+
3
+ var z = require('zod');
4
+ var jose = require('jose');
5
+
6
+ function _interopNamespace(e) {
7
+ if (e && e.__esModule) return e;
8
+ var n = Object.create(null);
9
+ if (e) {
10
+ Object.keys(e).forEach(function (k) {
11
+ if (k !== 'default') {
12
+ var d = Object.getOwnPropertyDescriptor(e, k);
13
+ Object.defineProperty(n, k, d.get ? d : {
14
+ enumerable: true,
15
+ get: function () { return e[k]; }
16
+ });
17
+ }
18
+ });
19
+ }
20
+ n.default = e;
21
+ return Object.freeze(n);
22
+ }
23
+
24
+ var z__namespace = /*#__PURE__*/_interopNamespace(z);
25
+
26
+ // src/auth/social-providers/registry.ts
27
+
28
+ // src/auth/env/logger.ts
29
+ var levels = ["debug", "info", "success", "warn", "error"];
30
+ function shouldPublishLog(current, level) {
31
+ return levels.indexOf(level) >= levels.indexOf(current);
32
+ }
33
+ function resolveLogLevel() {
34
+ try {
35
+ const env = typeof process !== "undefined" && process.env ? process.env.ATHENA_AUTH_LOG_LEVEL : void 0;
36
+ if (env === "debug" || env === "info" || env === "warn" || env === "error" || env === "success") {
37
+ return env;
38
+ }
39
+ } catch {
40
+ }
41
+ return "warn";
42
+ }
43
+ function log(level, message, ...args) {
44
+ const current = resolveLogLevel();
45
+ if (!shouldPublishLog(current, level === "info" ? "info" : level)) return;
46
+ const fn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
47
+ fn(`[athena-auth] ${message}`, ...args);
48
+ }
49
+ var logger = {
50
+ debug: (message, ...args) => log("debug", message, ...args),
51
+ info: (message, ...args) => log("info", message, ...args),
52
+ success: (message, ...args) => log("info", message, ...args),
53
+ warn: (message, ...args) => log("warn", message, ...args),
54
+ error: (message, ...args) => log("error", message, ...args)
55
+ };
56
+
57
+ // src/auth/error/athena-auth-error.ts
58
+ var AthenaAuthError = class extends Error {
59
+ constructor(message, options) {
60
+ super(message, options);
61
+ this.name = "AthenaAuthError";
62
+ this.message = message;
63
+ this.stack = "";
64
+ }
65
+ };
66
+
67
+ // src/auth/error/api-error.ts
68
+ var APIError = class _APIError extends Error {
69
+ status;
70
+ statusCode;
71
+ body;
72
+ headers;
73
+ constructor(status, body) {
74
+ const message = typeof body === "string" ? body : body?.message ?? (typeof status === "string" ? status : `HTTP ${status}`);
75
+ super(message);
76
+ this.name = "APIError";
77
+ this.status = status;
78
+ this.statusCode = typeof status === "number" ? status : 500;
79
+ this.body = typeof body === "string" ? { message: body } : body;
80
+ this.headers = void 0;
81
+ }
82
+ static fromStatus(status, body) {
83
+ return new _APIError(status, body);
84
+ }
85
+ static from(status, error) {
86
+ return new _APIError(status, {
87
+ message: error.message,
88
+ code: error.code
89
+ });
90
+ }
91
+ };
92
+
93
+ // src/auth/utils/base64.ts
94
+ function toUint8Array(input) {
95
+ if (typeof input === "string") {
96
+ return new TextEncoder().encode(input);
97
+ }
98
+ if (input instanceof Uint8Array) {
99
+ return input;
100
+ }
101
+ return new Uint8Array(input);
102
+ }
103
+ function bytesToBase64(bytes, urlSafe, padding) {
104
+ let binary = "";
105
+ for (let i = 0; i < bytes.length; i++) {
106
+ binary += String.fromCharCode(bytes[i]);
107
+ }
108
+ let encoded = btoa(binary);
109
+ if (urlSafe) {
110
+ encoded = encoded.replace(/\+/g, "-").replace(/\//g, "_");
111
+ }
112
+ if (!padding) {
113
+ encoded = encoded.replace(/=+$/g, "");
114
+ }
115
+ return encoded;
116
+ }
117
+ var base64 = {
118
+ encode(input) {
119
+ return bytesToBase64(toUint8Array(input), false, true);
120
+ }
121
+ };
122
+ var base64Url = {
123
+ encode(input, options) {
124
+ return bytesToBase64(toUint8Array(input), true, options?.padding !== false);
125
+ }
126
+ };
127
+
128
+ // src/auth/fetch/body.ts
129
+ function normalizeFetchBody(body) {
130
+ if (body == null) return body;
131
+ if (typeof body === "string") return body;
132
+ if (body instanceof URLSearchParams) return body;
133
+ if (body instanceof FormData) return body;
134
+ if (body instanceof Blob) return body;
135
+ if (typeof body === "object") {
136
+ return JSON.stringify(body);
137
+ }
138
+ return String(body);
139
+ }
140
+ function applyFetchQuery(url, query) {
141
+ if (!query) return url;
142
+ const u = new URL(url);
143
+ for (const [key, value] of Object.entries(query)) {
144
+ if (value === void 0 || value === null) continue;
145
+ u.searchParams.set(key, String(value));
146
+ }
147
+ return u.toString();
148
+ }
149
+
150
+ // src/auth/fetch/athena-fetch.ts
151
+ async function athenaFetch(url, options = {}) {
152
+ const headers = new Headers(options.headers);
153
+ if (options.auth?.token) {
154
+ const type = options.auth.type ?? "Bearer";
155
+ headers.set("authorization", `${type} ${options.auth.token}`);
156
+ }
157
+ const target = applyFetchQuery(url, options.query);
158
+ const body = normalizeFetchBody(options.body);
159
+ if (body != null && typeof body === "string" && !headers.has("content-type") && body.startsWith("{")) {
160
+ headers.set("content-type", "application/json");
161
+ }
162
+ try {
163
+ const response = await fetch(target, {
164
+ method: options.method ?? "GET",
165
+ headers,
166
+ body: body ?? void 0,
167
+ redirect: options.redirect ?? "follow",
168
+ signal: options.signal
169
+ });
170
+ options.onResponse?.({ response });
171
+ if (!response.ok) {
172
+ let message = response.statusText || `HTTP ${response.status}`;
173
+ try {
174
+ const errBody = await response.clone().json();
175
+ message = errBody.message || errBody.error_description || errBody.error || message;
176
+ } catch {
177
+ try {
178
+ message = await response.clone().text() || message;
179
+ } catch {
180
+ }
181
+ }
182
+ const error = {
183
+ message,
184
+ status: response.status,
185
+ statusText: response.statusText
186
+ };
187
+ options.onError?.({ response, error });
188
+ return { data: null, error };
189
+ }
190
+ const contentType = response.headers.get("content-type") ?? "";
191
+ if (contentType.includes("application/json") || contentType.includes("+json")) {
192
+ const data = await response.json();
193
+ return { data, error: null };
194
+ }
195
+ const text = await response.text();
196
+ if (!text) {
197
+ return { data: {}, error: null };
198
+ }
199
+ try {
200
+ return { data: JSON.parse(text), error: null };
201
+ } catch {
202
+ return { data: text, error: null };
203
+ }
204
+ } catch (cause) {
205
+ const error = {
206
+ message: cause instanceof Error ? cause.message : String(cause),
207
+ status: 0,
208
+ statusText: "NetworkError"
209
+ };
210
+ options.onError?.({
211
+ response: new Response(null, { status: 502, statusText: "Network Error" }),
212
+ error: cause
213
+ });
214
+ return { data: null, error };
215
+ }
216
+ }
217
+
218
+ // src/auth/oauth2/reject-redirects.ts
219
+ var HTTP_REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
220
+ function isRedirectResponse(response) {
221
+ return response.type === "opaqueredirect" || HTTP_REDIRECT_STATUSES.has(response.status);
222
+ }
223
+ function redirectRefused(endpoint) {
224
+ return new AthenaAuthError(
225
+ `The OAuth endpoint "${endpoint}" returned an HTTP redirect. Server-side OAuth fetches refuse redirects to prevent SSRF; configure the final endpoint URL.`
226
+ );
227
+ }
228
+ var NO_FOLLOW_REDIRECT = { redirect: "manual" };
229
+ async function fetchRefusingRedirects(url, options) {
230
+ let redirected = false;
231
+ const result = await athenaFetch(url, {
232
+ ...options,
233
+ ...NO_FOLLOW_REDIRECT,
234
+ onError(context) {
235
+ if (isRedirectResponse(context.response)) redirected = true;
236
+ }
237
+ });
238
+ if (redirected) throw redirectRefused(url);
239
+ return result;
240
+ }
241
+
242
+ // src/auth/oauth2/token-utils.ts
243
+ function getOAuth2Tokens(data) {
244
+ const getDate = (seconds) => {
245
+ const now = /* @__PURE__ */ new Date();
246
+ return new Date(now.getTime() + seconds * 1e3);
247
+ };
248
+ return {
249
+ tokenType: data.token_type,
250
+ accessToken: data.access_token,
251
+ refreshToken: data.refresh_token,
252
+ accessTokenExpiresAt: data.expires_in ? getDate(data.expires_in) : void 0,
253
+ refreshTokenExpiresAt: data.refresh_token_expires_in ? getDate(data.refresh_token_expires_in) : void 0,
254
+ scopes: data?.scope ? typeof data.scope === "string" ? data.scope.split(" ") : data.scope : [],
255
+ idToken: data.id_token,
256
+ raw: data
257
+ };
258
+ }
259
+
260
+ // src/auth/oauth2/client-id.ts
261
+ function getPrimaryClientId(clientId) {
262
+ const value = Array.isArray(clientId) ? clientId[0] : clientId;
263
+ return typeof value === "string" && value.length > 0 ? value : void 0;
264
+ }
265
+
266
+ // src/auth/oauth2/pkce.ts
267
+ async function generateCodeChallenge(codeVerifier) {
268
+ const encoder = new TextEncoder();
269
+ const data = encoder.encode(codeVerifier);
270
+ const hash = await crypto.subtle.digest("SHA-256", data);
271
+ return base64Url.encode(new Uint8Array(hash), {
272
+ padding: false
273
+ });
274
+ }
275
+
276
+ // src/auth/oauth2/create-authorization-url.ts
277
+ async function createAuthorizationURL({
278
+ id: _id,
279
+ options,
280
+ authorizationEndpoint: authorizationEndpoint2,
281
+ state,
282
+ codeVerifier,
283
+ scopes,
284
+ claims,
285
+ redirectURI,
286
+ duration,
287
+ prompt,
288
+ accessType,
289
+ responseType,
290
+ display,
291
+ loginHint,
292
+ hd,
293
+ responseMode,
294
+ additionalParams,
295
+ scopeJoiner
296
+ }) {
297
+ options = typeof options === "function" ? await options() : options;
298
+ const url = new URL(options.authorizationEndpoint || authorizationEndpoint2);
299
+ url.searchParams.set("response_type", responseType || "code");
300
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
301
+ url.searchParams.set("client_id", primaryClientId);
302
+ url.searchParams.set("state", state);
303
+ if (scopes) {
304
+ url.searchParams.set("scope", scopes.join(scopeJoiner || " "));
305
+ }
306
+ url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
307
+ duration && url.searchParams.set("duration", duration);
308
+ display && url.searchParams.set("display", display);
309
+ loginHint && url.searchParams.set("login_hint", loginHint);
310
+ prompt && url.searchParams.set("prompt", prompt);
311
+ hd && url.searchParams.set("hd", hd);
312
+ accessType && url.searchParams.set("access_type", accessType);
313
+ responseMode && url.searchParams.set("response_mode", responseMode);
314
+ if (codeVerifier) {
315
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
316
+ url.searchParams.set("code_challenge_method", "S256");
317
+ url.searchParams.set("code_challenge", codeChallenge);
318
+ }
319
+ if (claims) {
320
+ const claimsObj = claims.reduce(
321
+ (acc, claim) => {
322
+ acc[claim] = null;
323
+ return acc;
324
+ },
325
+ {}
326
+ );
327
+ url.searchParams.set(
328
+ "claims",
329
+ JSON.stringify({
330
+ id_token: { email: null, email_verified: null, ...claimsObj }
331
+ })
332
+ );
333
+ }
334
+ if (additionalParams) {
335
+ Object.entries(additionalParams).forEach(([key, value]) => {
336
+ url.searchParams.set(key, value);
337
+ });
338
+ }
339
+ return url;
340
+ }
341
+
342
+ // src/auth/oauth2/refresh-access-token.ts
343
+ function createRefreshAccessTokenRequest({
344
+ refreshToken,
345
+ options,
346
+ authentication,
347
+ extraParams,
348
+ resource
349
+ }) {
350
+ const body = new URLSearchParams();
351
+ const headers = {
352
+ "content-type": "application/x-www-form-urlencoded",
353
+ accept: "application/json"
354
+ };
355
+ body.set("grant_type", "refresh_token");
356
+ body.set("refresh_token", refreshToken);
357
+ if (authentication === "basic") {
358
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
359
+ if (primaryClientId) {
360
+ headers["authorization"] = "Basic " + base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`);
361
+ } else {
362
+ headers["authorization"] = "Basic " + base64.encode(`:${options.clientSecret ?? ""}`);
363
+ }
364
+ } else {
365
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
366
+ body.set("client_id", primaryClientId);
367
+ if (options.clientSecret) {
368
+ body.set("client_secret", options.clientSecret);
369
+ }
370
+ }
371
+ if (resource) {
372
+ if (typeof resource === "string") {
373
+ body.append("resource", resource);
374
+ } else {
375
+ for (const _resource of resource) {
376
+ body.append("resource", _resource);
377
+ }
378
+ }
379
+ }
380
+ if (extraParams) {
381
+ for (const [key, value] of Object.entries(extraParams)) {
382
+ body.set(key, value);
383
+ }
384
+ }
385
+ return {
386
+ body,
387
+ headers
388
+ };
389
+ }
390
+ async function refreshAccessToken({
391
+ refreshToken,
392
+ options,
393
+ tokenEndpoint: tokenEndpoint2,
394
+ authentication,
395
+ extraParams
396
+ }) {
397
+ const { body, headers } = await createRefreshAccessTokenRequest({
398
+ refreshToken,
399
+ options,
400
+ authentication,
401
+ extraParams
402
+ });
403
+ const { data, error } = await fetchRefusingRedirects(tokenEndpoint2, {
404
+ method: "POST",
405
+ body,
406
+ headers
407
+ });
408
+ if (error) {
409
+ throw error;
410
+ }
411
+ const tokens = {
412
+ accessToken: data.access_token,
413
+ refreshToken: data.refresh_token,
414
+ tokenType: data.token_type,
415
+ scopes: data.scope?.split(" "),
416
+ idToken: data.id_token
417
+ };
418
+ if (data.expires_in) {
419
+ const now = /* @__PURE__ */ new Date();
420
+ tokens.accessTokenExpiresAt = new Date(
421
+ now.getTime() + data.expires_in * 1e3
422
+ );
423
+ }
424
+ if (data.refresh_token_expires_in) {
425
+ const now = /* @__PURE__ */ new Date();
426
+ tokens.refreshTokenExpiresAt = new Date(
427
+ now.getTime() + data.refresh_token_expires_in * 1e3
428
+ );
429
+ }
430
+ return tokens;
431
+ }
432
+ async function authorizationCodeRequest({
433
+ code,
434
+ codeVerifier,
435
+ redirectURI,
436
+ options,
437
+ authentication,
438
+ deviceId,
439
+ headers,
440
+ additionalParams = {},
441
+ resource
442
+ }) {
443
+ options = typeof options === "function" ? await options() : options;
444
+ return createAuthorizationCodeRequest({
445
+ code,
446
+ codeVerifier,
447
+ redirectURI,
448
+ options,
449
+ authentication,
450
+ deviceId,
451
+ headers,
452
+ additionalParams,
453
+ resource
454
+ });
455
+ }
456
+ function createAuthorizationCodeRequest({
457
+ code,
458
+ codeVerifier,
459
+ redirectURI,
460
+ options,
461
+ authentication,
462
+ deviceId,
463
+ headers,
464
+ additionalParams = {},
465
+ resource
466
+ }) {
467
+ const body = new URLSearchParams();
468
+ const requestHeaders = {
469
+ "content-type": "application/x-www-form-urlencoded",
470
+ accept: "application/json",
471
+ ...headers
472
+ };
473
+ body.set("grant_type", "authorization_code");
474
+ body.set("code", code);
475
+ codeVerifier && body.set("code_verifier", codeVerifier);
476
+ options.clientKey && body.set("client_key", options.clientKey);
477
+ deviceId && body.set("device_id", deviceId);
478
+ body.set("redirect_uri", options.redirectURI || redirectURI);
479
+ if (resource) {
480
+ if (typeof resource === "string") {
481
+ body.append("resource", resource);
482
+ } else {
483
+ for (const _resource of resource) {
484
+ body.append("resource", _resource);
485
+ }
486
+ }
487
+ }
488
+ if (authentication === "basic") {
489
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
490
+ const encodedCredentials = base64.encode(
491
+ `${primaryClientId}:${options.clientSecret ?? ""}`
492
+ );
493
+ requestHeaders["authorization"] = `Basic ${encodedCredentials}`;
494
+ } else {
495
+ const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
496
+ body.set("client_id", primaryClientId);
497
+ if (options.clientSecret) {
498
+ body.set("client_secret", options.clientSecret);
499
+ }
500
+ }
501
+ for (const [key, value] of Object.entries(additionalParams)) {
502
+ if (!body.has(key)) body.append(key, value);
503
+ }
504
+ return {
505
+ body,
506
+ headers: requestHeaders
507
+ };
508
+ }
509
+ async function validateAuthorizationCode({
510
+ code,
511
+ codeVerifier,
512
+ redirectURI,
513
+ options,
514
+ tokenEndpoint: tokenEndpoint2,
515
+ authentication,
516
+ deviceId,
517
+ headers,
518
+ additionalParams = {},
519
+ resource
520
+ }) {
521
+ const { body, headers: requestHeaders } = await authorizationCodeRequest({
522
+ code,
523
+ codeVerifier,
524
+ redirectURI,
525
+ options,
526
+ authentication,
527
+ deviceId,
528
+ headers,
529
+ additionalParams,
530
+ resource
531
+ });
532
+ const { data, error } = await fetchRefusingRedirects(tokenEndpoint2, {
533
+ method: "POST",
534
+ body,
535
+ headers: requestHeaders
536
+ });
537
+ if (error) {
538
+ throw error;
539
+ }
540
+ const tokens = getOAuth2Tokens(data);
541
+ return tokens;
542
+ }
543
+
544
+ // src/auth/social-providers/apple-crypto.ts
545
+ async function sha256Hex(value) {
546
+ const data = new TextEncoder().encode(value);
547
+ const digest = await crypto.subtle.digest("SHA-256", data);
548
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
549
+ }
550
+ async function nonceMatches(jwtNonce, nonce) {
551
+ if (typeof jwtNonce !== "string") {
552
+ return false;
553
+ }
554
+ if (jwtNonce === nonce) {
555
+ return true;
556
+ }
557
+ return jwtNonce === await sha256Hex(nonce);
558
+ }
559
+ async function getJwksPublicKey(jwksUrl, kid) {
560
+ const { data } = await athenaFetch(jwksUrl);
561
+ if (!data?.keys) {
562
+ throw new APIError("BAD_REQUEST", {
563
+ message: "Keys not found"
564
+ });
565
+ }
566
+ const jwk = data.keys.find((key) => key.kid === kid);
567
+ if (!jwk) {
568
+ throw new Error(`JWK with kid ${kid} not found`);
569
+ }
570
+ return await jose.importJWK(jwk, jwk.alg);
571
+ }
572
+
573
+ // src/auth/social-providers/apple-keys.ts
574
+ var getApplePublicKey = async (kid) => {
575
+ return getJwksPublicKey("https://appleid.apple.com/auth/keys", kid);
576
+ };
577
+
578
+ // src/auth/social-providers/apple.ts
579
+ var apple = (options) => {
580
+ const tokenEndpoint2 = "https://appleid.apple.com/auth/token";
581
+ return {
582
+ id: "apple",
583
+ name: "Apple",
584
+ async createAuthorizationURL({ state, scopes, redirectURI }) {
585
+ if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
586
+ logger.error(
587
+ "Client ID and client secret are required for Apple. Make sure to provide them in the options."
588
+ );
589
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
590
+ }
591
+ const _scope = options.disableDefaultScope ? [] : ["email", "name"];
592
+ if (options.scope) _scope.push(...options.scope);
593
+ if (scopes) _scope.push(...scopes);
594
+ const url = await createAuthorizationURL({
595
+ id: "apple",
596
+ options,
597
+ authorizationEndpoint: "https://appleid.apple.com/auth/authorize",
598
+ scopes: _scope,
599
+ state,
600
+ redirectURI,
601
+ responseMode: "form_post",
602
+ responseType: "code id_token"
603
+ });
604
+ return url;
605
+ },
606
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
607
+ return validateAuthorizationCode({
608
+ code,
609
+ codeVerifier,
610
+ redirectURI,
611
+ options,
612
+ tokenEndpoint: tokenEndpoint2
613
+ });
614
+ },
615
+ async verifyIdToken(token, nonce) {
616
+ if (options.disableIdTokenSignIn) {
617
+ return false;
618
+ }
619
+ if (options.verifyIdToken) {
620
+ return options.verifyIdToken(token, nonce);
621
+ }
622
+ try {
623
+ const decodedHeader = jose.decodeProtectedHeader(token);
624
+ const { kid, alg: jwtAlg } = decodedHeader;
625
+ if (!kid || !jwtAlg) return false;
626
+ const publicKey = await getApplePublicKey(kid);
627
+ const { payload: jwtClaims } = await jose.jwtVerify(token, publicKey, {
628
+ algorithms: [jwtAlg],
629
+ issuer: "https://appleid.apple.com",
630
+ audience: options.audience && options.audience.length ? options.audience : options.appBundleIdentifier ? options.appBundleIdentifier : options.clientId,
631
+ maxTokenAge: "1h"
632
+ });
633
+ ["email_verified", "is_private_email"].forEach((field) => {
634
+ if (jwtClaims[field] !== void 0) {
635
+ jwtClaims[field] = Boolean(jwtClaims[field]);
636
+ }
637
+ });
638
+ if (nonce && !await nonceMatches(jwtClaims.nonce, nonce)) {
639
+ return false;
640
+ }
641
+ return !!jwtClaims;
642
+ } catch {
643
+ return false;
644
+ }
645
+ },
646
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
647
+ return refreshAccessToken({
648
+ refreshToken,
649
+ options,
650
+ tokenEndpoint: tokenEndpoint2
651
+ });
652
+ },
653
+ async getUserInfo(token) {
654
+ if (options.getUserInfo) {
655
+ return options.getUserInfo(token);
656
+ }
657
+ if (!token.idToken) {
658
+ return null;
659
+ }
660
+ const profile = jose.decodeJwt(token.idToken);
661
+ if (!profile) {
662
+ return null;
663
+ }
664
+ let name;
665
+ if (token.user?.name) {
666
+ const firstName = token.user.name.firstName || "";
667
+ const lastName = token.user.name.lastName || "";
668
+ const fullName = `${firstName} ${lastName}`.trim();
669
+ name = fullName;
670
+ } else {
671
+ name = profile.name || "";
672
+ }
673
+ const emailVerified = typeof profile.email_verified === "boolean" ? profile.email_verified : profile.email_verified === "true";
674
+ const enrichedProfile = {
675
+ ...profile,
676
+ name
677
+ };
678
+ const userMap = await options.mapProfileToUser?.(enrichedProfile);
679
+ return {
680
+ user: {
681
+ id: profile.sub,
682
+ name: enrichedProfile.name,
683
+ emailVerified,
684
+ email: profile.email,
685
+ ...userMap
686
+ },
687
+ data: enrichedProfile
688
+ };
689
+ },
690
+ options
691
+ };
692
+ };
693
+
694
+ // src/auth/social-providers/atlassian.ts
695
+ var atlassian = (options) => {
696
+ const tokenEndpoint2 = "https://auth.atlassian.com/oauth/token";
697
+ return {
698
+ id: "atlassian",
699
+ name: "Atlassian",
700
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
701
+ if (!options.clientId || !options.clientSecret) {
702
+ logger.error("Client Id and Secret are required for Atlassian");
703
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
704
+ }
705
+ if (!codeVerifier) {
706
+ throw new AthenaAuthError("codeVerifier is required for Atlassian");
707
+ }
708
+ const _scopes = options.disableDefaultScope ? [] : ["read:jira-user", "offline_access"];
709
+ if (options.scope) _scopes.push(...options.scope);
710
+ if (scopes) _scopes.push(...scopes);
711
+ return createAuthorizationURL({
712
+ id: "atlassian",
713
+ options,
714
+ authorizationEndpoint: "https://auth.atlassian.com/authorize",
715
+ scopes: _scopes,
716
+ state,
717
+ codeVerifier,
718
+ redirectURI,
719
+ additionalParams: {
720
+ audience: "api.atlassian.com"
721
+ },
722
+ prompt: options.prompt
723
+ });
724
+ },
725
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
726
+ return validateAuthorizationCode({
727
+ code,
728
+ codeVerifier,
729
+ redirectURI,
730
+ options,
731
+ tokenEndpoint: tokenEndpoint2
732
+ });
733
+ },
734
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
735
+ return refreshAccessToken({
736
+ refreshToken,
737
+ options: {
738
+ clientId: options.clientId,
739
+ clientSecret: options.clientSecret
740
+ },
741
+ tokenEndpoint: tokenEndpoint2
742
+ });
743
+ },
744
+ async getUserInfo(token) {
745
+ if (options.getUserInfo) {
746
+ return options.getUserInfo(token);
747
+ }
748
+ if (!token.accessToken) {
749
+ return null;
750
+ }
751
+ try {
752
+ const { data: profile } = await athenaFetch("https://api.atlassian.com/me", {
753
+ headers: { Authorization: `Bearer ${token.accessToken}` }
754
+ });
755
+ if (!profile) return null;
756
+ const userMap = await options.mapProfileToUser?.(profile);
757
+ return {
758
+ user: {
759
+ id: profile.account_id,
760
+ name: profile.name,
761
+ email: profile.email,
762
+ image: profile.picture,
763
+ emailVerified: false,
764
+ ...userMap
765
+ },
766
+ data: profile
767
+ };
768
+ } catch (error) {
769
+ logger.error("Failed to fetch user info from Figma:", error);
770
+ return null;
771
+ }
772
+ },
773
+ options
774
+ };
775
+ };
776
+
777
+ // src/auth/social-providers/helpers/trim-trailing-slash.ts
778
+ function trimTrailingSlash(value) {
779
+ let result = value;
780
+ while (result.endsWith("/")) {
781
+ result = result.slice(0, -1);
782
+ }
783
+ return result;
784
+ }
785
+
786
+ // src/auth/social-providers/athena.ts
787
+ function resolveIssuer(options) {
788
+ if (options.issuer) return trimTrailingSlash(options.issuer);
789
+ if (options.authorizationEndpoint) {
790
+ try {
791
+ const url = new URL(options.authorizationEndpoint);
792
+ return `${url.protocol}//${url.host}`;
793
+ } catch {
794
+ }
795
+ }
796
+ throw new Error(
797
+ "Athena social provider requires `issuer` (or a full `authorizationEndpoint`) in options."
798
+ );
799
+ }
800
+ var athena = (options) => {
801
+ const issuer = () => resolveIssuer(options);
802
+ const authorizationEndpoint2 = () => options.authorizationEndpoint || `${issuer()}/oauth2/authorize`;
803
+ const tokenEndpoint2 = () => options.tokenEndpoint || `${issuer()}/oauth2/token`;
804
+ const userInfoEndpoint = () => options.userInfoEndpoint || `${issuer()}/oauth2/userinfo`;
805
+ return {
806
+ id: "athena",
807
+ name: "Athena",
808
+ createAuthorizationURL({
809
+ state,
810
+ scopes,
811
+ codeVerifier,
812
+ redirectURI,
813
+ loginHint,
814
+ display
815
+ }) {
816
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile", "email"];
817
+ if (options.scope) _scopes.push(...options.scope);
818
+ if (scopes) _scopes.push(...scopes);
819
+ return createAuthorizationURL({
820
+ id: "athena",
821
+ options,
822
+ authorizationEndpoint: authorizationEndpoint2(),
823
+ scopes: _scopes,
824
+ state,
825
+ codeVerifier,
826
+ redirectURI,
827
+ loginHint,
828
+ display,
829
+ prompt: options.prompt
830
+ });
831
+ },
832
+ validateAuthorizationCode: async ({
833
+ code,
834
+ codeVerifier,
835
+ redirectURI
836
+ }) => {
837
+ return validateAuthorizationCode({
838
+ code,
839
+ codeVerifier,
840
+ redirectURI,
841
+ options,
842
+ tokenEndpoint: tokenEndpoint2()
843
+ });
844
+ },
845
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
846
+ return refreshAccessToken({
847
+ refreshToken,
848
+ options: {
849
+ clientId: options.clientId,
850
+ clientKey: options.clientKey,
851
+ clientSecret: options.clientSecret
852
+ },
853
+ tokenEndpoint: tokenEndpoint2()
854
+ });
855
+ },
856
+ async getUserInfo(token) {
857
+ if (options.getUserInfo) {
858
+ return options.getUserInfo(token);
859
+ }
860
+ if (!token.accessToken) {
861
+ return null;
862
+ }
863
+ const { data: profile, error } = await athenaFetch(
864
+ userInfoEndpoint(),
865
+ {
866
+ headers: {
867
+ Authorization: `Bearer ${token.accessToken}`,
868
+ "User-Agent": "athena-auth",
869
+ Accept: "application/json"
870
+ }
871
+ }
872
+ );
873
+ if (error || !profile) {
874
+ logger.error("Athena OAuth userinfo failed:", error);
875
+ return null;
876
+ }
877
+ const userMap = await options.mapProfileToUser?.(profile);
878
+ return {
879
+ user: {
880
+ id: profile.sub,
881
+ name: profile.name,
882
+ email: profile.email,
883
+ image: profile.picture,
884
+ emailVerified: profile.email_verified ?? false,
885
+ ...userMap
886
+ },
887
+ data: profile
888
+ };
889
+ },
890
+ options
891
+ };
892
+ };
893
+
894
+ // src/auth/social-providers/cognito-keys.ts
895
+ var getCognitoPublicKey = async (kid, region, userPoolId) => {
896
+ const jwksUri = `https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json`;
897
+ try {
898
+ return await getJwksPublicKey(jwksUri, kid);
899
+ } catch (error) {
900
+ logger.error("Failed to fetch Cognito public key:", error);
901
+ throw error;
902
+ }
903
+ };
904
+
905
+ // src/auth/social-providers/cognito.ts
906
+ var cognito = (options) => {
907
+ if (!options.domain || !options.region || !options.userPoolId) {
908
+ logger.error(
909
+ "Domain, region and userPoolId are required for Amazon Cognito. Make sure to provide them in the options."
910
+ );
911
+ throw new AthenaAuthError("DOMAIN_AND_REGION_REQUIRED");
912
+ }
913
+ const cleanDomain = options.domain.replace(/^https?:\/\//, "");
914
+ const authorizationEndpoint2 = `https://${cleanDomain}/oauth2/authorize`;
915
+ const tokenEndpoint2 = `https://${cleanDomain}/oauth2/token`;
916
+ const userInfoEndpoint = `https://${cleanDomain}/oauth2/userinfo`;
917
+ return {
918
+ id: "cognito",
919
+ name: "Cognito",
920
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
921
+ if (!getPrimaryClientId(options.clientId)) {
922
+ logger.error(
923
+ "ClientId is required for Amazon Cognito. Make sure to provide them in the options."
924
+ );
925
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
926
+ }
927
+ if (options.requireClientSecret && !options.clientSecret) {
928
+ logger.error(
929
+ "Client Secret is required when requireClientSecret is true. Make sure to provide it in the options."
930
+ );
931
+ throw new AthenaAuthError("CLIENT_SECRET_REQUIRED");
932
+ }
933
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile", "email"];
934
+ if (options.scope) _scopes.push(...options.scope);
935
+ if (scopes) _scopes.push(...scopes);
936
+ const url = await createAuthorizationURL({
937
+ id: "cognito",
938
+ options: {
939
+ ...options
940
+ },
941
+ authorizationEndpoint: authorizationEndpoint2,
942
+ scopes: _scopes,
943
+ state,
944
+ codeVerifier,
945
+ redirectURI,
946
+ prompt: options.prompt
947
+ });
948
+ const scopeValue = url.searchParams.get("scope");
949
+ if (scopeValue) {
950
+ url.searchParams.delete("scope");
951
+ const encodedScope = encodeURIComponent(scopeValue);
952
+ const urlString = url.toString();
953
+ const separator = urlString.includes("?") ? "&" : "?";
954
+ return new URL(`${urlString}${separator}scope=${encodedScope}`);
955
+ }
956
+ return url;
957
+ },
958
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
959
+ return validateAuthorizationCode({
960
+ code,
961
+ codeVerifier,
962
+ redirectURI,
963
+ options,
964
+ tokenEndpoint: tokenEndpoint2
965
+ });
966
+ },
967
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
968
+ return refreshAccessToken({
969
+ refreshToken,
970
+ options: {
971
+ clientId: options.clientId,
972
+ clientKey: options.clientKey,
973
+ clientSecret: options.clientSecret
974
+ },
975
+ tokenEndpoint: tokenEndpoint2
976
+ });
977
+ },
978
+ async verifyIdToken(token, nonce) {
979
+ if (options.disableIdTokenSignIn) {
980
+ return false;
981
+ }
982
+ if (options.verifyIdToken) {
983
+ return options.verifyIdToken(token, nonce);
984
+ }
985
+ try {
986
+ const decodedHeader = jose.decodeProtectedHeader(token);
987
+ const { kid, alg: jwtAlg } = decodedHeader;
988
+ if (!kid || !jwtAlg) return false;
989
+ const publicKey = await getCognitoPublicKey(
990
+ kid,
991
+ options.region,
992
+ options.userPoolId
993
+ );
994
+ const expectedIssuer = `https://cognito-idp.${options.region}.amazonaws.com/${options.userPoolId}`;
995
+ const { payload: jwtClaims } = await jose.jwtVerify(token, publicKey, {
996
+ algorithms: [jwtAlg],
997
+ issuer: expectedIssuer,
998
+ audience: options.clientId,
999
+ maxTokenAge: "1h"
1000
+ });
1001
+ if (nonce && jwtClaims.nonce !== nonce) {
1002
+ return false;
1003
+ }
1004
+ return true;
1005
+ } catch (error) {
1006
+ logger.error("Failed to verify ID token:", error);
1007
+ return false;
1008
+ }
1009
+ },
1010
+ async getUserInfo(token) {
1011
+ if (options.getUserInfo) {
1012
+ return options.getUserInfo(token);
1013
+ }
1014
+ if (token.idToken) {
1015
+ try {
1016
+ const profile = jose.decodeJwt(token.idToken);
1017
+ if (!profile) {
1018
+ return null;
1019
+ }
1020
+ const name = profile.name || profile.given_name || profile.username || "";
1021
+ const enrichedProfile = {
1022
+ ...profile,
1023
+ name
1024
+ };
1025
+ const userMap = await options.mapProfileToUser?.(enrichedProfile);
1026
+ return {
1027
+ user: {
1028
+ id: profile.sub,
1029
+ name: enrichedProfile.name,
1030
+ email: profile.email,
1031
+ image: profile.picture,
1032
+ emailVerified: profile.email_verified,
1033
+ ...userMap
1034
+ },
1035
+ data: enrichedProfile
1036
+ };
1037
+ } catch (error) {
1038
+ logger.error("Failed to decode ID token:", error);
1039
+ }
1040
+ }
1041
+ if (token.accessToken) {
1042
+ try {
1043
+ const { data: userInfo } = await athenaFetch(
1044
+ userInfoEndpoint,
1045
+ {
1046
+ headers: {
1047
+ Authorization: `Bearer ${token.accessToken}`
1048
+ }
1049
+ }
1050
+ );
1051
+ if (userInfo) {
1052
+ const userMap = await options.mapProfileToUser?.(userInfo);
1053
+ return {
1054
+ user: {
1055
+ id: userInfo.sub,
1056
+ name: userInfo.name || userInfo.given_name || userInfo.username || "",
1057
+ email: userInfo.email,
1058
+ image: userInfo.picture,
1059
+ emailVerified: userInfo.email_verified,
1060
+ ...userMap
1061
+ },
1062
+ data: userInfo
1063
+ };
1064
+ }
1065
+ } catch (error) {
1066
+ logger.error("Failed to fetch user info from Cognito:", error);
1067
+ }
1068
+ }
1069
+ return null;
1070
+ },
1071
+ options
1072
+ };
1073
+ };
1074
+
1075
+ // src/auth/social-providers/discord.ts
1076
+ var discord = (options) => {
1077
+ const tokenEndpoint2 = "https://discord.com/api/oauth2/token";
1078
+ return {
1079
+ id: "discord",
1080
+ name: "Discord",
1081
+ createAuthorizationURL({ state, scopes, redirectURI }) {
1082
+ const _scopes = options.disableDefaultScope ? [] : ["identify", "email"];
1083
+ if (scopes) _scopes.push(...scopes);
1084
+ if (options.scope) _scopes.push(...options.scope);
1085
+ const hasBotScope = _scopes.includes("bot");
1086
+ const permissionsParam = hasBotScope && options.permissions !== void 0 ? `&permissions=${options.permissions}` : "";
1087
+ return new URL(
1088
+ `https://discord.com/api/oauth2/authorize?scope=${_scopes.join(
1089
+ "+"
1090
+ )}&response_type=code&client_id=${options.clientId}&redirect_uri=${encodeURIComponent(
1091
+ options.redirectURI || redirectURI
1092
+ )}&state=${state}&prompt=${options.prompt || "none"}${permissionsParam}`
1093
+ );
1094
+ },
1095
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1096
+ return validateAuthorizationCode({
1097
+ code,
1098
+ redirectURI,
1099
+ options,
1100
+ tokenEndpoint: tokenEndpoint2
1101
+ });
1102
+ },
1103
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1104
+ return refreshAccessToken({
1105
+ refreshToken,
1106
+ options: {
1107
+ clientId: options.clientId,
1108
+ clientKey: options.clientKey,
1109
+ clientSecret: options.clientSecret
1110
+ },
1111
+ tokenEndpoint: tokenEndpoint2
1112
+ });
1113
+ },
1114
+ async getUserInfo(token) {
1115
+ if (options.getUserInfo) {
1116
+ return options.getUserInfo(token);
1117
+ }
1118
+ const { data: profile, error } = await athenaFetch(
1119
+ "https://discord.com/api/users/@me",
1120
+ {
1121
+ headers: {
1122
+ authorization: `Bearer ${token.accessToken}`
1123
+ }
1124
+ }
1125
+ );
1126
+ if (error) {
1127
+ return null;
1128
+ }
1129
+ if (profile.avatar === null) {
1130
+ const defaultAvatarNumber = profile.discriminator === "0" ? Number(BigInt(profile.id) >> BigInt(22)) % 6 : parseInt(profile.discriminator) % 5;
1131
+ profile.image_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNumber}.png`;
1132
+ } else {
1133
+ const format = profile.avatar.startsWith("a_") ? "gif" : "png";
1134
+ profile.image_url = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`;
1135
+ }
1136
+ const userMap = await options.mapProfileToUser?.(profile);
1137
+ return {
1138
+ user: {
1139
+ id: profile.id,
1140
+ name: profile.global_name || profile.username || "",
1141
+ email: profile.email,
1142
+ emailVerified: profile.verified,
1143
+ image: profile.image_url,
1144
+ ...userMap
1145
+ },
1146
+ data: profile
1147
+ };
1148
+ },
1149
+ options
1150
+ };
1151
+ };
1152
+
1153
+ // src/auth/social-providers/dropbox.ts
1154
+ var dropbox = (options) => {
1155
+ const tokenEndpoint2 = "https://api.dropboxapi.com/oauth2/token";
1156
+ return {
1157
+ id: "dropbox",
1158
+ name: "Dropbox",
1159
+ createAuthorizationURL: async ({
1160
+ state,
1161
+ scopes,
1162
+ codeVerifier,
1163
+ redirectURI
1164
+ }) => {
1165
+ const _scopes = options.disableDefaultScope ? [] : ["account_info.read"];
1166
+ if (options.scope) _scopes.push(...options.scope);
1167
+ if (scopes) _scopes.push(...scopes);
1168
+ const additionalParams = {};
1169
+ if (options.accessType) {
1170
+ additionalParams.token_access_type = options.accessType;
1171
+ }
1172
+ return await createAuthorizationURL({
1173
+ id: "dropbox",
1174
+ options,
1175
+ authorizationEndpoint: "https://www.dropbox.com/oauth2/authorize",
1176
+ scopes: _scopes,
1177
+ state,
1178
+ redirectURI,
1179
+ codeVerifier,
1180
+ additionalParams
1181
+ });
1182
+ },
1183
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1184
+ return await validateAuthorizationCode({
1185
+ code,
1186
+ codeVerifier,
1187
+ redirectURI,
1188
+ options,
1189
+ tokenEndpoint: tokenEndpoint2
1190
+ });
1191
+ },
1192
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1193
+ return refreshAccessToken({
1194
+ refreshToken,
1195
+ options: {
1196
+ clientId: options.clientId,
1197
+ clientKey: options.clientKey,
1198
+ clientSecret: options.clientSecret
1199
+ },
1200
+ tokenEndpoint: tokenEndpoint2
1201
+ });
1202
+ },
1203
+ async getUserInfo(token) {
1204
+ if (options.getUserInfo) {
1205
+ return options.getUserInfo(token);
1206
+ }
1207
+ const { data: profile, error } = await athenaFetch(
1208
+ "https://api.dropboxapi.com/2/users/get_current_account",
1209
+ {
1210
+ method: "POST",
1211
+ headers: {
1212
+ Authorization: `Bearer ${token.accessToken}`
1213
+ }
1214
+ }
1215
+ );
1216
+ if (error) {
1217
+ return null;
1218
+ }
1219
+ const userMap = await options.mapProfileToUser?.(profile);
1220
+ return {
1221
+ user: {
1222
+ id: profile.account_id,
1223
+ name: profile.name?.display_name,
1224
+ email: profile.email,
1225
+ emailVerified: profile.email_verified || false,
1226
+ image: profile.profile_photo_url,
1227
+ ...userMap
1228
+ },
1229
+ data: profile
1230
+ };
1231
+ },
1232
+ options
1233
+ };
1234
+ };
1235
+
1236
+ // src/auth/social-providers/facebook-verify.ts
1237
+ async function verifyFacebookAccessToken(accessToken, options) {
1238
+ const primaryClientId = getPrimaryClientId(options.clientId);
1239
+ if (!primaryClientId || !options.clientSecret) {
1240
+ return null;
1241
+ }
1242
+ const clientIds = Array.isArray(options.clientId) ? options.clientId : [options.clientId];
1243
+ const appAccessToken = `${primaryClientId}|${options.clientSecret}`;
1244
+ const { data, error } = await athenaFetch(
1245
+ "https://graph.facebook.com/debug_token",
1246
+ {
1247
+ query: {
1248
+ input_token: accessToken,
1249
+ access_token: appAccessToken
1250
+ }
1251
+ }
1252
+ );
1253
+ if (error || !data?.data) {
1254
+ return null;
1255
+ }
1256
+ const { is_valid, app_id, user_id } = data.data;
1257
+ if (is_valid !== true || !app_id || !clientIds.includes(app_id) || !user_id) {
1258
+ return null;
1259
+ }
1260
+ return user_id;
1261
+ }
1262
+
1263
+ // src/auth/social-providers/facebook.ts
1264
+ var facebook = (options) => {
1265
+ return {
1266
+ id: "facebook",
1267
+ name: "Facebook",
1268
+ async createAuthorizationURL({ state, scopes, redirectURI, loginHint }) {
1269
+ if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
1270
+ logger.error(
1271
+ "Client ID and client secret are required for Facebook. Make sure to provide them in the options."
1272
+ );
1273
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
1274
+ }
1275
+ const _scopes = options.disableDefaultScope ? [] : ["email", "public_profile"];
1276
+ if (options.scope) _scopes.push(...options.scope);
1277
+ if (scopes) _scopes.push(...scopes);
1278
+ return await createAuthorizationURL({
1279
+ id: "facebook",
1280
+ options,
1281
+ authorizationEndpoint: "https://www.facebook.com/v24.0/dialog/oauth",
1282
+ scopes: _scopes,
1283
+ state,
1284
+ redirectURI,
1285
+ loginHint,
1286
+ additionalParams: options.configId ? {
1287
+ config_id: options.configId
1288
+ } : {}
1289
+ });
1290
+ },
1291
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1292
+ return validateAuthorizationCode({
1293
+ code,
1294
+ redirectURI,
1295
+ options,
1296
+ tokenEndpoint: "https://graph.facebook.com/v24.0/oauth/access_token"
1297
+ });
1298
+ },
1299
+ async verifyIdToken(token, nonce) {
1300
+ if (options.disableIdTokenSignIn) {
1301
+ return false;
1302
+ }
1303
+ if (options.verifyIdToken) {
1304
+ return options.verifyIdToken(token, nonce);
1305
+ }
1306
+ if (token.split(".").length === 3) {
1307
+ try {
1308
+ const { payload: jwtClaims } = await jose.jwtVerify(
1309
+ token,
1310
+ jose.createRemoteJWKSet(
1311
+ // https://developers.facebook.com/docs/facebook-login/limited-login/token/#jwks
1312
+ new URL(
1313
+ "https://limited.facebook.com/.well-known/oauth/openid/jwks/"
1314
+ )
1315
+ ),
1316
+ {
1317
+ algorithms: ["RS256"],
1318
+ audience: options.clientId,
1319
+ issuer: "https://www.facebook.com"
1320
+ }
1321
+ );
1322
+ if (nonce && jwtClaims.nonce !== nonce) {
1323
+ return false;
1324
+ }
1325
+ return !!jwtClaims;
1326
+ } catch {
1327
+ return false;
1328
+ }
1329
+ }
1330
+ return await verifyFacebookAccessToken(token, options) !== null;
1331
+ },
1332
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1333
+ return refreshAccessToken({
1334
+ refreshToken,
1335
+ options: {
1336
+ clientId: options.clientId,
1337
+ clientKey: options.clientKey,
1338
+ clientSecret: options.clientSecret
1339
+ },
1340
+ tokenEndpoint: "https://graph.facebook.com/v24.0/oauth/access_token"
1341
+ });
1342
+ },
1343
+ async getUserInfo(token) {
1344
+ if (options.getUserInfo) {
1345
+ return options.getUserInfo(token);
1346
+ }
1347
+ if (token.idToken && token.idToken.split(".").length === 3) {
1348
+ const profile2 = jose.decodeJwt(token.idToken);
1349
+ const user = {
1350
+ id: profile2.sub,
1351
+ name: profile2.name,
1352
+ email: profile2.email,
1353
+ picture: {
1354
+ data: {
1355
+ url: profile2.picture,
1356
+ height: 100,
1357
+ width: 100,
1358
+ is_silhouette: false
1359
+ }
1360
+ }
1361
+ };
1362
+ const userMap2 = await options.mapProfileToUser?.({
1363
+ ...user,
1364
+ email_verified: false
1365
+ });
1366
+ return {
1367
+ user: {
1368
+ ...user,
1369
+ emailVerified: false,
1370
+ ...userMap2
1371
+ },
1372
+ data: profile2
1373
+ };
1374
+ }
1375
+ const accessToken = token.accessToken;
1376
+ if (!accessToken) {
1377
+ return null;
1378
+ }
1379
+ const tokenUserId = await verifyFacebookAccessToken(accessToken, options);
1380
+ if (!tokenUserId) {
1381
+ return null;
1382
+ }
1383
+ const fields = [
1384
+ "id",
1385
+ "name",
1386
+ "email",
1387
+ "picture",
1388
+ ...options?.fields || []
1389
+ ];
1390
+ const { data: profile, error } = await athenaFetch(
1391
+ "https://graph.facebook.com/me?fields=" + fields.join(","),
1392
+ {
1393
+ auth: {
1394
+ type: "Bearer",
1395
+ token: accessToken
1396
+ }
1397
+ }
1398
+ );
1399
+ if (error) {
1400
+ return null;
1401
+ }
1402
+ if (profile.id !== tokenUserId) {
1403
+ return null;
1404
+ }
1405
+ const userMap = await options.mapProfileToUser?.(profile);
1406
+ return {
1407
+ user: {
1408
+ id: profile.id,
1409
+ name: profile.name,
1410
+ email: profile.email,
1411
+ image: profile.picture.data.url,
1412
+ emailVerified: profile.email_verified ?? false,
1413
+ ...userMap
1414
+ },
1415
+ data: profile
1416
+ };
1417
+ },
1418
+ options
1419
+ };
1420
+ };
1421
+
1422
+ // src/auth/social-providers/figma.ts
1423
+ var figma = (options) => {
1424
+ const tokenEndpoint2 = "https://api.figma.com/v1/oauth/token";
1425
+ return {
1426
+ id: "figma",
1427
+ name: "Figma",
1428
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
1429
+ if (!options.clientId || !options.clientSecret) {
1430
+ logger.error(
1431
+ "Client Id and Client Secret are required for Figma. Make sure to provide them in the options."
1432
+ );
1433
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
1434
+ }
1435
+ if (!codeVerifier) {
1436
+ throw new AthenaAuthError("codeVerifier is required for Figma");
1437
+ }
1438
+ const _scopes = options.disableDefaultScope ? [] : ["current_user:read"];
1439
+ if (options.scope) _scopes.push(...options.scope);
1440
+ if (scopes) _scopes.push(...scopes);
1441
+ const url = await createAuthorizationURL({
1442
+ id: "figma",
1443
+ options,
1444
+ authorizationEndpoint: "https://www.figma.com/oauth",
1445
+ scopes: _scopes,
1446
+ state,
1447
+ codeVerifier,
1448
+ redirectURI
1449
+ });
1450
+ return url;
1451
+ },
1452
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1453
+ return validateAuthorizationCode({
1454
+ code,
1455
+ codeVerifier,
1456
+ redirectURI,
1457
+ options,
1458
+ tokenEndpoint: tokenEndpoint2,
1459
+ authentication: "basic"
1460
+ });
1461
+ },
1462
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1463
+ return refreshAccessToken({
1464
+ refreshToken,
1465
+ options: {
1466
+ clientId: options.clientId,
1467
+ clientKey: options.clientKey,
1468
+ clientSecret: options.clientSecret
1469
+ },
1470
+ tokenEndpoint: tokenEndpoint2,
1471
+ authentication: "basic"
1472
+ });
1473
+ },
1474
+ async getUserInfo(token) {
1475
+ if (options.getUserInfo) {
1476
+ return options.getUserInfo(token);
1477
+ }
1478
+ try {
1479
+ const { data: profile } = await athenaFetch(
1480
+ "https://api.figma.com/v1/me",
1481
+ {
1482
+ headers: {
1483
+ Authorization: `Bearer ${token.accessToken}`
1484
+ }
1485
+ }
1486
+ );
1487
+ if (!profile) {
1488
+ logger.error("Failed to fetch user from Figma");
1489
+ return null;
1490
+ }
1491
+ const userMap = await options.mapProfileToUser?.(profile);
1492
+ return {
1493
+ user: {
1494
+ id: profile.id,
1495
+ name: profile.handle,
1496
+ email: profile.email,
1497
+ image: profile.img_url,
1498
+ emailVerified: false,
1499
+ ...userMap
1500
+ },
1501
+ data: profile
1502
+ };
1503
+ } catch (error) {
1504
+ logger.error("Failed to fetch user info from Figma:", error);
1505
+ return null;
1506
+ }
1507
+ },
1508
+ options
1509
+ };
1510
+ };
1511
+
1512
+ // src/auth/social-providers/github.ts
1513
+ var github = (options) => {
1514
+ const tokenEndpoint2 = "https://github.com/login/oauth/access_token";
1515
+ return {
1516
+ id: "github",
1517
+ name: "GitHub",
1518
+ createAuthorizationURL({
1519
+ state,
1520
+ scopes,
1521
+ loginHint,
1522
+ codeVerifier,
1523
+ redirectURI
1524
+ }) {
1525
+ const _scopes = options.disableDefaultScope ? [] : ["read:user", "user:email"];
1526
+ if (options.scope) _scopes.push(...options.scope);
1527
+ if (scopes) _scopes.push(...scopes);
1528
+ return createAuthorizationURL({
1529
+ id: "github",
1530
+ options,
1531
+ authorizationEndpoint: "https://github.com/login/oauth/authorize",
1532
+ scopes: _scopes,
1533
+ state,
1534
+ codeVerifier,
1535
+ redirectURI,
1536
+ loginHint,
1537
+ prompt: options.prompt
1538
+ });
1539
+ },
1540
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1541
+ const { body, headers: requestHeaders } = createAuthorizationCodeRequest({
1542
+ code,
1543
+ codeVerifier,
1544
+ redirectURI,
1545
+ options
1546
+ });
1547
+ const { data, error } = await athenaFetch(tokenEndpoint2, {
1548
+ method: "POST",
1549
+ body,
1550
+ headers: requestHeaders
1551
+ });
1552
+ if (error) {
1553
+ logger.error("GitHub OAuth token exchange failed:", error);
1554
+ return null;
1555
+ }
1556
+ if ("error" in data) {
1557
+ logger.error("GitHub OAuth token exchange failed:", data);
1558
+ return null;
1559
+ }
1560
+ return getOAuth2Tokens(data);
1561
+ },
1562
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1563
+ return refreshAccessToken({
1564
+ refreshToken,
1565
+ options: {
1566
+ clientId: options.clientId,
1567
+ clientKey: options.clientKey,
1568
+ clientSecret: options.clientSecret
1569
+ },
1570
+ tokenEndpoint: tokenEndpoint2
1571
+ });
1572
+ },
1573
+ async getUserInfo(token) {
1574
+ if (options.getUserInfo) {
1575
+ return options.getUserInfo(token);
1576
+ }
1577
+ const { data: profile, error } = await athenaFetch(
1578
+ "https://api.github.com/user",
1579
+ {
1580
+ headers: {
1581
+ "User-Agent": "athena-auth",
1582
+ authorization: `Bearer ${token.accessToken}`
1583
+ }
1584
+ }
1585
+ );
1586
+ if (error) {
1587
+ return null;
1588
+ }
1589
+ const { data: emails } = await athenaFetch("https://api.github.com/user/emails", {
1590
+ headers: {
1591
+ Authorization: `Bearer ${token.accessToken}`,
1592
+ "User-Agent": "athena-auth"
1593
+ }
1594
+ });
1595
+ if (!profile.email && emails) {
1596
+ profile.email = (emails.find((e) => e.primary) ?? emails[0])?.email;
1597
+ }
1598
+ const emailVerified = emails?.find((e) => e.email === profile.email)?.verified ?? false;
1599
+ const userMap = await options.mapProfileToUser?.(profile);
1600
+ return {
1601
+ user: {
1602
+ id: profile.id,
1603
+ name: profile.name || profile.login || "",
1604
+ email: profile.email,
1605
+ image: profile.avatar_url,
1606
+ emailVerified,
1607
+ ...userMap
1608
+ },
1609
+ data: profile
1610
+ };
1611
+ },
1612
+ options
1613
+ };
1614
+ };
1615
+
1616
+ // src/auth/social-providers/gitlab.ts
1617
+ var cleanDoubleSlashes = (input = "") => {
1618
+ return input.split("://").map((str) => str.replace(/\/{2,}/g, "/")).join("://");
1619
+ };
1620
+ var issuerToEndpoints = (issuer) => {
1621
+ const baseUrl = issuer || "https://gitlab.com";
1622
+ return {
1623
+ authorizationEndpoint: cleanDoubleSlashes(`${baseUrl}/oauth/authorize`),
1624
+ tokenEndpoint: cleanDoubleSlashes(`${baseUrl}/oauth/token`),
1625
+ userinfoEndpoint: cleanDoubleSlashes(`${baseUrl}/api/v4/user`)
1626
+ };
1627
+ };
1628
+ var gitlab = (options) => {
1629
+ const { authorizationEndpoint: authorizationEndpoint2, tokenEndpoint: tokenEndpoint2, userinfoEndpoint: userinfoEndpoint2 } = issuerToEndpoints(options.issuer);
1630
+ const issuerId = "gitlab";
1631
+ const issuerName = "Gitlab";
1632
+ return {
1633
+ id: issuerId,
1634
+ name: issuerName,
1635
+ createAuthorizationURL: async ({
1636
+ state,
1637
+ scopes,
1638
+ codeVerifier,
1639
+ loginHint,
1640
+ redirectURI
1641
+ }) => {
1642
+ const _scopes = options.disableDefaultScope ? [] : ["read_user"];
1643
+ if (options.scope) _scopes.push(...options.scope);
1644
+ if (scopes) _scopes.push(...scopes);
1645
+ return await createAuthorizationURL({
1646
+ id: issuerId,
1647
+ options,
1648
+ authorizationEndpoint: authorizationEndpoint2,
1649
+ scopes: _scopes,
1650
+ state,
1651
+ redirectURI,
1652
+ codeVerifier,
1653
+ loginHint
1654
+ });
1655
+ },
1656
+ validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
1657
+ return validateAuthorizationCode({
1658
+ code,
1659
+ redirectURI,
1660
+ options,
1661
+ codeVerifier,
1662
+ tokenEndpoint: tokenEndpoint2
1663
+ });
1664
+ },
1665
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1666
+ return refreshAccessToken({
1667
+ refreshToken,
1668
+ options: {
1669
+ clientId: options.clientId,
1670
+ clientKey: options.clientKey,
1671
+ clientSecret: options.clientSecret
1672
+ },
1673
+ tokenEndpoint: tokenEndpoint2
1674
+ });
1675
+ },
1676
+ async getUserInfo(token) {
1677
+ if (options.getUserInfo) {
1678
+ return options.getUserInfo(token);
1679
+ }
1680
+ const { data: profile, error } = await athenaFetch(
1681
+ userinfoEndpoint2,
1682
+ { headers: { authorization: `Bearer ${token.accessToken}` } }
1683
+ );
1684
+ if (error || profile.state !== "active" || profile.locked) {
1685
+ return null;
1686
+ }
1687
+ const userMap = await options.mapProfileToUser?.(profile);
1688
+ return {
1689
+ user: {
1690
+ id: profile.id,
1691
+ name: profile.name ?? profile.username ?? "",
1692
+ email: profile.email,
1693
+ image: profile.avatar_url,
1694
+ emailVerified: profile.email_verified ?? false,
1695
+ ...userMap
1696
+ },
1697
+ data: profile
1698
+ };
1699
+ },
1700
+ options
1701
+ };
1702
+ };
1703
+
1704
+ // src/auth/social-providers/google-types.ts
1705
+ var GOOGLE_ID_TOKEN_MAX_AGE = "1h";
1706
+
1707
+ // src/auth/social-providers/google-keys.ts
1708
+ var getGooglePublicKey = async (kid) => {
1709
+ return getJwksPublicKey("https://www.googleapis.com/oauth2/v3/certs", kid);
1710
+ };
1711
+
1712
+ // src/auth/social-providers/google-verify.ts
1713
+ var verifyGoogleIdToken = async ({
1714
+ token,
1715
+ audience,
1716
+ nonce
1717
+ }) => {
1718
+ try {
1719
+ const { kid, alg: jwtAlg } = jose.decodeProtectedHeader(token);
1720
+ if (!kid || !jwtAlg) return null;
1721
+ const publicKey = await getGooglePublicKey(kid);
1722
+ const { payload: jwtClaims } = await jose.jwtVerify(token, publicKey, {
1723
+ algorithms: [jwtAlg],
1724
+ issuer: ["https://accounts.google.com", "accounts.google.com"],
1725
+ audience,
1726
+ maxTokenAge: GOOGLE_ID_TOKEN_MAX_AGE
1727
+ });
1728
+ if (nonce && jwtClaims.nonce !== nonce) {
1729
+ return null;
1730
+ }
1731
+ return jwtClaims;
1732
+ } catch {
1733
+ return null;
1734
+ }
1735
+ };
1736
+ var isGoogleHostedDomainAllowed = (configuredHostedDomain, tokenHostedDomain) => {
1737
+ if (!configuredHostedDomain) return true;
1738
+ if (typeof tokenHostedDomain !== "string" || !tokenHostedDomain) {
1739
+ return false;
1740
+ }
1741
+ if (configuredHostedDomain === "*") return true;
1742
+ return tokenHostedDomain === configuredHostedDomain;
1743
+ };
1744
+
1745
+ // src/auth/social-providers/helpers/merge-scopes.ts
1746
+ function mergeScopes(defaults, optionScopes, requestScopes, disableDefaultScope) {
1747
+ const scopes = disableDefaultScope ? [] : [...defaults];
1748
+ if (optionScopes) scopes.push(...optionScopes);
1749
+ if (requestScopes) scopes.push(...requestScopes);
1750
+ return [...new Set(scopes)];
1751
+ }
1752
+
1753
+ // src/auth/social-providers/google.ts
1754
+ var google = (options) => {
1755
+ return {
1756
+ id: "google",
1757
+ name: "Google",
1758
+ async createAuthorizationURL({
1759
+ state,
1760
+ scopes,
1761
+ codeVerifier,
1762
+ redirectURI,
1763
+ loginHint,
1764
+ display
1765
+ }) {
1766
+ if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
1767
+ logger.error(
1768
+ "Client Id and Client Secret is required for Google. Make sure to provide them in the options."
1769
+ );
1770
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
1771
+ }
1772
+ if (!codeVerifier) {
1773
+ throw new AthenaAuthError("codeVerifier is required for Google");
1774
+ }
1775
+ const _scopes = mergeScopes(
1776
+ ["email", "profile", "openid"],
1777
+ options.scope,
1778
+ scopes,
1779
+ options.disableDefaultScope
1780
+ );
1781
+ const url = await createAuthorizationURL({
1782
+ id: "google",
1783
+ options,
1784
+ authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
1785
+ scopes: _scopes,
1786
+ state,
1787
+ codeVerifier,
1788
+ redirectURI,
1789
+ prompt: options.prompt,
1790
+ accessType: options.accessType,
1791
+ display: display || options.display,
1792
+ loginHint,
1793
+ hd: options.hd,
1794
+ additionalParams: {
1795
+ include_granted_scopes: "true"
1796
+ }
1797
+ });
1798
+ return url;
1799
+ },
1800
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1801
+ return validateAuthorizationCode({
1802
+ code,
1803
+ codeVerifier,
1804
+ redirectURI,
1805
+ options,
1806
+ tokenEndpoint: "https://oauth2.googleapis.com/token"
1807
+ });
1808
+ },
1809
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1810
+ return refreshAccessToken({
1811
+ refreshToken,
1812
+ options: {
1813
+ clientId: options.clientId,
1814
+ clientKey: options.clientKey,
1815
+ clientSecret: options.clientSecret
1816
+ },
1817
+ tokenEndpoint: "https://oauth2.googleapis.com/token"
1818
+ });
1819
+ },
1820
+ async verifyIdToken(token, nonce) {
1821
+ if (options.disableIdTokenSignIn) {
1822
+ return false;
1823
+ }
1824
+ if (options.verifyIdToken) {
1825
+ return options.verifyIdToken(token, nonce);
1826
+ }
1827
+ const jwtClaims = await verifyGoogleIdToken({
1828
+ token,
1829
+ audience: options.clientId,
1830
+ nonce
1831
+ });
1832
+ if (!jwtClaims) {
1833
+ return false;
1834
+ }
1835
+ return isGoogleHostedDomainAllowed(options.hd, jwtClaims.hd);
1836
+ },
1837
+ async getUserInfo(token) {
1838
+ if (options.getUserInfo) {
1839
+ return options.getUserInfo(token);
1840
+ }
1841
+ if (!token.idToken) {
1842
+ return null;
1843
+ }
1844
+ const user = jose.decodeJwt(token.idToken);
1845
+ if (!isGoogleHostedDomainAllowed(options.hd, user.hd)) {
1846
+ logger.error(
1847
+ `Google sign-in rejected: id token hosted domain (hd) "${user.hd ?? "<missing>"}" does not satisfy the configured "hd" option "${options.hd}".`
1848
+ );
1849
+ return null;
1850
+ }
1851
+ const userMap = await options.mapProfileToUser?.(user);
1852
+ return {
1853
+ user: {
1854
+ id: user.sub,
1855
+ name: user.name,
1856
+ email: user.email,
1857
+ image: user.picture,
1858
+ emailVerified: user.email_verified,
1859
+ ...userMap
1860
+ },
1861
+ data: user
1862
+ };
1863
+ },
1864
+ options
1865
+ };
1866
+ };
1867
+
1868
+ // src/auth/social-providers/huggingface.ts
1869
+ var huggingface = (options) => {
1870
+ const tokenEndpoint2 = "https://huggingface.co/oauth/token";
1871
+ return {
1872
+ id: "huggingface",
1873
+ name: "Hugging Face",
1874
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
1875
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile", "email"];
1876
+ if (options.scope) _scopes.push(...options.scope);
1877
+ if (scopes) _scopes.push(...scopes);
1878
+ return createAuthorizationURL({
1879
+ id: "huggingface",
1880
+ options,
1881
+ authorizationEndpoint: "https://huggingface.co/oauth/authorize",
1882
+ scopes: _scopes,
1883
+ state,
1884
+ codeVerifier,
1885
+ redirectURI
1886
+ });
1887
+ },
1888
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
1889
+ return validateAuthorizationCode({
1890
+ code,
1891
+ codeVerifier,
1892
+ redirectURI,
1893
+ options,
1894
+ tokenEndpoint: tokenEndpoint2
1895
+ });
1896
+ },
1897
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1898
+ return refreshAccessToken({
1899
+ refreshToken,
1900
+ options: {
1901
+ clientId: options.clientId,
1902
+ clientKey: options.clientKey,
1903
+ clientSecret: options.clientSecret
1904
+ },
1905
+ tokenEndpoint: tokenEndpoint2
1906
+ });
1907
+ },
1908
+ async getUserInfo(token) {
1909
+ if (options.getUserInfo) {
1910
+ return options.getUserInfo(token);
1911
+ }
1912
+ const { data: profile, error } = await athenaFetch(
1913
+ "https://huggingface.co/oauth/userinfo",
1914
+ {
1915
+ method: "GET",
1916
+ headers: {
1917
+ Authorization: `Bearer ${token.accessToken}`
1918
+ }
1919
+ }
1920
+ );
1921
+ if (error) {
1922
+ return null;
1923
+ }
1924
+ const userMap = await options.mapProfileToUser?.(profile);
1925
+ return {
1926
+ user: {
1927
+ id: profile.sub,
1928
+ name: profile.name || profile.preferred_username || "",
1929
+ email: profile.email,
1930
+ image: profile.picture,
1931
+ emailVerified: profile.email_verified ?? false,
1932
+ ...userMap
1933
+ },
1934
+ data: profile
1935
+ };
1936
+ },
1937
+ options
1938
+ };
1939
+ };
1940
+
1941
+ // src/auth/social-providers/kakao.ts
1942
+ var kakao = (options) => {
1943
+ const tokenEndpoint2 = "https://kauth.kakao.com/oauth/token";
1944
+ return {
1945
+ id: "kakao",
1946
+ name: "Kakao",
1947
+ createAuthorizationURL({ state, scopes, redirectURI }) {
1948
+ const _scopes = options.disableDefaultScope ? [] : ["account_email", "profile_image", "profile_nickname"];
1949
+ if (options.scope) _scopes.push(...options.scope);
1950
+ if (scopes) _scopes.push(...scopes);
1951
+ return createAuthorizationURL({
1952
+ id: "kakao",
1953
+ options,
1954
+ authorizationEndpoint: "https://kauth.kakao.com/oauth/authorize",
1955
+ scopes: _scopes,
1956
+ state,
1957
+ redirectURI
1958
+ });
1959
+ },
1960
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
1961
+ return validateAuthorizationCode({
1962
+ code,
1963
+ redirectURI,
1964
+ options,
1965
+ tokenEndpoint: tokenEndpoint2
1966
+ });
1967
+ },
1968
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
1969
+ return refreshAccessToken({
1970
+ refreshToken,
1971
+ options: {
1972
+ clientId: options.clientId,
1973
+ clientKey: options.clientKey,
1974
+ clientSecret: options.clientSecret
1975
+ },
1976
+ tokenEndpoint: tokenEndpoint2
1977
+ });
1978
+ },
1979
+ async getUserInfo(token) {
1980
+ if (options.getUserInfo) {
1981
+ return options.getUserInfo(token);
1982
+ }
1983
+ const { data: profile, error } = await athenaFetch(
1984
+ "https://kapi.kakao.com/v2/user/me",
1985
+ {
1986
+ headers: {
1987
+ Authorization: `Bearer ${token.accessToken}`
1988
+ }
1989
+ }
1990
+ );
1991
+ if (error || !profile) {
1992
+ return null;
1993
+ }
1994
+ const userMap = await options.mapProfileToUser?.(profile);
1995
+ const account = profile.kakao_account || {};
1996
+ const kakaoProfile = account.profile || {};
1997
+ const user = {
1998
+ id: String(profile.id),
1999
+ name: kakaoProfile.nickname || account.name || "",
2000
+ email: account.email,
2001
+ image: kakaoProfile.profile_image_url || kakaoProfile.thumbnail_image_url,
2002
+ emailVerified: !!account.is_email_valid && !!account.is_email_verified,
2003
+ ...userMap
2004
+ };
2005
+ return {
2006
+ user,
2007
+ data: profile
2008
+ };
2009
+ },
2010
+ options
2011
+ };
2012
+ };
2013
+
2014
+ // src/auth/social-providers/kick.ts
2015
+ var kick = (options) => {
2016
+ return {
2017
+ id: "kick",
2018
+ name: "Kick",
2019
+ createAuthorizationURL({ state, scopes, redirectURI, codeVerifier }) {
2020
+ const _scopes = options.disableDefaultScope ? [] : ["user:read"];
2021
+ if (options.scope) _scopes.push(...options.scope);
2022
+ if (scopes) _scopes.push(...scopes);
2023
+ return createAuthorizationURL({
2024
+ id: "kick",
2025
+ redirectURI,
2026
+ options,
2027
+ authorizationEndpoint: "https://id.kick.com/oauth/authorize",
2028
+ scopes: _scopes,
2029
+ codeVerifier,
2030
+ state
2031
+ });
2032
+ },
2033
+ async validateAuthorizationCode({ code, redirectURI, codeVerifier }) {
2034
+ return validateAuthorizationCode({
2035
+ code,
2036
+ redirectURI,
2037
+ options,
2038
+ tokenEndpoint: "https://id.kick.com/oauth/token",
2039
+ codeVerifier
2040
+ });
2041
+ },
2042
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2043
+ return refreshAccessToken({
2044
+ refreshToken,
2045
+ options: {
2046
+ clientId: options.clientId,
2047
+ clientSecret: options.clientSecret
2048
+ },
2049
+ tokenEndpoint: "https://id.kick.com/oauth/token"
2050
+ });
2051
+ },
2052
+ async getUserInfo(token) {
2053
+ if (options.getUserInfo) {
2054
+ return options.getUserInfo(token);
2055
+ }
2056
+ const { data, error } = await athenaFetch("https://api.kick.com/public/v1/users", {
2057
+ method: "GET",
2058
+ headers: {
2059
+ Authorization: `Bearer ${token.accessToken}`
2060
+ }
2061
+ });
2062
+ if (error) {
2063
+ return null;
2064
+ }
2065
+ const profile = data.data[0];
2066
+ const userMap = await options.mapProfileToUser?.(profile);
2067
+ return {
2068
+ user: {
2069
+ id: profile.user_id,
2070
+ name: profile.name,
2071
+ email: profile.email,
2072
+ image: profile.profile_picture,
2073
+ emailVerified: false,
2074
+ ...userMap
2075
+ },
2076
+ data: profile
2077
+ };
2078
+ },
2079
+ options
2080
+ };
2081
+ };
2082
+ var line = (options) => {
2083
+ const authorizationEndpoint2 = "https://access.line.me/oauth2/v2.1/authorize";
2084
+ const tokenEndpoint2 = "https://api.line.me/oauth2/v2.1/token";
2085
+ const userInfoEndpoint = "https://api.line.me/oauth2/v2.1/userinfo";
2086
+ const verifyIdTokenEndpoint = "https://api.line.me/oauth2/v2.1/verify";
2087
+ return {
2088
+ id: "line",
2089
+ name: "LINE",
2090
+ async createAuthorizationURL({
2091
+ state,
2092
+ scopes,
2093
+ codeVerifier,
2094
+ redirectURI,
2095
+ loginHint
2096
+ }) {
2097
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile", "email"];
2098
+ if (options.scope) _scopes.push(...options.scope);
2099
+ if (scopes) _scopes.push(...scopes);
2100
+ return await createAuthorizationURL({
2101
+ id: "line",
2102
+ options,
2103
+ authorizationEndpoint: authorizationEndpoint2,
2104
+ scopes: _scopes,
2105
+ state,
2106
+ codeVerifier,
2107
+ redirectURI,
2108
+ loginHint
2109
+ });
2110
+ },
2111
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
2112
+ return validateAuthorizationCode({
2113
+ code,
2114
+ codeVerifier,
2115
+ redirectURI,
2116
+ options,
2117
+ tokenEndpoint: tokenEndpoint2
2118
+ });
2119
+ },
2120
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2121
+ return refreshAccessToken({
2122
+ refreshToken,
2123
+ options: {
2124
+ clientId: options.clientId,
2125
+ clientSecret: options.clientSecret
2126
+ },
2127
+ tokenEndpoint: tokenEndpoint2
2128
+ });
2129
+ },
2130
+ async verifyIdToken(token, nonce) {
2131
+ if (options.disableIdTokenSignIn) {
2132
+ return false;
2133
+ }
2134
+ if (options.verifyIdToken) {
2135
+ return options.verifyIdToken(token, nonce);
2136
+ }
2137
+ const body = new URLSearchParams();
2138
+ body.set("id_token", token);
2139
+ body.set("client_id", options.clientId);
2140
+ if (nonce) body.set("nonce", nonce);
2141
+ const { data, error } = await athenaFetch(
2142
+ verifyIdTokenEndpoint,
2143
+ {
2144
+ method: "POST",
2145
+ headers: {
2146
+ "content-type": "application/x-www-form-urlencoded"
2147
+ },
2148
+ body
2149
+ }
2150
+ );
2151
+ if (error || !data) {
2152
+ return false;
2153
+ }
2154
+ if (data.aud !== options.clientId) return false;
2155
+ if (data.nonce && data.nonce !== nonce) return false;
2156
+ return true;
2157
+ },
2158
+ async getUserInfo(token) {
2159
+ if (options.getUserInfo) {
2160
+ return options.getUserInfo(token);
2161
+ }
2162
+ let profile = null;
2163
+ if (token.idToken) {
2164
+ try {
2165
+ profile = jose.decodeJwt(token.idToken);
2166
+ } catch {
2167
+ }
2168
+ }
2169
+ if (!profile) {
2170
+ const { data } = await athenaFetch(userInfoEndpoint, {
2171
+ headers: {
2172
+ authorization: `Bearer ${token.accessToken}`
2173
+ }
2174
+ });
2175
+ profile = data || null;
2176
+ }
2177
+ if (!profile) return null;
2178
+ const userMap = await options.mapProfileToUser?.(profile);
2179
+ const id = profile.sub || profile.userId;
2180
+ const name = profile.name || profile.displayName || "";
2181
+ const image = profile.picture || profile.pictureUrl || void 0;
2182
+ const email = profile.email;
2183
+ return {
2184
+ user: {
2185
+ id,
2186
+ name,
2187
+ email,
2188
+ image,
2189
+ // LINE does not expose email verification status in ID token/userinfo
2190
+ emailVerified: false,
2191
+ ...userMap
2192
+ },
2193
+ data: profile
2194
+ };
2195
+ },
2196
+ options
2197
+ };
2198
+ };
2199
+
2200
+ // src/auth/social-providers/linear.ts
2201
+ var linear = (options) => {
2202
+ const tokenEndpoint2 = "https://api.linear.app/oauth/token";
2203
+ return {
2204
+ id: "linear",
2205
+ name: "Linear",
2206
+ createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
2207
+ const _scopes = options.disableDefaultScope ? [] : ["read"];
2208
+ if (options.scope) _scopes.push(...options.scope);
2209
+ if (scopes) _scopes.push(...scopes);
2210
+ return createAuthorizationURL({
2211
+ id: "linear",
2212
+ options,
2213
+ authorizationEndpoint: "https://linear.app/oauth/authorize",
2214
+ scopes: _scopes,
2215
+ state,
2216
+ redirectURI,
2217
+ loginHint
2218
+ });
2219
+ },
2220
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2221
+ return validateAuthorizationCode({
2222
+ code,
2223
+ redirectURI,
2224
+ options,
2225
+ tokenEndpoint: tokenEndpoint2
2226
+ });
2227
+ },
2228
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2229
+ return refreshAccessToken({
2230
+ refreshToken,
2231
+ options: {
2232
+ clientId: options.clientId,
2233
+ clientKey: options.clientKey,
2234
+ clientSecret: options.clientSecret
2235
+ },
2236
+ tokenEndpoint: tokenEndpoint2
2237
+ });
2238
+ },
2239
+ async getUserInfo(token) {
2240
+ if (options.getUserInfo) {
2241
+ return options.getUserInfo(token);
2242
+ }
2243
+ const { data: profile, error } = await athenaFetch(
2244
+ "https://api.linear.app/graphql",
2245
+ {
2246
+ method: "POST",
2247
+ headers: {
2248
+ "Content-Type": "application/json",
2249
+ Authorization: `Bearer ${token.accessToken}`
2250
+ },
2251
+ body: JSON.stringify({
2252
+ query: `
2253
+ query {
2254
+ viewer {
2255
+ id
2256
+ name
2257
+ email
2258
+ avatarUrl
2259
+ active
2260
+ createdAt
2261
+ updatedAt
2262
+ }
2263
+ }
2264
+ `
2265
+ })
2266
+ }
2267
+ );
2268
+ if (error || !profile?.data?.viewer) {
2269
+ return null;
2270
+ }
2271
+ const userData = profile.data.viewer;
2272
+ const userMap = await options.mapProfileToUser?.(userData);
2273
+ return {
2274
+ user: {
2275
+ id: profile.data.viewer.id,
2276
+ name: profile.data.viewer.name,
2277
+ email: profile.data.viewer.email,
2278
+ image: profile.data.viewer.avatarUrl,
2279
+ emailVerified: false,
2280
+ ...userMap
2281
+ },
2282
+ data: userData
2283
+ };
2284
+ },
2285
+ options
2286
+ };
2287
+ };
2288
+
2289
+ // src/auth/social-providers/linkedin.ts
2290
+ var linkedin = (options) => {
2291
+ const authorizationEndpoint2 = "https://www.linkedin.com/oauth/v2/authorization";
2292
+ const tokenEndpoint2 = "https://www.linkedin.com/oauth/v2/accessToken";
2293
+ return {
2294
+ id: "linkedin",
2295
+ name: "Linkedin",
2296
+ createAuthorizationURL: async ({
2297
+ state,
2298
+ scopes,
2299
+ redirectURI,
2300
+ loginHint
2301
+ }) => {
2302
+ const _scopes = options.disableDefaultScope ? [] : ["profile", "email", "openid"];
2303
+ if (options.scope) _scopes.push(...options.scope);
2304
+ if (scopes) _scopes.push(...scopes);
2305
+ return await createAuthorizationURL({
2306
+ id: "linkedin",
2307
+ options,
2308
+ authorizationEndpoint: authorizationEndpoint2,
2309
+ scopes: _scopes,
2310
+ state,
2311
+ loginHint,
2312
+ redirectURI
2313
+ });
2314
+ },
2315
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2316
+ return await validateAuthorizationCode({
2317
+ code,
2318
+ redirectURI,
2319
+ options,
2320
+ tokenEndpoint: tokenEndpoint2
2321
+ });
2322
+ },
2323
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2324
+ return refreshAccessToken({
2325
+ refreshToken,
2326
+ options: {
2327
+ clientId: options.clientId,
2328
+ clientKey: options.clientKey,
2329
+ clientSecret: options.clientSecret
2330
+ },
2331
+ tokenEndpoint: tokenEndpoint2
2332
+ });
2333
+ },
2334
+ async getUserInfo(token) {
2335
+ if (options.getUserInfo) {
2336
+ return options.getUserInfo(token);
2337
+ }
2338
+ const { data: profile, error } = await athenaFetch(
2339
+ "https://api.linkedin.com/v2/userinfo",
2340
+ {
2341
+ method: "GET",
2342
+ headers: {
2343
+ Authorization: `Bearer ${token.accessToken}`
2344
+ }
2345
+ }
2346
+ );
2347
+ if (error) {
2348
+ return null;
2349
+ }
2350
+ const userMap = await options.mapProfileToUser?.(profile);
2351
+ return {
2352
+ user: {
2353
+ id: profile.sub,
2354
+ name: profile.name,
2355
+ email: profile.email,
2356
+ emailVerified: profile.email_verified ?? false,
2357
+ image: profile.picture,
2358
+ ...userMap
2359
+ },
2360
+ data: profile
2361
+ };
2362
+ },
2363
+ options
2364
+ };
2365
+ };
2366
+
2367
+ // src/auth/social-providers/microsoft-keys.ts
2368
+ var getMicrosoftPublicKey = async (kid, tenant, authority) => {
2369
+ return getJwksPublicKey(
2370
+ `${authority}/${tenant}/discovery/v2.0/keys`,
2371
+ kid
2372
+ );
2373
+ };
2374
+
2375
+ // src/auth/social-providers/microsoft-types.ts
2376
+ var MICROSOFT_CONSUMER_TENANT_ID = "9188040d-6c67-4c5b-b112-36a304b66dad";
2377
+
2378
+ // src/auth/social-providers/microsoft-entra-id.ts
2379
+ var microsoft = (options) => {
2380
+ const tenant = options.tenantId || "common";
2381
+ const authority = trimTrailingSlash(
2382
+ options.authority || "https://login.microsoftonline.com"
2383
+ );
2384
+ const authorizationEndpoint2 = `${authority}/${tenant}/oauth2/v2.0/authorize`;
2385
+ const tokenEndpoint2 = `${authority}/${tenant}/oauth2/v2.0/token`;
2386
+ return {
2387
+ id: "microsoft",
2388
+ name: "Microsoft EntraID",
2389
+ createAuthorizationURL(data) {
2390
+ if (!getPrimaryClientId(options.clientId)) {
2391
+ logger.error(
2392
+ "Client Id is required for Microsoft Entra ID. Make sure to provide it in the options."
2393
+ );
2394
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
2395
+ }
2396
+ const scopes = options.disableDefaultScope ? [] : ["openid", "profile", "email", "User.Read", "offline_access"];
2397
+ if (options.scope) scopes.push(...options.scope);
2398
+ if (data.scopes) scopes.push(...data.scopes);
2399
+ return createAuthorizationURL({
2400
+ id: "microsoft",
2401
+ options,
2402
+ authorizationEndpoint: authorizationEndpoint2,
2403
+ state: data.state,
2404
+ codeVerifier: data.codeVerifier,
2405
+ scopes,
2406
+ redirectURI: data.redirectURI,
2407
+ prompt: options.prompt,
2408
+ loginHint: data.loginHint
2409
+ });
2410
+ },
2411
+ validateAuthorizationCode({ code, codeVerifier, redirectURI }) {
2412
+ return validateAuthorizationCode({
2413
+ code,
2414
+ codeVerifier,
2415
+ redirectURI,
2416
+ options,
2417
+ tokenEndpoint: tokenEndpoint2
2418
+ });
2419
+ },
2420
+ async verifyIdToken(token, nonce) {
2421
+ if (options.disableIdTokenSignIn) {
2422
+ return false;
2423
+ }
2424
+ if (options.verifyIdToken) {
2425
+ return options.verifyIdToken(token, nonce);
2426
+ }
2427
+ try {
2428
+ const { kid, alg: jwtAlg } = jose.decodeProtectedHeader(token);
2429
+ if (!kid || !jwtAlg) return false;
2430
+ const publicKey = await getMicrosoftPublicKey(kid, tenant, authority);
2431
+ const verifyOptions = {
2432
+ algorithms: [jwtAlg],
2433
+ audience: options.clientId,
2434
+ maxTokenAge: "1h"
2435
+ };
2436
+ if (tenant !== "common" && tenant !== "organizations" && tenant !== "consumers") {
2437
+ verifyOptions.issuer = `${authority}/${tenant}/v2.0`;
2438
+ }
2439
+ const { payload: jwtClaims } = await jose.jwtVerify(
2440
+ token,
2441
+ publicKey,
2442
+ verifyOptions
2443
+ );
2444
+ if (nonce && jwtClaims.nonce !== nonce) {
2445
+ return false;
2446
+ }
2447
+ const tid = jwtClaims.tid;
2448
+ if (typeof tid !== "string" || jwtClaims.iss !== `${authority}/${tid}/v2.0`) {
2449
+ return false;
2450
+ }
2451
+ if (tenant === "organizations" && tid === MICROSOFT_CONSUMER_TENANT_ID) {
2452
+ return false;
2453
+ }
2454
+ if (tenant === "consumers" && tid !== MICROSOFT_CONSUMER_TENANT_ID) {
2455
+ return false;
2456
+ }
2457
+ return true;
2458
+ } catch (error) {
2459
+ logger.error("Failed to verify ID token:", error);
2460
+ return false;
2461
+ }
2462
+ },
2463
+ async getUserInfo(token) {
2464
+ if (options.getUserInfo) {
2465
+ return options.getUserInfo(token);
2466
+ }
2467
+ if (!token.idToken) {
2468
+ return null;
2469
+ }
2470
+ const user = jose.decodeJwt(token.idToken);
2471
+ const profilePhotoSize = options.profilePhotoSize || 48;
2472
+ await athenaFetch(
2473
+ `https://graph.microsoft.com/v1.0/me/photos/${profilePhotoSize}x${profilePhotoSize}/$value`,
2474
+ {
2475
+ headers: {
2476
+ Authorization: `Bearer ${token.accessToken}`
2477
+ },
2478
+ async onResponse(context) {
2479
+ if (options.disableProfilePhoto || !context.response.ok) {
2480
+ return;
2481
+ }
2482
+ try {
2483
+ const response = context.response.clone();
2484
+ const pictureBuffer = await response.arrayBuffer();
2485
+ const pictureBase64 = base64.encode(pictureBuffer);
2486
+ user.picture = `data:image/jpeg;base64, ${pictureBase64}`;
2487
+ } catch (e) {
2488
+ logger.error(
2489
+ e && typeof e === "object" && "name" in e ? e.name : "",
2490
+ e
2491
+ );
2492
+ }
2493
+ }
2494
+ }
2495
+ );
2496
+ const userMap = await options.mapProfileToUser?.(user);
2497
+ 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;
2498
+ return {
2499
+ user: {
2500
+ id: user.sub,
2501
+ name: user.name,
2502
+ email: user.email,
2503
+ image: user.picture,
2504
+ emailVerified,
2505
+ ...userMap
2506
+ },
2507
+ data: user
2508
+ };
2509
+ },
2510
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2511
+ const scopes = options.disableDefaultScope ? [] : ["openid", "profile", "email", "User.Read", "offline_access"];
2512
+ if (options.scope) scopes.push(...options.scope);
2513
+ return refreshAccessToken({
2514
+ refreshToken,
2515
+ options: {
2516
+ clientId: options.clientId,
2517
+ clientSecret: options.clientSecret
2518
+ },
2519
+ extraParams: {
2520
+ scope: scopes.join(" ")
2521
+ // Include the scopes in request to microsoft
2522
+ },
2523
+ tokenEndpoint: tokenEndpoint2
2524
+ });
2525
+ },
2526
+ options
2527
+ };
2528
+ };
2529
+
2530
+ // src/auth/social-providers/naver.ts
2531
+ var naver = (options) => {
2532
+ const tokenEndpoint2 = "https://nid.naver.com/oauth2.0/token";
2533
+ return {
2534
+ id: "naver",
2535
+ name: "Naver",
2536
+ createAuthorizationURL({ state, scopes, redirectURI }) {
2537
+ const _scopes = options.disableDefaultScope ? [] : ["profile", "email"];
2538
+ if (options.scope) _scopes.push(...options.scope);
2539
+ if (scopes) _scopes.push(...scopes);
2540
+ return createAuthorizationURL({
2541
+ id: "naver",
2542
+ options,
2543
+ authorizationEndpoint: "https://nid.naver.com/oauth2.0/authorize",
2544
+ scopes: _scopes,
2545
+ state,
2546
+ redirectURI
2547
+ });
2548
+ },
2549
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2550
+ return validateAuthorizationCode({
2551
+ code,
2552
+ redirectURI,
2553
+ options,
2554
+ tokenEndpoint: tokenEndpoint2
2555
+ });
2556
+ },
2557
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2558
+ return refreshAccessToken({
2559
+ refreshToken,
2560
+ options: {
2561
+ clientId: options.clientId,
2562
+ clientKey: options.clientKey,
2563
+ clientSecret: options.clientSecret
2564
+ },
2565
+ tokenEndpoint: tokenEndpoint2
2566
+ });
2567
+ },
2568
+ async getUserInfo(token) {
2569
+ if (options.getUserInfo) {
2570
+ return options.getUserInfo(token);
2571
+ }
2572
+ const { data: profile, error } = await athenaFetch(
2573
+ "https://openapi.naver.com/v1/nid/me",
2574
+ {
2575
+ headers: {
2576
+ Authorization: `Bearer ${token.accessToken}`
2577
+ }
2578
+ }
2579
+ );
2580
+ if (error || !profile || profile.resultcode !== "00") {
2581
+ return null;
2582
+ }
2583
+ const userMap = await options.mapProfileToUser?.(profile);
2584
+ const res = profile.response || {};
2585
+ const user = {
2586
+ id: res.id,
2587
+ name: res.name || res.nickname || "",
2588
+ email: res.email,
2589
+ image: res.profile_image,
2590
+ emailVerified: false,
2591
+ ...userMap
2592
+ };
2593
+ return {
2594
+ user,
2595
+ data: profile
2596
+ };
2597
+ },
2598
+ options
2599
+ };
2600
+ };
2601
+
2602
+ // src/auth/social-providers/notion.ts
2603
+ var notion = (options) => {
2604
+ const tokenEndpoint2 = "https://api.notion.com/v1/oauth/token";
2605
+ return {
2606
+ id: "notion",
2607
+ name: "Notion",
2608
+ createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
2609
+ const _scopes = options.disableDefaultScope ? [] : [];
2610
+ if (options.scope) _scopes.push(...options.scope);
2611
+ if (scopes) _scopes.push(...scopes);
2612
+ return createAuthorizationURL({
2613
+ id: "notion",
2614
+ options,
2615
+ authorizationEndpoint: "https://api.notion.com/v1/oauth/authorize",
2616
+ scopes: _scopes,
2617
+ state,
2618
+ redirectURI,
2619
+ loginHint,
2620
+ additionalParams: {
2621
+ owner: "user"
2622
+ }
2623
+ });
2624
+ },
2625
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2626
+ return validateAuthorizationCode({
2627
+ code,
2628
+ redirectURI,
2629
+ options,
2630
+ tokenEndpoint: tokenEndpoint2,
2631
+ authentication: "basic"
2632
+ });
2633
+ },
2634
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2635
+ return refreshAccessToken({
2636
+ refreshToken,
2637
+ options: {
2638
+ clientId: options.clientId,
2639
+ clientKey: options.clientKey,
2640
+ clientSecret: options.clientSecret
2641
+ },
2642
+ tokenEndpoint: tokenEndpoint2
2643
+ });
2644
+ },
2645
+ async getUserInfo(token) {
2646
+ if (options.getUserInfo) {
2647
+ return options.getUserInfo(token);
2648
+ }
2649
+ const { data: profile, error } = await athenaFetch("https://api.notion.com/v1/users/me", {
2650
+ headers: {
2651
+ Authorization: `Bearer ${token.accessToken}`,
2652
+ "Notion-Version": "2022-06-28"
2653
+ }
2654
+ });
2655
+ if (error || !profile) {
2656
+ return null;
2657
+ }
2658
+ const userProfile = profile.bot?.owner?.user;
2659
+ if (!userProfile) {
2660
+ return null;
2661
+ }
2662
+ const userMap = await options.mapProfileToUser?.(userProfile);
2663
+ return {
2664
+ user: {
2665
+ id: userProfile.id,
2666
+ name: userProfile.name || "",
2667
+ email: userProfile.person?.email || null,
2668
+ image: userProfile.avatar_url,
2669
+ emailVerified: false,
2670
+ ...userMap
2671
+ },
2672
+ data: userProfile
2673
+ };
2674
+ },
2675
+ options
2676
+ };
2677
+ };
2678
+ var paybin = (options) => {
2679
+ const issuer = options.issuer || "https://idp.paybin.io";
2680
+ const authorizationEndpoint2 = `${issuer}/oauth2/authorize`;
2681
+ const tokenEndpoint2 = `${issuer}/oauth2/token`;
2682
+ return {
2683
+ id: "paybin",
2684
+ name: "Paybin",
2685
+ async createAuthorizationURL({
2686
+ state,
2687
+ scopes,
2688
+ codeVerifier,
2689
+ redirectURI,
2690
+ loginHint
2691
+ }) {
2692
+ if (!options.clientId || !options.clientSecret) {
2693
+ logger.error(
2694
+ "Client Id and Client Secret is required for Paybin. Make sure to provide them in the options."
2695
+ );
2696
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
2697
+ }
2698
+ if (!codeVerifier) {
2699
+ throw new AthenaAuthError("codeVerifier is required for Paybin");
2700
+ }
2701
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "email", "profile"];
2702
+ if (options.scope) _scopes.push(...options.scope);
2703
+ if (scopes) _scopes.push(...scopes);
2704
+ const url = await createAuthorizationURL({
2705
+ id: "paybin",
2706
+ options,
2707
+ authorizationEndpoint: authorizationEndpoint2,
2708
+ scopes: _scopes,
2709
+ state,
2710
+ codeVerifier,
2711
+ redirectURI,
2712
+ prompt: options.prompt,
2713
+ loginHint
2714
+ });
2715
+ return url;
2716
+ },
2717
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
2718
+ return validateAuthorizationCode({
2719
+ code,
2720
+ codeVerifier,
2721
+ redirectURI,
2722
+ options,
2723
+ tokenEndpoint: tokenEndpoint2
2724
+ });
2725
+ },
2726
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2727
+ return refreshAccessToken({
2728
+ refreshToken,
2729
+ options: {
2730
+ clientId: options.clientId,
2731
+ clientKey: options.clientKey,
2732
+ clientSecret: options.clientSecret
2733
+ },
2734
+ tokenEndpoint: tokenEndpoint2
2735
+ });
2736
+ },
2737
+ async getUserInfo(token) {
2738
+ if (options.getUserInfo) {
2739
+ return options.getUserInfo(token);
2740
+ }
2741
+ if (!token.idToken) {
2742
+ return null;
2743
+ }
2744
+ const user = jose.decodeJwt(token.idToken);
2745
+ const userMap = await options.mapProfileToUser?.(user);
2746
+ return {
2747
+ user: {
2748
+ id: user.sub,
2749
+ name: user.name || user.preferred_username || "",
2750
+ email: user.email,
2751
+ image: user.picture,
2752
+ emailVerified: user.email_verified || false,
2753
+ ...userMap
2754
+ },
2755
+ data: user
2756
+ };
2757
+ },
2758
+ options
2759
+ };
2760
+ };
2761
+
2762
+ // src/auth/social-providers/paypal-keys.ts
2763
+ var getPayPalPublicKey = async (kid, jwksUri) => {
2764
+ return getJwksPublicKey(jwksUri, kid);
2765
+ };
2766
+
2767
+ // src/auth/social-providers/paypal.ts
2768
+ var PAYPAL_ID_TOKEN_ALGORITHMS = ["RS256", "HS256"];
2769
+ var paypal = (options) => {
2770
+ const environment = options.environment || "sandbox";
2771
+ const isSandbox = environment === "sandbox";
2772
+ const authorizationEndpoint2 = isSandbox ? "https://www.sandbox.paypal.com/signin/authorize" : "https://www.paypal.com/signin/authorize";
2773
+ const tokenEndpoint2 = isSandbox ? "https://api-m.sandbox.paypal.com/v1/oauth2/token" : "https://api-m.paypal.com/v1/oauth2/token";
2774
+ const userInfoEndpoint = isSandbox ? "https://api-m.sandbox.paypal.com/v1/identity/oauth2/userinfo" : "https://api-m.paypal.com/v1/identity/oauth2/userinfo";
2775
+ const issuer = isSandbox ? "https://www.sandbox.paypal.com" : "https://www.paypal.com";
2776
+ const jwksEndpoint = isSandbox ? "https://api.sandbox.paypal.com/v1/oauth2/certs" : "https://api.paypal.com/v1/oauth2/certs";
2777
+ return {
2778
+ id: "paypal",
2779
+ name: "PayPal",
2780
+ async createAuthorizationURL({ state, codeVerifier, redirectURI }) {
2781
+ if (!options.clientId || !options.clientSecret) {
2782
+ logger.error(
2783
+ "Client Id and Client Secret is required for PayPal. Make sure to provide them in the options."
2784
+ );
2785
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
2786
+ }
2787
+ const _scopes = [];
2788
+ const url = await createAuthorizationURL({
2789
+ id: "paypal",
2790
+ options,
2791
+ authorizationEndpoint: authorizationEndpoint2,
2792
+ scopes: _scopes,
2793
+ state,
2794
+ codeVerifier,
2795
+ redirectURI,
2796
+ prompt: options.prompt
2797
+ });
2798
+ return url;
2799
+ },
2800
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
2801
+ const credentials = base64.encode(
2802
+ `${options.clientId}:${options.clientSecret}`
2803
+ );
2804
+ try {
2805
+ const response = await athenaFetch(tokenEndpoint2, {
2806
+ method: "POST",
2807
+ headers: {
2808
+ Authorization: `Basic ${credentials}`,
2809
+ Accept: "application/json",
2810
+ "Accept-Language": "en_US",
2811
+ "Content-Type": "application/x-www-form-urlencoded"
2812
+ },
2813
+ body: new URLSearchParams({
2814
+ grant_type: "authorization_code",
2815
+ code,
2816
+ redirect_uri: redirectURI
2817
+ }).toString()
2818
+ });
2819
+ if (!response.data) {
2820
+ throw new AthenaAuthError("FAILED_TO_GET_ACCESS_TOKEN");
2821
+ }
2822
+ const data = response.data;
2823
+ const result = {
2824
+ accessToken: data.access_token,
2825
+ refreshToken: data.refresh_token,
2826
+ accessTokenExpiresAt: data.expires_in ? new Date(Date.now() + data.expires_in * 1e3) : void 0,
2827
+ idToken: data.id_token
2828
+ };
2829
+ return result;
2830
+ } catch (error) {
2831
+ logger.error("PayPal token exchange failed:", error);
2832
+ throw new AthenaAuthError("FAILED_TO_GET_ACCESS_TOKEN");
2833
+ }
2834
+ },
2835
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2836
+ const credentials = base64.encode(
2837
+ `${options.clientId}:${options.clientSecret}`
2838
+ );
2839
+ try {
2840
+ const response = await athenaFetch(tokenEndpoint2, {
2841
+ method: "POST",
2842
+ headers: {
2843
+ Authorization: `Basic ${credentials}`,
2844
+ Accept: "application/json",
2845
+ "Accept-Language": "en_US",
2846
+ "Content-Type": "application/x-www-form-urlencoded"
2847
+ },
2848
+ body: new URLSearchParams({
2849
+ grant_type: "refresh_token",
2850
+ refresh_token: refreshToken
2851
+ }).toString()
2852
+ });
2853
+ if (!response.data) {
2854
+ throw new AthenaAuthError("FAILED_TO_REFRESH_ACCESS_TOKEN");
2855
+ }
2856
+ const data = response.data;
2857
+ return {
2858
+ accessToken: data.access_token,
2859
+ refreshToken: data.refresh_token,
2860
+ accessTokenExpiresAt: data.expires_in ? new Date(Date.now() + data.expires_in * 1e3) : void 0
2861
+ };
2862
+ } catch (error) {
2863
+ logger.error("PayPal token refresh failed:", error);
2864
+ throw new AthenaAuthError("FAILED_TO_REFRESH_ACCESS_TOKEN");
2865
+ }
2866
+ },
2867
+ async verifyIdToken(token, nonce) {
2868
+ if (options.disableIdTokenSignIn) {
2869
+ return false;
2870
+ }
2871
+ if (options.verifyIdToken) {
2872
+ return options.verifyIdToken(token, nonce);
2873
+ }
2874
+ try {
2875
+ const { kid, alg: jwtAlg } = jose.decodeProtectedHeader(token);
2876
+ if (!jwtAlg) return false;
2877
+ if (!PAYPAL_ID_TOKEN_ALGORITHMS.includes(
2878
+ jwtAlg
2879
+ )) {
2880
+ return false;
2881
+ }
2882
+ const key = jwtAlg === "HS256" ? new TextEncoder().encode(options.clientSecret) : kid ? await getPayPalPublicKey(kid, jwksEndpoint) : void 0;
2883
+ if (!key) return false;
2884
+ const { payload: jwtClaims } = await jose.jwtVerify(token, key, {
2885
+ algorithms: [jwtAlg],
2886
+ issuer,
2887
+ audience: options.clientId,
2888
+ maxTokenAge: "1h"
2889
+ });
2890
+ if (nonce && jwtClaims.nonce !== nonce) {
2891
+ return false;
2892
+ }
2893
+ return true;
2894
+ } catch (error) {
2895
+ logger.error("Failed to verify PayPal ID token:", error);
2896
+ return false;
2897
+ }
2898
+ },
2899
+ async getUserInfo(token) {
2900
+ if (options.getUserInfo) {
2901
+ return options.getUserInfo(token);
2902
+ }
2903
+ if (!token.accessToken) {
2904
+ logger.error("Access token is required to fetch PayPal user info");
2905
+ return null;
2906
+ }
2907
+ try {
2908
+ const response = await athenaFetch(
2909
+ `${userInfoEndpoint}?schema=paypalv1.1`,
2910
+ {
2911
+ headers: {
2912
+ Authorization: `Bearer ${token.accessToken}`,
2913
+ Accept: "application/json"
2914
+ }
2915
+ }
2916
+ );
2917
+ if (!response.data) {
2918
+ logger.error("Failed to fetch user info from PayPal");
2919
+ return null;
2920
+ }
2921
+ const userInfo = response.data;
2922
+ if (token.idToken) {
2923
+ let idTokenSubject;
2924
+ try {
2925
+ idTokenSubject = jose.decodeJwt(token.idToken).sub;
2926
+ } catch (error) {
2927
+ logger.error("Failed to decode PayPal ID token:", error);
2928
+ return null;
2929
+ }
2930
+ const userInfoSubject = userInfo.sub ?? userInfo.user_id;
2931
+ if (!idTokenSubject || userInfoSubject !== idTokenSubject) {
2932
+ logger.error(
2933
+ "PayPal user info subject does not match ID token subject"
2934
+ );
2935
+ return null;
2936
+ }
2937
+ }
2938
+ const userMap = await options.mapProfileToUser?.(userInfo);
2939
+ const result = {
2940
+ user: {
2941
+ id: userInfo.user_id,
2942
+ name: userInfo.name,
2943
+ email: userInfo.email,
2944
+ image: userInfo.picture,
2945
+ emailVerified: userInfo.email_verified,
2946
+ ...userMap
2947
+ },
2948
+ data: userInfo
2949
+ };
2950
+ return result;
2951
+ } catch (error) {
2952
+ logger.error("Failed to fetch user info from PayPal:", error);
2953
+ return null;
2954
+ }
2955
+ },
2956
+ options
2957
+ };
2958
+ };
2959
+
2960
+ // src/auth/social-providers/polar.ts
2961
+ var polar = (options) => {
2962
+ const tokenEndpoint2 = "https://api.polar.sh/v1/oauth2/token";
2963
+ return {
2964
+ id: "polar",
2965
+ name: "Polar",
2966
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
2967
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile", "email"];
2968
+ if (options.scope) _scopes.push(...options.scope);
2969
+ if (scopes) _scopes.push(...scopes);
2970
+ return createAuthorizationURL({
2971
+ id: "polar",
2972
+ options,
2973
+ authorizationEndpoint: "https://polar.sh/oauth2/authorize",
2974
+ scopes: _scopes,
2975
+ state,
2976
+ codeVerifier,
2977
+ redirectURI,
2978
+ prompt: options.prompt
2979
+ });
2980
+ },
2981
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
2982
+ return validateAuthorizationCode({
2983
+ code,
2984
+ codeVerifier,
2985
+ redirectURI,
2986
+ options,
2987
+ tokenEndpoint: tokenEndpoint2
2988
+ });
2989
+ },
2990
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
2991
+ return refreshAccessToken({
2992
+ refreshToken,
2993
+ options: {
2994
+ clientId: options.clientId,
2995
+ clientKey: options.clientKey,
2996
+ clientSecret: options.clientSecret
2997
+ },
2998
+ tokenEndpoint: tokenEndpoint2
2999
+ });
3000
+ },
3001
+ async getUserInfo(token) {
3002
+ if (options.getUserInfo) {
3003
+ return options.getUserInfo(token);
3004
+ }
3005
+ const { data: profile, error } = await athenaFetch(
3006
+ "https://api.polar.sh/v1/oauth2/userinfo",
3007
+ {
3008
+ headers: {
3009
+ Authorization: `Bearer ${token.accessToken}`
3010
+ }
3011
+ }
3012
+ );
3013
+ if (error) {
3014
+ return null;
3015
+ }
3016
+ const userMap = await options.mapProfileToUser?.(profile);
3017
+ return {
3018
+ user: {
3019
+ id: profile.id,
3020
+ name: profile.public_name || profile.username || "",
3021
+ email: profile.email,
3022
+ image: profile.avatar_url,
3023
+ emailVerified: profile.email_verified ?? false,
3024
+ ...userMap
3025
+ },
3026
+ data: profile
3027
+ };
3028
+ },
3029
+ options
3030
+ };
3031
+ };
3032
+
3033
+ // src/auth/social-providers/railway.ts
3034
+ var authorizationEndpoint = "https://backboard.railway.com/oauth/auth";
3035
+ var tokenEndpoint = "https://backboard.railway.com/oauth/token";
3036
+ var userinfoEndpoint = "https://backboard.railway.com/oauth/me";
3037
+ var railway = (options) => {
3038
+ return {
3039
+ id: "railway",
3040
+ name: "Railway",
3041
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
3042
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "email", "profile"];
3043
+ if (options.scope) _scopes.push(...options.scope);
3044
+ if (scopes) _scopes.push(...scopes);
3045
+ return createAuthorizationURL({
3046
+ id: "railway",
3047
+ options,
3048
+ authorizationEndpoint,
3049
+ scopes: _scopes,
3050
+ state,
3051
+ codeVerifier,
3052
+ redirectURI
3053
+ });
3054
+ },
3055
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
3056
+ return validateAuthorizationCode({
3057
+ code,
3058
+ codeVerifier,
3059
+ redirectURI,
3060
+ options,
3061
+ tokenEndpoint,
3062
+ authentication: "basic"
3063
+ });
3064
+ },
3065
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3066
+ return refreshAccessToken({
3067
+ refreshToken,
3068
+ options: {
3069
+ clientId: options.clientId,
3070
+ clientKey: options.clientKey,
3071
+ clientSecret: options.clientSecret
3072
+ },
3073
+ tokenEndpoint,
3074
+ authentication: "basic"
3075
+ });
3076
+ },
3077
+ async getUserInfo(token) {
3078
+ if (options.getUserInfo) {
3079
+ return options.getUserInfo(token);
3080
+ }
3081
+ const { data: profile, error } = await athenaFetch(
3082
+ userinfoEndpoint,
3083
+ { headers: { authorization: `Bearer ${token.accessToken}` } }
3084
+ );
3085
+ if (error || !profile) {
3086
+ return null;
3087
+ }
3088
+ const userMap = await options.mapProfileToUser?.(profile);
3089
+ return {
3090
+ user: {
3091
+ id: profile.sub,
3092
+ name: profile.name,
3093
+ email: profile.email,
3094
+ image: profile.picture,
3095
+ emailVerified: false,
3096
+ ...userMap
3097
+ },
3098
+ data: profile
3099
+ };
3100
+ },
3101
+ options
3102
+ };
3103
+ };
3104
+
3105
+ // src/auth/social-providers/reddit.ts
3106
+ var reddit = (options) => {
3107
+ return {
3108
+ id: "reddit",
3109
+ name: "Reddit",
3110
+ createAuthorizationURL({ state, scopes, redirectURI }) {
3111
+ const _scopes = options.disableDefaultScope ? [] : ["identity"];
3112
+ if (options.scope) _scopes.push(...options.scope);
3113
+ if (scopes) _scopes.push(...scopes);
3114
+ return createAuthorizationURL({
3115
+ id: "reddit",
3116
+ options,
3117
+ authorizationEndpoint: "https://www.reddit.com/api/v1/authorize",
3118
+ scopes: _scopes,
3119
+ state,
3120
+ redirectURI,
3121
+ duration: options.duration
3122
+ });
3123
+ },
3124
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
3125
+ const body = new URLSearchParams({
3126
+ grant_type: "authorization_code",
3127
+ code,
3128
+ redirect_uri: options.redirectURI || redirectURI
3129
+ });
3130
+ const headers = {
3131
+ "content-type": "application/x-www-form-urlencoded",
3132
+ accept: "text/plain",
3133
+ "User-Agent": "athena-auth",
3134
+ Authorization: `Basic ${base64.encode(
3135
+ `${options.clientId}:${options.clientSecret}`
3136
+ )}`
3137
+ };
3138
+ const { data, error } = await athenaFetch(
3139
+ "https://www.reddit.com/api/v1/access_token",
3140
+ {
3141
+ method: "POST",
3142
+ headers,
3143
+ body: body.toString()
3144
+ }
3145
+ );
3146
+ if (error) {
3147
+ throw error;
3148
+ }
3149
+ return getOAuth2Tokens(data);
3150
+ },
3151
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3152
+ return refreshAccessToken({
3153
+ refreshToken,
3154
+ options: {
3155
+ clientId: options.clientId,
3156
+ clientKey: options.clientKey,
3157
+ clientSecret: options.clientSecret
3158
+ },
3159
+ authentication: "basic",
3160
+ tokenEndpoint: "https://www.reddit.com/api/v1/access_token"
3161
+ });
3162
+ },
3163
+ async getUserInfo(token) {
3164
+ if (options.getUserInfo) {
3165
+ return options.getUserInfo(token);
3166
+ }
3167
+ const { data: profile, error } = await athenaFetch(
3168
+ "https://oauth.reddit.com/api/v1/me",
3169
+ {
3170
+ headers: {
3171
+ Authorization: `Bearer ${token.accessToken}`,
3172
+ "User-Agent": "athena-auth"
3173
+ }
3174
+ }
3175
+ );
3176
+ if (error) {
3177
+ return null;
3178
+ }
3179
+ const userMap = await options.mapProfileToUser?.(profile);
3180
+ const email = userMap?.email || `${profile.id}@reddit.invalid`;
3181
+ return {
3182
+ user: {
3183
+ id: profile.id,
3184
+ name: profile.name,
3185
+ image: profile.icon_img?.split("?")[0],
3186
+ ...userMap,
3187
+ email,
3188
+ emailVerified: userMap?.emailVerified ?? false
3189
+ },
3190
+ data: profile
3191
+ };
3192
+ },
3193
+ options
3194
+ };
3195
+ };
3196
+
3197
+ // src/auth/social-providers/roblox.ts
3198
+ var roblox = (options) => {
3199
+ const tokenEndpoint2 = "https://apis.roblox.com/oauth/v1/token";
3200
+ return {
3201
+ id: "roblox",
3202
+ name: "Roblox",
3203
+ createAuthorizationURL({ state, scopes, redirectURI }) {
3204
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile"];
3205
+ if (options.scope) _scopes.push(...options.scope);
3206
+ if (scopes) _scopes.push(...scopes);
3207
+ return new URL(
3208
+ `https://apis.roblox.com/oauth/v1/authorize?scope=${_scopes.join(
3209
+ "+"
3210
+ )}&response_type=code&client_id=${options.clientId}&redirect_uri=${encodeURIComponent(
3211
+ options.redirectURI || redirectURI
3212
+ )}&state=${state}&prompt=${options.prompt || "select_account consent"}`
3213
+ );
3214
+ },
3215
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
3216
+ return validateAuthorizationCode({
3217
+ code,
3218
+ redirectURI: options.redirectURI || redirectURI,
3219
+ options,
3220
+ tokenEndpoint: tokenEndpoint2,
3221
+ authentication: "post"
3222
+ });
3223
+ },
3224
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3225
+ return refreshAccessToken({
3226
+ refreshToken,
3227
+ options: {
3228
+ clientId: options.clientId,
3229
+ clientKey: options.clientKey,
3230
+ clientSecret: options.clientSecret
3231
+ },
3232
+ tokenEndpoint: tokenEndpoint2
3233
+ });
3234
+ },
3235
+ async getUserInfo(token) {
3236
+ if (options.getUserInfo) {
3237
+ return options.getUserInfo(token);
3238
+ }
3239
+ const { data: profile, error } = await athenaFetch(
3240
+ "https://apis.roblox.com/oauth/v1/userinfo",
3241
+ {
3242
+ headers: {
3243
+ authorization: `Bearer ${token.accessToken}`
3244
+ }
3245
+ }
3246
+ );
3247
+ if (error) {
3248
+ return null;
3249
+ }
3250
+ const userMap = await options.mapProfileToUser?.(profile);
3251
+ return {
3252
+ user: {
3253
+ id: profile.sub,
3254
+ name: profile.nickname || profile.preferred_username || "",
3255
+ image: profile.picture,
3256
+ email: profile.preferred_username || null,
3257
+ // Roblox does not provide email
3258
+ emailVerified: false,
3259
+ ...userMap
3260
+ },
3261
+ data: {
3262
+ ...profile
3263
+ }
3264
+ };
3265
+ },
3266
+ options
3267
+ };
3268
+ };
3269
+
3270
+ // src/auth/social-providers/salesforce.ts
3271
+ var salesforce = (options) => {
3272
+ const environment = options.environment ?? "production";
3273
+ const isSandbox = environment === "sandbox";
3274
+ const authorizationEndpoint2 = options.loginUrl ? `https://${options.loginUrl}/services/oauth2/authorize` : isSandbox ? "https://test.salesforce.com/services/oauth2/authorize" : "https://login.salesforce.com/services/oauth2/authorize";
3275
+ const tokenEndpoint2 = options.loginUrl ? `https://${options.loginUrl}/services/oauth2/token` : isSandbox ? "https://test.salesforce.com/services/oauth2/token" : "https://login.salesforce.com/services/oauth2/token";
3276
+ 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";
3277
+ return {
3278
+ id: "salesforce",
3279
+ name: "Salesforce",
3280
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
3281
+ if (!options.clientId || !options.clientSecret) {
3282
+ logger.error(
3283
+ "Client Id and Client Secret are required for Salesforce. Make sure to provide them in the options."
3284
+ );
3285
+ throw new AthenaAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
3286
+ }
3287
+ if (!codeVerifier) {
3288
+ throw new AthenaAuthError("codeVerifier is required for Salesforce");
3289
+ }
3290
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "email", "profile"];
3291
+ if (options.scope) _scopes.push(...options.scope);
3292
+ if (scopes) _scopes.push(...scopes);
3293
+ return createAuthorizationURL({
3294
+ id: "salesforce",
3295
+ options,
3296
+ authorizationEndpoint: authorizationEndpoint2,
3297
+ scopes: _scopes,
3298
+ state,
3299
+ codeVerifier,
3300
+ redirectURI: options.redirectURI || redirectURI
3301
+ });
3302
+ },
3303
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
3304
+ return validateAuthorizationCode({
3305
+ code,
3306
+ codeVerifier,
3307
+ redirectURI: options.redirectURI || redirectURI,
3308
+ options,
3309
+ tokenEndpoint: tokenEndpoint2
3310
+ });
3311
+ },
3312
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3313
+ return refreshAccessToken({
3314
+ refreshToken,
3315
+ options: {
3316
+ clientId: options.clientId,
3317
+ clientSecret: options.clientSecret
3318
+ },
3319
+ tokenEndpoint: tokenEndpoint2
3320
+ });
3321
+ },
3322
+ async getUserInfo(token) {
3323
+ if (options.getUserInfo) {
3324
+ return options.getUserInfo(token);
3325
+ }
3326
+ try {
3327
+ const { data: user } = await athenaFetch(
3328
+ userInfoEndpoint,
3329
+ {
3330
+ headers: {
3331
+ Authorization: `Bearer ${token.accessToken}`
3332
+ }
3333
+ }
3334
+ );
3335
+ if (!user) {
3336
+ logger.error("Failed to fetch user info from Salesforce");
3337
+ return null;
3338
+ }
3339
+ const userMap = await options.mapProfileToUser?.(user);
3340
+ return {
3341
+ user: {
3342
+ id: user.user_id,
3343
+ name: user.name,
3344
+ email: user.email,
3345
+ image: user.photos?.picture || user.photos?.thumbnail,
3346
+ emailVerified: user.email_verified ?? false,
3347
+ ...userMap
3348
+ },
3349
+ data: user
3350
+ };
3351
+ } catch (error) {
3352
+ logger.error("Failed to fetch user info from Salesforce:", error);
3353
+ return null;
3354
+ }
3355
+ },
3356
+ options
3357
+ };
3358
+ };
3359
+
3360
+ // src/auth/social-providers/slack.ts
3361
+ var slack = (options) => {
3362
+ const tokenEndpoint2 = "https://slack.com/api/openid.connect.token";
3363
+ return {
3364
+ id: "slack",
3365
+ name: "Slack",
3366
+ createAuthorizationURL({ state, scopes, redirectURI }) {
3367
+ const _scopes = options.disableDefaultScope ? [] : ["openid", "profile", "email"];
3368
+ if (scopes) _scopes.push(...scopes);
3369
+ if (options.scope) _scopes.push(...options.scope);
3370
+ const url = new URL("https://slack.com/openid/connect/authorize");
3371
+ url.searchParams.set("scope", _scopes.join(" "));
3372
+ url.searchParams.set("response_type", "code");
3373
+ url.searchParams.set("client_id", options.clientId);
3374
+ url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
3375
+ url.searchParams.set("state", state);
3376
+ return url;
3377
+ },
3378
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
3379
+ return validateAuthorizationCode({
3380
+ code,
3381
+ redirectURI,
3382
+ options,
3383
+ tokenEndpoint: tokenEndpoint2
3384
+ });
3385
+ },
3386
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3387
+ return refreshAccessToken({
3388
+ refreshToken,
3389
+ options: {
3390
+ clientId: options.clientId,
3391
+ clientKey: options.clientKey,
3392
+ clientSecret: options.clientSecret
3393
+ },
3394
+ tokenEndpoint: tokenEndpoint2
3395
+ });
3396
+ },
3397
+ async getUserInfo(token) {
3398
+ if (options.getUserInfo) {
3399
+ return options.getUserInfo(token);
3400
+ }
3401
+ const { data: profile, error } = await athenaFetch(
3402
+ "https://slack.com/api/openid.connect.userInfo",
3403
+ {
3404
+ headers: {
3405
+ authorization: `Bearer ${token.accessToken}`
3406
+ }
3407
+ }
3408
+ );
3409
+ if (error) {
3410
+ return null;
3411
+ }
3412
+ const userMap = await options.mapProfileToUser?.(profile);
3413
+ return {
3414
+ user: {
3415
+ id: profile["https://slack.com/user_id"],
3416
+ name: profile.name || "",
3417
+ email: profile.email,
3418
+ emailVerified: profile.email_verified,
3419
+ image: profile.picture || profile["https://slack.com/user_image_512"],
3420
+ ...userMap
3421
+ },
3422
+ data: profile
3423
+ };
3424
+ },
3425
+ options
3426
+ };
3427
+ };
3428
+
3429
+ // src/auth/social-providers/spotify.ts
3430
+ var spotify = (options) => {
3431
+ const tokenEndpoint2 = "https://accounts.spotify.com/api/token";
3432
+ return {
3433
+ id: "spotify",
3434
+ name: "Spotify",
3435
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
3436
+ const _scopes = options.disableDefaultScope ? [] : ["user-read-email"];
3437
+ if (options.scope) _scopes.push(...options.scope);
3438
+ if (scopes) _scopes.push(...scopes);
3439
+ return createAuthorizationURL({
3440
+ id: "spotify",
3441
+ options,
3442
+ authorizationEndpoint: "https://accounts.spotify.com/authorize",
3443
+ scopes: _scopes,
3444
+ state,
3445
+ codeVerifier,
3446
+ redirectURI
3447
+ });
3448
+ },
3449
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
3450
+ return validateAuthorizationCode({
3451
+ code,
3452
+ codeVerifier,
3453
+ redirectURI,
3454
+ options,
3455
+ tokenEndpoint: tokenEndpoint2
3456
+ });
3457
+ },
3458
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3459
+ return refreshAccessToken({
3460
+ refreshToken,
3461
+ options: {
3462
+ clientId: options.clientId,
3463
+ clientKey: options.clientKey,
3464
+ clientSecret: options.clientSecret
3465
+ },
3466
+ tokenEndpoint: tokenEndpoint2
3467
+ });
3468
+ },
3469
+ async getUserInfo(token) {
3470
+ if (options.getUserInfo) {
3471
+ return options.getUserInfo(token);
3472
+ }
3473
+ const { data: profile, error } = await athenaFetch(
3474
+ "https://api.spotify.com/v1/me",
3475
+ {
3476
+ method: "GET",
3477
+ headers: {
3478
+ Authorization: `Bearer ${token.accessToken}`
3479
+ }
3480
+ }
3481
+ );
3482
+ if (error) {
3483
+ return null;
3484
+ }
3485
+ const userMap = await options.mapProfileToUser?.(profile);
3486
+ return {
3487
+ user: {
3488
+ id: profile.id,
3489
+ name: profile.display_name,
3490
+ email: profile.email,
3491
+ image: profile.images[0]?.url,
3492
+ emailVerified: false,
3493
+ ...userMap
3494
+ },
3495
+ data: profile
3496
+ };
3497
+ },
3498
+ options
3499
+ };
3500
+ };
3501
+
3502
+ // src/auth/social-providers/tiktok.ts
3503
+ var tiktok = (options) => {
3504
+ const tokenEndpoint2 = "https://open.tiktokapis.com/v2/oauth/token/";
3505
+ return {
3506
+ id: "tiktok",
3507
+ name: "TikTok",
3508
+ createAuthorizationURL({ state, scopes, redirectURI }) {
3509
+ const _scopes = options.disableDefaultScope ? [] : ["user.info.profile"];
3510
+ if (options.scope) _scopes.push(...options.scope);
3511
+ if (scopes) _scopes.push(...scopes);
3512
+ return new URL(
3513
+ `https://www.tiktok.com/v2/auth/authorize?scope=${_scopes.join(
3514
+ ","
3515
+ )}&response_type=code&client_key=${options.clientKey}&redirect_uri=${encodeURIComponent(
3516
+ options.redirectURI || redirectURI
3517
+ )}&state=${state}`
3518
+ );
3519
+ },
3520
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
3521
+ return validateAuthorizationCode({
3522
+ code,
3523
+ redirectURI: options.redirectURI || redirectURI,
3524
+ options: {
3525
+ clientKey: options.clientKey,
3526
+ clientSecret: options.clientSecret
3527
+ },
3528
+ tokenEndpoint: tokenEndpoint2
3529
+ });
3530
+ },
3531
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3532
+ return refreshAccessToken({
3533
+ refreshToken,
3534
+ options: {
3535
+ clientSecret: options.clientSecret
3536
+ },
3537
+ tokenEndpoint: tokenEndpoint2,
3538
+ authentication: "post",
3539
+ extraParams: {
3540
+ client_key: options.clientKey
3541
+ }
3542
+ });
3543
+ },
3544
+ async getUserInfo(token) {
3545
+ if (options.getUserInfo) {
3546
+ return options.getUserInfo(token);
3547
+ }
3548
+ const fields = [
3549
+ "open_id",
3550
+ "avatar_large_url",
3551
+ "display_name",
3552
+ "username"
3553
+ ];
3554
+ const { data: profile, error } = await athenaFetch(
3555
+ `https://open.tiktokapis.com/v2/user/info/?fields=${fields.join(",")}`,
3556
+ {
3557
+ headers: {
3558
+ authorization: `Bearer ${token.accessToken}`
3559
+ }
3560
+ }
3561
+ );
3562
+ if (error) {
3563
+ return null;
3564
+ }
3565
+ return {
3566
+ user: {
3567
+ email: profile.data.user.email || profile.data.user.username,
3568
+ id: profile.data.user.open_id,
3569
+ name: profile.data.user.display_name || profile.data.user.username || "",
3570
+ image: profile.data.user.avatar_large_url,
3571
+ emailVerified: false
3572
+ },
3573
+ data: profile
3574
+ };
3575
+ },
3576
+ options
3577
+ };
3578
+ };
3579
+ var twitch = (options) => {
3580
+ const tokenEndpoint2 = "https://id.twitch.tv/oauth2/token";
3581
+ return {
3582
+ id: "twitch",
3583
+ name: "Twitch",
3584
+ createAuthorizationURL({ state, scopes, redirectURI }) {
3585
+ const _scopes = options.disableDefaultScope ? [] : ["user:read:email", "openid"];
3586
+ if (options.scope) _scopes.push(...options.scope);
3587
+ if (scopes) _scopes.push(...scopes);
3588
+ return createAuthorizationURL({
3589
+ id: "twitch",
3590
+ redirectURI,
3591
+ options,
3592
+ authorizationEndpoint: "https://id.twitch.tv/oauth2/authorize",
3593
+ scopes: _scopes,
3594
+ state,
3595
+ claims: options.claims || [
3596
+ "email",
3597
+ "email_verified",
3598
+ "preferred_username",
3599
+ "picture"
3600
+ ]
3601
+ });
3602
+ },
3603
+ validateAuthorizationCode: async ({ code, redirectURI }) => {
3604
+ return validateAuthorizationCode({
3605
+ code,
3606
+ redirectURI,
3607
+ options,
3608
+ tokenEndpoint: tokenEndpoint2
3609
+ });
3610
+ },
3611
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3612
+ return refreshAccessToken({
3613
+ refreshToken,
3614
+ options: {
3615
+ clientId: options.clientId,
3616
+ clientKey: options.clientKey,
3617
+ clientSecret: options.clientSecret
3618
+ },
3619
+ tokenEndpoint: tokenEndpoint2
3620
+ });
3621
+ },
3622
+ async getUserInfo(token) {
3623
+ if (options.getUserInfo) {
3624
+ return options.getUserInfo(token);
3625
+ }
3626
+ const idToken = token.idToken;
3627
+ if (!idToken) {
3628
+ logger.error("No idToken found in token");
3629
+ return null;
3630
+ }
3631
+ const profile = jose.decodeJwt(idToken);
3632
+ const userMap = await options.mapProfileToUser?.(profile);
3633
+ return {
3634
+ user: {
3635
+ id: profile.sub,
3636
+ name: profile.preferred_username,
3637
+ email: profile.email,
3638
+ image: profile.picture,
3639
+ emailVerified: profile.email_verified,
3640
+ ...userMap
3641
+ },
3642
+ data: profile
3643
+ };
3644
+ },
3645
+ options
3646
+ };
3647
+ };
3648
+
3649
+ // src/auth/social-providers/twitter.ts
3650
+ var twitter = (options) => {
3651
+ const tokenEndpoint2 = "https://api.x.com/2/oauth2/token";
3652
+ return {
3653
+ id: "twitter",
3654
+ name: "Twitter",
3655
+ createAuthorizationURL(data) {
3656
+ const _scopes = options.disableDefaultScope ? [] : ["users.read", "tweet.read", "offline.access", "users.email"];
3657
+ if (options.scope) _scopes.push(...options.scope);
3658
+ if (data.scopes) _scopes.push(...data.scopes);
3659
+ return createAuthorizationURL({
3660
+ id: "twitter",
3661
+ options,
3662
+ authorizationEndpoint: "https://x.com/i/oauth2/authorize",
3663
+ scopes: _scopes,
3664
+ state: data.state,
3665
+ codeVerifier: data.codeVerifier,
3666
+ redirectURI: data.redirectURI
3667
+ });
3668
+ },
3669
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
3670
+ return validateAuthorizationCode({
3671
+ code,
3672
+ codeVerifier,
3673
+ authentication: "basic",
3674
+ redirectURI,
3675
+ options,
3676
+ tokenEndpoint: tokenEndpoint2
3677
+ });
3678
+ },
3679
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3680
+ return refreshAccessToken({
3681
+ refreshToken,
3682
+ options: {
3683
+ clientId: options.clientId,
3684
+ clientKey: options.clientKey,
3685
+ clientSecret: options.clientSecret
3686
+ },
3687
+ authentication: "basic",
3688
+ tokenEndpoint: tokenEndpoint2
3689
+ });
3690
+ },
3691
+ async getUserInfo(token) {
3692
+ if (options.getUserInfo) {
3693
+ return options.getUserInfo(token);
3694
+ }
3695
+ const { data: profile, error: profileError } = await athenaFetch(
3696
+ "https://api.x.com/2/users/me?user.fields=profile_image_url",
3697
+ {
3698
+ method: "GET",
3699
+ headers: {
3700
+ Authorization: `Bearer ${token.accessToken}`
3701
+ }
3702
+ }
3703
+ );
3704
+ if (profileError) {
3705
+ return null;
3706
+ }
3707
+ const { data: emailData, error: emailError } = await athenaFetch("https://api.x.com/2/users/me?user.fields=confirmed_email", {
3708
+ method: "GET",
3709
+ headers: {
3710
+ Authorization: `Bearer ${token.accessToken}`
3711
+ }
3712
+ });
3713
+ let emailVerified = false;
3714
+ if (!emailError && emailData?.data?.confirmed_email) {
3715
+ profile.data.email = emailData.data.confirmed_email;
3716
+ emailVerified = true;
3717
+ }
3718
+ const userMap = await options.mapProfileToUser?.(profile);
3719
+ return {
3720
+ user: {
3721
+ id: profile.data.id,
3722
+ name: profile.data.name,
3723
+ email: profile.data.email || profile.data.username || null,
3724
+ image: profile.data.profile_image_url,
3725
+ emailVerified,
3726
+ ...userMap
3727
+ },
3728
+ data: profile
3729
+ };
3730
+ },
3731
+ options
3732
+ };
3733
+ };
3734
+
3735
+ // src/auth/social-providers/vercel.ts
3736
+ var vercel = (options) => {
3737
+ return {
3738
+ id: "vercel",
3739
+ name: "Vercel",
3740
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
3741
+ if (!codeVerifier) {
3742
+ throw new AthenaAuthError("codeVerifier is required for Vercel");
3743
+ }
3744
+ let _scopes = void 0;
3745
+ if (options.scope !== void 0 || scopes !== void 0) {
3746
+ _scopes = [];
3747
+ if (options.scope) _scopes.push(...options.scope);
3748
+ if (scopes) _scopes.push(...scopes);
3749
+ }
3750
+ return createAuthorizationURL({
3751
+ id: "vercel",
3752
+ options,
3753
+ authorizationEndpoint: "https://vercel.com/oauth/authorize",
3754
+ scopes: _scopes,
3755
+ state,
3756
+ codeVerifier,
3757
+ redirectURI
3758
+ });
3759
+ },
3760
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
3761
+ return validateAuthorizationCode({
3762
+ code,
3763
+ codeVerifier,
3764
+ redirectURI,
3765
+ options,
3766
+ tokenEndpoint: "https://api.vercel.com/login/oauth/token"
3767
+ });
3768
+ },
3769
+ async getUserInfo(token) {
3770
+ if (options.getUserInfo) {
3771
+ return options.getUserInfo(token);
3772
+ }
3773
+ const { data: profile, error } = await athenaFetch(
3774
+ "https://api.vercel.com/login/oauth/userinfo",
3775
+ {
3776
+ headers: {
3777
+ Authorization: `Bearer ${token.accessToken}`
3778
+ }
3779
+ }
3780
+ );
3781
+ if (error || !profile) {
3782
+ return null;
3783
+ }
3784
+ const userMap = await options.mapProfileToUser?.(profile);
3785
+ return {
3786
+ user: {
3787
+ id: profile.sub,
3788
+ name: profile.name ?? profile.preferred_username ?? "",
3789
+ email: profile.email,
3790
+ image: profile.picture,
3791
+ emailVerified: profile.email_verified ?? false,
3792
+ ...userMap
3793
+ },
3794
+ data: profile
3795
+ };
3796
+ },
3797
+ options
3798
+ };
3799
+ };
3800
+
3801
+ // src/auth/social-providers/vk.ts
3802
+ var vk = (options) => {
3803
+ const tokenEndpoint2 = "https://id.vk.com/oauth2/auth";
3804
+ return {
3805
+ id: "vk",
3806
+ name: "VK",
3807
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
3808
+ const _scopes = options.disableDefaultScope ? [] : ["email", "phone"];
3809
+ if (options.scope) _scopes.push(...options.scope);
3810
+ if (scopes) _scopes.push(...scopes);
3811
+ const authorizationEndpoint2 = "https://id.vk.com/authorize";
3812
+ return createAuthorizationURL({
3813
+ id: "vk",
3814
+ options,
3815
+ authorizationEndpoint: authorizationEndpoint2,
3816
+ scopes: _scopes,
3817
+ state,
3818
+ redirectURI,
3819
+ codeVerifier
3820
+ });
3821
+ },
3822
+ validateAuthorizationCode: async ({
3823
+ code,
3824
+ codeVerifier,
3825
+ redirectURI,
3826
+ deviceId
3827
+ }) => {
3828
+ return validateAuthorizationCode({
3829
+ code,
3830
+ codeVerifier,
3831
+ redirectURI: options.redirectURI || redirectURI,
3832
+ options,
3833
+ deviceId,
3834
+ tokenEndpoint: tokenEndpoint2
3835
+ });
3836
+ },
3837
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3838
+ return refreshAccessToken({
3839
+ refreshToken,
3840
+ options: {
3841
+ clientId: options.clientId,
3842
+ clientKey: options.clientKey,
3843
+ clientSecret: options.clientSecret
3844
+ },
3845
+ tokenEndpoint: tokenEndpoint2
3846
+ });
3847
+ },
3848
+ async getUserInfo(data) {
3849
+ if (options.getUserInfo) {
3850
+ return options.getUserInfo(data);
3851
+ }
3852
+ if (!data.accessToken) {
3853
+ return null;
3854
+ }
3855
+ const formBody = new URLSearchParams({
3856
+ access_token: data.accessToken,
3857
+ client_id: options.clientId
3858
+ }).toString();
3859
+ const { data: profile, error } = await athenaFetch(
3860
+ "https://id.vk.com/oauth2/user_info",
3861
+ {
3862
+ method: "POST",
3863
+ headers: {
3864
+ "Content-Type": "application/x-www-form-urlencoded"
3865
+ },
3866
+ body: formBody
3867
+ }
3868
+ );
3869
+ if (error) {
3870
+ return null;
3871
+ }
3872
+ const userMap = await options.mapProfileToUser?.(profile);
3873
+ if (!profile.user.email && !userMap?.email) {
3874
+ return null;
3875
+ }
3876
+ return {
3877
+ user: {
3878
+ id: profile.user.user_id,
3879
+ first_name: profile.user.first_name,
3880
+ last_name: profile.user.last_name,
3881
+ email: profile.user.email,
3882
+ image: profile.user.avatar,
3883
+ emailVerified: false,
3884
+ birthday: profile.user.birthday,
3885
+ sex: profile.user.sex,
3886
+ name: `${profile.user.first_name} ${profile.user.last_name}`,
3887
+ ...userMap
3888
+ },
3889
+ data: profile
3890
+ };
3891
+ },
3892
+ options
3893
+ };
3894
+ };
3895
+
3896
+ // src/auth/social-providers/wechat.ts
3897
+ var wechat = (options) => {
3898
+ return {
3899
+ id: "wechat",
3900
+ name: "WeChat",
3901
+ createAuthorizationURL({ state, scopes, redirectURI }) {
3902
+ const _scopes = options.disableDefaultScope ? [] : ["snsapi_login"];
3903
+ options.scope && _scopes.push(...options.scope);
3904
+ scopes && _scopes.push(...scopes);
3905
+ const url = new URL("https://open.weixin.qq.com/connect/qrconnect");
3906
+ url.searchParams.set("scope", _scopes.join(","));
3907
+ url.searchParams.set("response_type", "code");
3908
+ url.searchParams.set("appid", options.clientId);
3909
+ url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
3910
+ url.searchParams.set("state", state);
3911
+ url.searchParams.set("lang", options.lang || "cn");
3912
+ url.hash = "wechat_redirect";
3913
+ return url;
3914
+ },
3915
+ // WeChat uses non-standard token exchange (appid/secret instead of
3916
+ // client_id/client_secret, GET instead of POST), so shared helpers
3917
+ // like validateAuthorizationCode/getOAuth2Tokens cannot be used directly.
3918
+ validateAuthorizationCode: async ({ code }) => {
3919
+ const params = new URLSearchParams({
3920
+ appid: options.clientId,
3921
+ secret: options.clientSecret,
3922
+ code,
3923
+ grant_type: "authorization_code"
3924
+ });
3925
+ const { data: tokenData, error } = await athenaFetch(
3926
+ "https://api.weixin.qq.com/sns/oauth2/access_token?" + params.toString(),
3927
+ {
3928
+ method: "GET"
3929
+ }
3930
+ );
3931
+ if (error || !tokenData || tokenData.errcode) {
3932
+ throw new Error(
3933
+ `Failed to validate authorization code: ${tokenData?.errmsg || error?.message || "Unknown error"}`
3934
+ );
3935
+ }
3936
+ return {
3937
+ tokenType: "Bearer",
3938
+ accessToken: tokenData.access_token,
3939
+ refreshToken: tokenData.refresh_token,
3940
+ accessTokenExpiresAt: new Date(
3941
+ Date.now() + tokenData.expires_in * 1e3
3942
+ ),
3943
+ scopes: tokenData.scope.split(","),
3944
+ // WeChat requires openid for the userinfo endpoint, which is
3945
+ // returned alongside the access token.
3946
+ openid: tokenData.openid,
3947
+ unionid: tokenData.unionid
3948
+ };
3949
+ },
3950
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => {
3951
+ const params = new URLSearchParams({
3952
+ appid: options.clientId,
3953
+ grant_type: "refresh_token",
3954
+ refresh_token: refreshToken
3955
+ });
3956
+ const { data: tokenData, error } = await athenaFetch(
3957
+ "https://api.weixin.qq.com/sns/oauth2/refresh_token?" + params.toString(),
3958
+ {
3959
+ method: "GET"
3960
+ }
3961
+ );
3962
+ if (error || !tokenData || tokenData.errcode) {
3963
+ throw new Error(
3964
+ `Failed to refresh access token: ${tokenData?.errmsg || error?.message || "Unknown error"}`
3965
+ );
3966
+ }
3967
+ return {
3968
+ tokenType: "Bearer",
3969
+ accessToken: tokenData.access_token,
3970
+ refreshToken: tokenData.refresh_token,
3971
+ accessTokenExpiresAt: new Date(
3972
+ Date.now() + tokenData.expires_in * 1e3
3973
+ ),
3974
+ scopes: tokenData.scope.split(",")
3975
+ };
3976
+ },
3977
+ async getUserInfo(token) {
3978
+ if (options.getUserInfo) {
3979
+ return options.getUserInfo(token);
3980
+ }
3981
+ const openid = token.openid;
3982
+ if (!openid) {
3983
+ return null;
3984
+ }
3985
+ const params = new URLSearchParams({
3986
+ access_token: token.accessToken || "",
3987
+ openid,
3988
+ lang: "zh_CN"
3989
+ });
3990
+ const { data: profile, error } = await athenaFetch("https://api.weixin.qq.com/sns/userinfo?" + params.toString(), {
3991
+ method: "GET"
3992
+ });
3993
+ if (error || !profile || profile.errcode) {
3994
+ return null;
3995
+ }
3996
+ const userMap = await options.mapProfileToUser?.(profile);
3997
+ return {
3998
+ user: {
3999
+ id: profile.unionid || profile.openid || openid,
4000
+ name: profile.nickname,
4001
+ // WeChat does not return an email, and the OAuth callback rejects a
4002
+ // missing one, so the default sign-in would always fail. Synthesize a
4003
+ // stable, non-routable placeholder (RFC 2606 `.invalid`) keyed to the
4004
+ // user's WeChat id, left unverified. Applications that collect a real
4005
+ // email override it via `mapProfileToUser`.
4006
+ email: profile.email || `${profile.unionid || profile.openid || openid}@wechat.invalid`,
4007
+ image: profile.headimgurl,
4008
+ emailVerified: false,
4009
+ ...userMap
4010
+ },
4011
+ data: profile
4012
+ };
4013
+ },
4014
+ options
4015
+ };
4016
+ };
4017
+
4018
+ // src/auth/social-providers/zoom.ts
4019
+ var zoom = (userOptions) => {
4020
+ const options = {
4021
+ pkce: true,
4022
+ ...userOptions
4023
+ };
4024
+ return {
4025
+ id: "zoom",
4026
+ name: "Zoom",
4027
+ createAuthorizationURL: async ({ state, redirectURI, codeVerifier }) => {
4028
+ const params = new URLSearchParams({
4029
+ response_type: "code",
4030
+ redirect_uri: options.redirectURI ? options.redirectURI : redirectURI,
4031
+ client_id: options.clientId,
4032
+ state
4033
+ });
4034
+ if (options.pkce) {
4035
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
4036
+ params.set("code_challenge_method", "S256");
4037
+ params.set("code_challenge", codeChallenge);
4038
+ }
4039
+ const url = new URL("https://zoom.us/oauth/authorize");
4040
+ url.search = params.toString();
4041
+ return url;
4042
+ },
4043
+ validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
4044
+ return validateAuthorizationCode({
4045
+ code,
4046
+ redirectURI: options.redirectURI || redirectURI,
4047
+ codeVerifier,
4048
+ options,
4049
+ tokenEndpoint: "https://zoom.us/oauth/token",
4050
+ authentication: "post"
4051
+ });
4052
+ },
4053
+ refreshAccessToken: options.refreshAccessToken ? options.refreshAccessToken : async (refreshToken) => refreshAccessToken({
4054
+ refreshToken,
4055
+ options: {
4056
+ clientId: options.clientId,
4057
+ clientKey: options.clientKey,
4058
+ clientSecret: options.clientSecret
4059
+ },
4060
+ tokenEndpoint: "https://zoom.us/oauth/token"
4061
+ }),
4062
+ async getUserInfo(token) {
4063
+ if (options.getUserInfo) {
4064
+ return options.getUserInfo(token);
4065
+ }
4066
+ const { data: profile, error } = await athenaFetch(
4067
+ "https://api.zoom.us/v2/users/me",
4068
+ {
4069
+ headers: {
4070
+ authorization: `Bearer ${token.accessToken}`
4071
+ }
4072
+ }
4073
+ );
4074
+ if (error) {
4075
+ return null;
4076
+ }
4077
+ const userMap = await options.mapProfileToUser?.(profile);
4078
+ return {
4079
+ user: {
4080
+ id: profile.id,
4081
+ name: profile.display_name,
4082
+ image: profile.pic_url,
4083
+ email: profile.email,
4084
+ emailVerified: Boolean(profile.verified),
4085
+ ...userMap
4086
+ },
4087
+ data: {
4088
+ ...profile
4089
+ }
4090
+ };
4091
+ }
4092
+ };
4093
+ };
4094
+
4095
+ // src/auth/social-providers/registry.ts
4096
+ var socialProviders = {
4097
+ apple,
4098
+ atlassian,
4099
+ athena,
4100
+ cognito,
4101
+ discord,
4102
+ facebook,
4103
+ figma,
4104
+ github,
4105
+ microsoft,
4106
+ google,
4107
+ huggingface,
4108
+ slack,
4109
+ spotify,
4110
+ twitch,
4111
+ twitter,
4112
+ dropbox,
4113
+ kick,
4114
+ linear,
4115
+ linkedin,
4116
+ gitlab,
4117
+ tiktok,
4118
+ reddit,
4119
+ roblox,
4120
+ salesforce,
4121
+ vk,
4122
+ zoom,
4123
+ notion,
4124
+ kakao,
4125
+ naver,
4126
+ line,
4127
+ paybin,
4128
+ paypal,
4129
+ polar,
4130
+ railway,
4131
+ vercel,
4132
+ wechat
4133
+ };
4134
+ var socialProviderList = Object.keys(socialProviders);
4135
+ var SocialProviderListEnum = z__namespace.enum(socialProviderList).or(z__namespace.string());
4136
+
4137
+ exports.MICROSOFT_CONSUMER_TENANT_ID = MICROSOFT_CONSUMER_TENANT_ID;
4138
+ exports.SocialProviderListEnum = SocialProviderListEnum;
4139
+ exports.apple = apple;
4140
+ exports.athena = athena;
4141
+ exports.atlassian = atlassian;
4142
+ exports.cognito = cognito;
4143
+ exports.discord = discord;
4144
+ exports.dropbox = dropbox;
4145
+ exports.facebook = facebook;
4146
+ exports.figma = figma;
4147
+ exports.getApplePublicKey = getApplePublicKey;
4148
+ exports.getCognitoPublicKey = getCognitoPublicKey;
4149
+ exports.getGooglePublicKey = getGooglePublicKey;
4150
+ exports.getJwksPublicKey = getJwksPublicKey;
4151
+ exports.getMicrosoftPublicKey = getMicrosoftPublicKey;
4152
+ exports.getPayPalPublicKey = getPayPalPublicKey;
4153
+ exports.github = github;
4154
+ exports.gitlab = gitlab;
4155
+ exports.google = google;
4156
+ exports.huggingface = huggingface;
4157
+ exports.isGoogleHostedDomainAllowed = isGoogleHostedDomainAllowed;
4158
+ exports.kakao = kakao;
4159
+ exports.kick = kick;
4160
+ exports.line = line;
4161
+ exports.linear = linear;
4162
+ exports.linkedin = linkedin;
4163
+ exports.mergeScopes = mergeScopes;
4164
+ exports.microsoft = microsoft;
4165
+ exports.naver = naver;
4166
+ exports.notion = notion;
4167
+ exports.paybin = paybin;
4168
+ exports.paypal = paypal;
4169
+ exports.polar = polar;
4170
+ exports.railway = railway;
4171
+ exports.reddit = reddit;
4172
+ exports.roblox = roblox;
4173
+ exports.salesforce = salesforce;
4174
+ exports.slack = slack;
4175
+ exports.socialProviderList = socialProviderList;
4176
+ exports.socialProviders = socialProviders;
4177
+ exports.spotify = spotify;
4178
+ exports.tiktok = tiktok;
4179
+ exports.trimTrailingSlash = trimTrailingSlash;
4180
+ exports.twitch = twitch;
4181
+ exports.twitter = twitter;
4182
+ exports.vercel = vercel;
4183
+ exports.verifyFacebookAccessToken = verifyFacebookAccessToken;
4184
+ exports.verifyGoogleIdToken = verifyGoogleIdToken;
4185
+ exports.vk = vk;
4186
+ exports.wechat = wechat;
4187
+ exports.zoom = zoom;
4188
+ //# sourceMappingURL=social-providers.cjs.map
4189
+ //# sourceMappingURL=social-providers.cjs.map