@leanmcp/auth 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -661
- package/dist/{auth0-DMNC3QWJ.mjs → auth0-UTD4QBG6.mjs} +4 -2
- package/dist/chunk-LPEX4YW6.mjs +13 -0
- package/dist/{chunk-ESHQ6BRM.mjs → chunk-P4HFKA5R.mjs} +7 -7
- package/dist/chunk-RGCCBQWG.mjs +113 -0
- package/dist/chunk-ZOPKMOPV.mjs +53 -0
- package/dist/{clerk-7PVVTTC7.mjs → clerk-3SDKGD6C.mjs} +4 -2
- package/dist/client/index.d.mts +499 -0
- package/dist/client/index.d.ts +499 -0
- package/dist/client/index.js +1039 -0
- package/dist/client/index.mjs +869 -0
- package/dist/{cognito-5Q5HGYMA.mjs → cognito-QQT7LK2Y.mjs} +4 -2
- package/dist/index.mjs +2 -1
- package/dist/{leanmcp-X6BD6HOJ.mjs → leanmcp-Y7TXNSTD.mjs} +4 -2
- package/dist/proxy/index.d.mts +376 -0
- package/dist/proxy/index.d.ts +376 -0
- package/dist/proxy/index.js +536 -0
- package/dist/proxy/index.mjs +480 -0
- package/dist/server/index.d.mts +496 -0
- package/dist/server/index.d.ts +496 -0
- package/dist/server/index.js +882 -0
- package/dist/server/index.mjs +847 -0
- package/dist/storage/index.d.mts +181 -0
- package/dist/storage/index.d.ts +181 -0
- package/dist/storage/index.js +499 -0
- package/dist/storage/index.mjs +372 -0
- package/dist/types-DMpGN530.d.mts +122 -0
- package/dist/types-DMpGN530.d.ts +122 -0
- package/package.json +40 -7
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/proxy/index.ts
|
|
22
|
+
var proxy_exports = {};
|
|
23
|
+
__export(proxy_exports, {
|
|
24
|
+
OAuthProxy: () => OAuthProxy,
|
|
25
|
+
azureProvider: () => azureProvider,
|
|
26
|
+
customProvider: () => customProvider,
|
|
27
|
+
discordProvider: () => discordProvider,
|
|
28
|
+
githubProvider: () => githubProvider,
|
|
29
|
+
gitlabProvider: () => gitlabProvider,
|
|
30
|
+
googleProvider: () => googleProvider,
|
|
31
|
+
slackProvider: () => slackProvider
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(proxy_exports);
|
|
34
|
+
|
|
35
|
+
// src/proxy/providers.ts
|
|
36
|
+
function googleProvider(options) {
|
|
37
|
+
return {
|
|
38
|
+
id: "google",
|
|
39
|
+
name: "Google",
|
|
40
|
+
authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
41
|
+
tokenEndpoint: "https://oauth2.googleapis.com/token",
|
|
42
|
+
userInfoEndpoint: "https://www.googleapis.com/oauth2/v3/userinfo",
|
|
43
|
+
clientId: options.clientId,
|
|
44
|
+
clientSecret: options.clientSecret,
|
|
45
|
+
scopes: options.scopes ?? [
|
|
46
|
+
"openid",
|
|
47
|
+
"email",
|
|
48
|
+
"profile"
|
|
49
|
+
],
|
|
50
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
51
|
+
supportsPkce: true,
|
|
52
|
+
authorizationParams: {
|
|
53
|
+
access_type: "offline",
|
|
54
|
+
prompt: "consent"
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
__name(googleProvider, "googleProvider");
|
|
59
|
+
function githubProvider(options) {
|
|
60
|
+
return {
|
|
61
|
+
id: "github",
|
|
62
|
+
name: "GitHub",
|
|
63
|
+
authorizationEndpoint: "https://github.com/login/oauth/authorize",
|
|
64
|
+
tokenEndpoint: "https://github.com/login/oauth/access_token",
|
|
65
|
+
userInfoEndpoint: "https://api.github.com/user",
|
|
66
|
+
clientId: options.clientId,
|
|
67
|
+
clientSecret: options.clientSecret,
|
|
68
|
+
scopes: options.scopes ?? [
|
|
69
|
+
"read:user",
|
|
70
|
+
"user:email"
|
|
71
|
+
],
|
|
72
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
73
|
+
supportsPkce: false
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
__name(githubProvider, "githubProvider");
|
|
77
|
+
function azureProvider(options) {
|
|
78
|
+
const tenantId = options.tenantId ?? "common";
|
|
79
|
+
return {
|
|
80
|
+
id: "azure",
|
|
81
|
+
name: "Microsoft",
|
|
82
|
+
authorizationEndpoint: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`,
|
|
83
|
+
tokenEndpoint: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
|
|
84
|
+
userInfoEndpoint: "https://graph.microsoft.com/oidc/userinfo",
|
|
85
|
+
clientId: options.clientId,
|
|
86
|
+
clientSecret: options.clientSecret,
|
|
87
|
+
scopes: options.scopes ?? [
|
|
88
|
+
"openid",
|
|
89
|
+
"email",
|
|
90
|
+
"profile",
|
|
91
|
+
"offline_access"
|
|
92
|
+
],
|
|
93
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
94
|
+
supportsPkce: true
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
__name(azureProvider, "azureProvider");
|
|
98
|
+
function gitlabProvider(options) {
|
|
99
|
+
const baseUrl = options.baseUrl ?? "https://gitlab.com";
|
|
100
|
+
return {
|
|
101
|
+
id: "gitlab",
|
|
102
|
+
name: "GitLab",
|
|
103
|
+
authorizationEndpoint: `${baseUrl}/oauth/authorize`,
|
|
104
|
+
tokenEndpoint: `${baseUrl}/oauth/token`,
|
|
105
|
+
userInfoEndpoint: `${baseUrl}/api/v4/user`,
|
|
106
|
+
clientId: options.clientId,
|
|
107
|
+
clientSecret: options.clientSecret,
|
|
108
|
+
scopes: options.scopes ?? [
|
|
109
|
+
"openid",
|
|
110
|
+
"read_user",
|
|
111
|
+
"email"
|
|
112
|
+
],
|
|
113
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
114
|
+
supportsPkce: true
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
__name(gitlabProvider, "gitlabProvider");
|
|
118
|
+
function slackProvider(options) {
|
|
119
|
+
return {
|
|
120
|
+
id: "slack",
|
|
121
|
+
name: "Slack",
|
|
122
|
+
authorizationEndpoint: "https://slack.com/oauth/v2/authorize",
|
|
123
|
+
tokenEndpoint: "https://slack.com/api/oauth.v2.access",
|
|
124
|
+
userInfoEndpoint: "https://slack.com/api/users.identity",
|
|
125
|
+
clientId: options.clientId,
|
|
126
|
+
clientSecret: options.clientSecret,
|
|
127
|
+
scopes: options.scopes ?? [
|
|
128
|
+
"openid",
|
|
129
|
+
"email",
|
|
130
|
+
"profile"
|
|
131
|
+
],
|
|
132
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
133
|
+
supportsPkce: false
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
__name(slackProvider, "slackProvider");
|
|
137
|
+
function discordProvider(options) {
|
|
138
|
+
return {
|
|
139
|
+
id: "discord",
|
|
140
|
+
name: "Discord",
|
|
141
|
+
authorizationEndpoint: "https://discord.com/api/oauth2/authorize",
|
|
142
|
+
tokenEndpoint: "https://discord.com/api/oauth2/token",
|
|
143
|
+
userInfoEndpoint: "https://discord.com/api/users/@me",
|
|
144
|
+
clientId: options.clientId,
|
|
145
|
+
clientSecret: options.clientSecret,
|
|
146
|
+
scopes: options.scopes ?? [
|
|
147
|
+
"identify",
|
|
148
|
+
"email"
|
|
149
|
+
],
|
|
150
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
151
|
+
supportsPkce: true
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
__name(discordProvider, "discordProvider");
|
|
155
|
+
function customProvider(config) {
|
|
156
|
+
return {
|
|
157
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
158
|
+
supportsPkce: false,
|
|
159
|
+
...config
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
__name(customProvider, "customProvider");
|
|
163
|
+
|
|
164
|
+
// src/proxy/oauth-proxy.ts
|
|
165
|
+
var import_crypto2 = require("crypto");
|
|
166
|
+
|
|
167
|
+
// src/client/pkce.ts
|
|
168
|
+
var import_crypto = require("crypto");
|
|
169
|
+
function generateCodeVerifier(length = 64) {
|
|
170
|
+
if (length < 43 || length > 128) {
|
|
171
|
+
throw new Error("PKCE code verifier must be between 43-128 characters");
|
|
172
|
+
}
|
|
173
|
+
const bytesNeeded = Math.ceil(length * 0.75);
|
|
174
|
+
const randomBuffer = (0, import_crypto.randomBytes)(bytesNeeded);
|
|
175
|
+
return randomBuffer.toString("base64url").slice(0, length);
|
|
176
|
+
}
|
|
177
|
+
__name(generateCodeVerifier, "generateCodeVerifier");
|
|
178
|
+
function generateCodeChallenge(verifier) {
|
|
179
|
+
return (0, import_crypto.createHash)("sha256").update(verifier, "utf8").digest("base64url");
|
|
180
|
+
}
|
|
181
|
+
__name(generateCodeChallenge, "generateCodeChallenge");
|
|
182
|
+
function generatePKCE(verifierLength = 64) {
|
|
183
|
+
const verifier = generateCodeVerifier(verifierLength);
|
|
184
|
+
const challenge = generateCodeChallenge(verifier);
|
|
185
|
+
return {
|
|
186
|
+
verifier,
|
|
187
|
+
challenge,
|
|
188
|
+
method: "S256"
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
__name(generatePKCE, "generatePKCE");
|
|
192
|
+
|
|
193
|
+
// src/proxy/oauth-proxy.ts
|
|
194
|
+
var defaultTokenMapper = /* @__PURE__ */ __name(async (externalTokens, userInfo) => {
|
|
195
|
+
return {
|
|
196
|
+
access_token: externalTokens.access_token,
|
|
197
|
+
token_type: externalTokens.token_type,
|
|
198
|
+
expires_in: externalTokens.expires_in,
|
|
199
|
+
refresh_token: externalTokens.refresh_token,
|
|
200
|
+
user_id: userInfo.sub
|
|
201
|
+
};
|
|
202
|
+
}, "defaultTokenMapper");
|
|
203
|
+
var pendingRequests = /* @__PURE__ */ new Map();
|
|
204
|
+
var REQUEST_TTL_MS = 10 * 60 * 1e3;
|
|
205
|
+
function cleanupExpiredRequests() {
|
|
206
|
+
const now = Date.now();
|
|
207
|
+
for (const [key, request] of pendingRequests.entries()) {
|
|
208
|
+
if (now - request.createdAt > REQUEST_TTL_MS) {
|
|
209
|
+
pendingRequests.delete(key);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
__name(cleanupExpiredRequests, "cleanupExpiredRequests");
|
|
214
|
+
setInterval(cleanupExpiredRequests, 60 * 1e3);
|
|
215
|
+
var OAuthProxy = class {
|
|
216
|
+
static {
|
|
217
|
+
__name(this, "OAuthProxy");
|
|
218
|
+
}
|
|
219
|
+
config;
|
|
220
|
+
providersMap;
|
|
221
|
+
constructor(config) {
|
|
222
|
+
this.config = {
|
|
223
|
+
authorizePath: "/authorize",
|
|
224
|
+
tokenPath: "/token",
|
|
225
|
+
callbackPath: "/callback",
|
|
226
|
+
tokenMapper: defaultTokenMapper,
|
|
227
|
+
forwardPkce: true,
|
|
228
|
+
...config
|
|
229
|
+
};
|
|
230
|
+
this.providersMap = /* @__PURE__ */ new Map();
|
|
231
|
+
for (const provider of config.providers) {
|
|
232
|
+
this.providersMap.set(provider.id, provider);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get configured providers
|
|
237
|
+
*/
|
|
238
|
+
getProviders() {
|
|
239
|
+
return this.config.providers;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get a provider by ID
|
|
243
|
+
*/
|
|
244
|
+
getProvider(id) {
|
|
245
|
+
return this.providersMap.get(id);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Generate state parameter with signature
|
|
249
|
+
*/
|
|
250
|
+
generateState() {
|
|
251
|
+
const nonce = (0, import_crypto2.randomUUID)();
|
|
252
|
+
const signature = (0, import_crypto2.createHmac)("sha256", this.config.sessionSecret).update(nonce).digest("hex").substring(0, 8);
|
|
253
|
+
return `${nonce}.${signature}`;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Verify state parameter signature
|
|
257
|
+
*/
|
|
258
|
+
verifyState(state) {
|
|
259
|
+
const [nonce, signature] = state.split(".");
|
|
260
|
+
if (!nonce || !signature) return false;
|
|
261
|
+
const expectedSignature = (0, import_crypto2.createHmac)("sha256", this.config.sessionSecret).update(nonce).digest("hex").substring(0, 8);
|
|
262
|
+
return signature === expectedSignature;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Handle authorization request
|
|
266
|
+
*
|
|
267
|
+
* Redirects the user to the external provider's authorization page.
|
|
268
|
+
*
|
|
269
|
+
* @param params - Request parameters
|
|
270
|
+
* @returns URL to redirect the user to
|
|
271
|
+
*/
|
|
272
|
+
handleAuthorize(params) {
|
|
273
|
+
const provider = this.providersMap.get(params.provider);
|
|
274
|
+
if (!provider) {
|
|
275
|
+
throw new Error(`Unknown provider: ${params.provider}`);
|
|
276
|
+
}
|
|
277
|
+
const proxyState = this.generateState();
|
|
278
|
+
let codeVerifier;
|
|
279
|
+
let codeChallenge;
|
|
280
|
+
if (provider.supportsPkce && this.config.forwardPkce) {
|
|
281
|
+
const pkce = generatePKCE();
|
|
282
|
+
codeVerifier = pkce.verifier;
|
|
283
|
+
codeChallenge = pkce.challenge;
|
|
284
|
+
}
|
|
285
|
+
const pendingRequest = {
|
|
286
|
+
providerId: params.provider,
|
|
287
|
+
clientRedirectUri: params.redirect_uri,
|
|
288
|
+
clientState: params.state,
|
|
289
|
+
codeVerifier,
|
|
290
|
+
proxyState,
|
|
291
|
+
createdAt: Date.now()
|
|
292
|
+
};
|
|
293
|
+
pendingRequests.set(proxyState, pendingRequest);
|
|
294
|
+
const callbackUrl = `${this.config.baseUrl}${this.config.callbackPath}`;
|
|
295
|
+
const authUrl = new URL(provider.authorizationEndpoint);
|
|
296
|
+
authUrl.searchParams.set("client_id", provider.clientId);
|
|
297
|
+
authUrl.searchParams.set("redirect_uri", callbackUrl);
|
|
298
|
+
authUrl.searchParams.set("response_type", "code");
|
|
299
|
+
authUrl.searchParams.set("state", proxyState);
|
|
300
|
+
const scopes = params.scope?.split(" ") ?? provider.scopes;
|
|
301
|
+
authUrl.searchParams.set("scope", scopes.join(" "));
|
|
302
|
+
if (codeChallenge) {
|
|
303
|
+
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
304
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
305
|
+
}
|
|
306
|
+
if (provider.authorizationParams) {
|
|
307
|
+
for (const [key, value] of Object.entries(provider.authorizationParams)) {
|
|
308
|
+
authUrl.searchParams.set(key, value);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return authUrl.toString();
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Handle callback from external provider
|
|
315
|
+
*
|
|
316
|
+
* Exchanges the authorization code for tokens and maps them.
|
|
317
|
+
*
|
|
318
|
+
* @param params - Callback query parameters
|
|
319
|
+
* @returns Result with redirect URI and tokens
|
|
320
|
+
*/
|
|
321
|
+
async handleCallback(params) {
|
|
322
|
+
const { code, state, error, error_description } = params;
|
|
323
|
+
if (error) {
|
|
324
|
+
const pendingRequest2 = state ? pendingRequests.get(state) : void 0;
|
|
325
|
+
pendingRequests.delete(state ?? "");
|
|
326
|
+
const redirectUri2 = new URL(pendingRequest2?.clientRedirectUri ?? "/");
|
|
327
|
+
redirectUri2.searchParams.set("error", error);
|
|
328
|
+
if (error_description) {
|
|
329
|
+
redirectUri2.searchParams.set("error_description", error_description);
|
|
330
|
+
}
|
|
331
|
+
if (pendingRequest2?.clientState) {
|
|
332
|
+
redirectUri2.searchParams.set("state", pendingRequest2.clientState);
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
redirectUri: redirectUri2.toString(),
|
|
336
|
+
error
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
if (!state || !this.verifyState(state)) {
|
|
340
|
+
throw new Error("Invalid or missing state parameter");
|
|
341
|
+
}
|
|
342
|
+
const pendingRequest = pendingRequests.get(state);
|
|
343
|
+
if (!pendingRequest) {
|
|
344
|
+
throw new Error("State not found - authorization request expired");
|
|
345
|
+
}
|
|
346
|
+
pendingRequests.delete(state);
|
|
347
|
+
if (!code) {
|
|
348
|
+
throw new Error("Missing authorization code");
|
|
349
|
+
}
|
|
350
|
+
const provider = this.providersMap.get(pendingRequest.providerId);
|
|
351
|
+
if (!provider) {
|
|
352
|
+
throw new Error(`Provider not found: ${pendingRequest.providerId}`);
|
|
353
|
+
}
|
|
354
|
+
const callbackUrl = `${this.config.baseUrl}${this.config.callbackPath}`;
|
|
355
|
+
const externalTokens = await this.exchangeCodeForTokens(provider, code, callbackUrl, pendingRequest.codeVerifier);
|
|
356
|
+
let userInfo = {
|
|
357
|
+
sub: "unknown"
|
|
358
|
+
};
|
|
359
|
+
if (provider.userInfoEndpoint) {
|
|
360
|
+
userInfo = await this.fetchUserInfo(provider, externalTokens.access_token);
|
|
361
|
+
}
|
|
362
|
+
const mappedTokens = await this.config.tokenMapper(externalTokens, userInfo, provider);
|
|
363
|
+
const redirectUri = new URL(pendingRequest.clientRedirectUri);
|
|
364
|
+
const internalCode = this.generateInternalCode(mappedTokens);
|
|
365
|
+
redirectUri.searchParams.set("code", internalCode);
|
|
366
|
+
if (pendingRequest.clientState) {
|
|
367
|
+
redirectUri.searchParams.set("state", pendingRequest.clientState);
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
redirectUri: redirectUri.toString(),
|
|
371
|
+
tokens: mappedTokens
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Exchange authorization code for tokens with external provider
|
|
376
|
+
*/
|
|
377
|
+
async exchangeCodeForTokens(provider, code, redirectUri, codeVerifier) {
|
|
378
|
+
const tokenPayload = {
|
|
379
|
+
grant_type: "authorization_code",
|
|
380
|
+
code,
|
|
381
|
+
redirect_uri: redirectUri,
|
|
382
|
+
client_id: provider.clientId
|
|
383
|
+
};
|
|
384
|
+
if (provider.tokenEndpointAuthMethod === "client_secret_post") {
|
|
385
|
+
tokenPayload.client_secret = provider.clientSecret;
|
|
386
|
+
}
|
|
387
|
+
if (codeVerifier) {
|
|
388
|
+
tokenPayload.code_verifier = codeVerifier;
|
|
389
|
+
}
|
|
390
|
+
if (provider.tokenParams) {
|
|
391
|
+
for (const [key, value] of Object.entries(provider.tokenParams)) {
|
|
392
|
+
tokenPayload[key] = value;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
const headers = {
|
|
396
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
397
|
+
"Accept": "application/json"
|
|
398
|
+
};
|
|
399
|
+
if (provider.tokenEndpointAuthMethod === "client_secret_basic") {
|
|
400
|
+
const credentials = Buffer.from(`${provider.clientId}:${provider.clientSecret}`).toString("base64");
|
|
401
|
+
headers["Authorization"] = `Basic ${credentials}`;
|
|
402
|
+
}
|
|
403
|
+
const response = await fetch(provider.tokenEndpoint, {
|
|
404
|
+
method: "POST",
|
|
405
|
+
headers,
|
|
406
|
+
body: new URLSearchParams(tokenPayload)
|
|
407
|
+
});
|
|
408
|
+
if (!response.ok) {
|
|
409
|
+
const errorText = await response.text();
|
|
410
|
+
throw new Error(`Token exchange failed: ${response.status} - ${errorText}`);
|
|
411
|
+
}
|
|
412
|
+
return response.json();
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Fetch user info from external provider
|
|
416
|
+
*/
|
|
417
|
+
async fetchUserInfo(provider, accessToken) {
|
|
418
|
+
if (!provider.userInfoEndpoint) {
|
|
419
|
+
return {
|
|
420
|
+
sub: "unknown"
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const response = await fetch(provider.userInfoEndpoint, {
|
|
424
|
+
headers: {
|
|
425
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
426
|
+
"Accept": "application/json"
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
if (!response.ok) {
|
|
430
|
+
console.warn(`Failed to fetch user info: ${response.status}`);
|
|
431
|
+
return {
|
|
432
|
+
sub: "unknown"
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
const data = await response.json();
|
|
436
|
+
return {
|
|
437
|
+
sub: data.sub ?? data.id ?? data.user_id ?? "unknown",
|
|
438
|
+
email: data.email,
|
|
439
|
+
email_verified: data.email_verified ?? data.verified_email,
|
|
440
|
+
name: data.name ?? data.login ?? data.display_name,
|
|
441
|
+
picture: data.picture ?? data.avatar_url ?? data.avatar,
|
|
442
|
+
raw: data
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Generate an internal authorization code for the mapped tokens
|
|
447
|
+
* This code can be exchanged via the token endpoint
|
|
448
|
+
*/
|
|
449
|
+
generateInternalCode(tokens) {
|
|
450
|
+
const code = (0, import_crypto2.randomUUID)();
|
|
451
|
+
const signature = (0, import_crypto2.createHmac)("sha256", this.config.sessionSecret).update(code).update(JSON.stringify(tokens)).digest("hex").substring(0, 16);
|
|
452
|
+
const fullCode = `${code}.${signature}`;
|
|
453
|
+
pendingRequests.set(fullCode, {
|
|
454
|
+
providerId: "_internal",
|
|
455
|
+
clientRedirectUri: "",
|
|
456
|
+
proxyState: fullCode,
|
|
457
|
+
createdAt: Date.now(),
|
|
458
|
+
// @ts-expect-error - storing tokens in pending request
|
|
459
|
+
_tokens: tokens
|
|
460
|
+
});
|
|
461
|
+
setTimeout(() => pendingRequests.delete(fullCode), 5 * 60 * 1e3);
|
|
462
|
+
return fullCode;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Handle token request (exchange internal code for tokens)
|
|
466
|
+
*/
|
|
467
|
+
async handleToken(params) {
|
|
468
|
+
const { grant_type, code, refresh_token } = params;
|
|
469
|
+
if (grant_type === "authorization_code") {
|
|
470
|
+
if (!code) {
|
|
471
|
+
throw new Error("Missing code parameter");
|
|
472
|
+
}
|
|
473
|
+
const pending = pendingRequests.get(code);
|
|
474
|
+
if (!pending || !pending._tokens) {
|
|
475
|
+
throw new Error("Invalid or expired code");
|
|
476
|
+
}
|
|
477
|
+
pendingRequests.delete(code);
|
|
478
|
+
return pending._tokens;
|
|
479
|
+
}
|
|
480
|
+
if (grant_type === "refresh_token") {
|
|
481
|
+
if (!refresh_token) {
|
|
482
|
+
throw new Error("Missing refresh_token parameter");
|
|
483
|
+
}
|
|
484
|
+
throw new Error("Refresh token grant not implemented - use external provider directly");
|
|
485
|
+
}
|
|
486
|
+
throw new Error(`Unsupported grant_type: ${grant_type}`);
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Express/Connect middleware factory
|
|
490
|
+
*/
|
|
491
|
+
createMiddleware() {
|
|
492
|
+
return {
|
|
493
|
+
authorize: /* @__PURE__ */ __name((req, res) => {
|
|
494
|
+
try {
|
|
495
|
+
const url = this.handleAuthorize(req.query);
|
|
496
|
+
res.redirect(url);
|
|
497
|
+
} catch (error) {
|
|
498
|
+
res.status(400).json({
|
|
499
|
+
error: error.message
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}, "authorize"),
|
|
503
|
+
callback: /* @__PURE__ */ __name(async (req, res) => {
|
|
504
|
+
try {
|
|
505
|
+
const result = await this.handleCallback(req.query);
|
|
506
|
+
res.redirect(result.redirectUri);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
res.status(400).json({
|
|
509
|
+
error: error.message
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}, "callback"),
|
|
513
|
+
token: /* @__PURE__ */ __name(async (req, res) => {
|
|
514
|
+
try {
|
|
515
|
+
const tokens = await this.handleToken(req.body);
|
|
516
|
+
res.json(tokens);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
res.status(400).json({
|
|
519
|
+
error: error.message
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}, "token")
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
527
|
+
0 && (module.exports = {
|
|
528
|
+
OAuthProxy,
|
|
529
|
+
azureProvider,
|
|
530
|
+
customProvider,
|
|
531
|
+
discordProvider,
|
|
532
|
+
githubProvider,
|
|
533
|
+
gitlabProvider,
|
|
534
|
+
googleProvider,
|
|
535
|
+
slackProvider
|
|
536
|
+
});
|