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