@lenne.tech/nest-server 11.42.16 → 11.43.1
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/.claude/rules/better-auth.md +22 -0
- package/.claude/rules/configurable-features.md +3 -3
- package/.claude/rules/testing.md +4 -4
- package/FRAMEWORK-API.md +2 -2
- package/dist/core/common/interfaces/server-options.interface.d.ts +3 -0
- package/dist/core/modules/ai/core-ai-mcp-oauth.initializer.d.ts +19 -0
- package/dist/core/modules/ai/core-ai-mcp-oauth.initializer.js +145 -0
- package/dist/core/modules/ai/core-ai-mcp-oauth.initializer.js.map +1 -0
- package/dist/core/modules/ai/core-ai-mcp.controller.js +6 -5
- package/dist/core/modules/ai/core-ai-mcp.controller.js.map +1 -1
- package/dist/core/modules/ai/core-ai.module.d.ts +2 -0
- package/dist/core/modules/ai/core-ai.module.js +6 -1
- package/dist/core/modules/ai/core-ai.module.js.map +1 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.d.ts +33 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.js +282 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.js.map +1 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.d.ts +15 -2
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js +73 -19
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js.map +1 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.registry.d.ts +5 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.registry.js.map +1 -1
- package/dist/core/modules/ai/index.d.ts +1 -0
- package/dist/core/modules/ai/index.js +1 -0
- package/dist/core/modules/ai/index.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.d.ts +30 -2
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js +222 -15
- package/dist/core/modules/ai/services/core-ai-mcp-oauth.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.config.d.ts +4 -0
- package/dist/core/modules/better-auth/better-auth.config.js +29 -2
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +2 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.js +36 -1
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.service.js +9 -0
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +1 -1
- package/migration-guides/11.42.x-to-11.43.x.md +195 -0
- package/migration-guides/11.43.0-to-11.43.1.md +73 -0
- package/package.json +4 -4
- package/src/core/common/helpers/swagger.helper.ts +4 -4
- package/src/core/common/interfaces/server-options.interface.ts +37 -9
- package/src/core/modules/ai/INTEGRATION-CHECKLIST.md +50 -24
- package/src/core/modules/ai/README.md +98 -13
- package/src/core/modules/ai/core-ai-mcp-oauth.initializer.ts +199 -0
- package/src/core/modules/ai/core-ai-mcp.controller.ts +13 -6
- package/src/core/modules/ai/core-ai.module.ts +14 -2
- package/src/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.ts +369 -0
- package/src/core/modules/ai/helpers/ai-mcp-oauth.helper.ts +149 -43
- package/src/core/modules/ai/helpers/ai-mcp-oauth.registry.ts +18 -5
- package/src/core/modules/ai/index.ts +1 -0
- package/src/core/modules/ai/services/core-ai-mcp-oauth.service.ts +385 -28
- package/src/core/modules/better-auth/better-auth.config.ts +49 -5
- package/src/core/modules/better-auth/core-better-auth.controller.ts +75 -1
- package/src/core/modules/better-auth/core-better-auth.service.ts +21 -3
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MCP_OAUTH_CONSENT_SCRIPT = exports.MCP_OAUTH_CONSENT_GUARD_SCRIPT = exports.MCP_OAUTH_CONSENT_TOKEN_FIELD = exports.MCP_OAUTH_CONSENT_DECISION_FIELD = void 0;
|
|
4
|
+
exports.mcpOAuthConsentLocale = mcpOAuthConsentLocale;
|
|
5
|
+
exports.mcpOAuthConsentHeaders = mcpOAuthConsentHeaders;
|
|
6
|
+
exports.renderMcpOAuthConsentPage = renderMcpOAuthConsentPage;
|
|
7
|
+
exports.MCP_OAUTH_CONSENT_DECISION_FIELD = 'lt_mcp_consent';
|
|
8
|
+
exports.MCP_OAUTH_CONSENT_TOKEN_FIELD = 'lt_mcp_csrf';
|
|
9
|
+
const TEXTS = {
|
|
10
|
+
de: {
|
|
11
|
+
allow: 'Zulassen',
|
|
12
|
+
approveNoscript: 'Zum Bestätigen wird JavaScript benötigt.',
|
|
13
|
+
backupCode: 'Backup-Code verwenden',
|
|
14
|
+
code: 'Bestätigungscode',
|
|
15
|
+
codeFailed: 'Der Code ist ungültig.',
|
|
16
|
+
codeHint: 'Gib den Code aus deiner Authenticator-App ein.',
|
|
17
|
+
confirm: 'Bestätigen',
|
|
18
|
+
continue: 'Ich habe mich angemeldet – weiter',
|
|
19
|
+
deny: 'Ablehnen',
|
|
20
|
+
email: 'E-Mail',
|
|
21
|
+
expired: 'Die Seite war zu lange geöffnet. Bitte bestätige noch einmal.',
|
|
22
|
+
intro: '{client} möchte über MCP in deinem Namen auf {app} zugreifen – mit deinen Rechten.',
|
|
23
|
+
loopback: 'eine Anwendung auf diesem Computer',
|
|
24
|
+
noscript: 'Für die Anmeldung wird JavaScript benötigt.',
|
|
25
|
+
noSignIn: 'Auf diesem Server ist keine Anmeldung für MCP-Clients eingerichtet.',
|
|
26
|
+
otherMethod: 'Mit Passkey oder einem anderen Verfahren anmelden',
|
|
27
|
+
password: 'Passwort',
|
|
28
|
+
signedInAs: 'Angemeldet als {user}',
|
|
29
|
+
switchAccount: 'Anderes Konto',
|
|
30
|
+
signIn: 'Anmelden',
|
|
31
|
+
signInFailed: 'Anmeldung fehlgeschlagen. Bitte prüfe E-Mail und Passwort.',
|
|
32
|
+
signInIntro: 'Melde dich bei {app} an, um {client} den Zugriff zu erlauben.',
|
|
33
|
+
target: 'Die Freigabe geht an:',
|
|
34
|
+
title: 'Zugriff erlauben',
|
|
35
|
+
unnamedClient: 'Eine unbenannte Anwendung',
|
|
36
|
+
warning: 'Erlaube den Zugriff nur, wenn du die Verbindung gerade selbst gestartet hast.',
|
|
37
|
+
},
|
|
38
|
+
en: {
|
|
39
|
+
allow: 'Allow',
|
|
40
|
+
approveNoscript: 'Confirming requires JavaScript.',
|
|
41
|
+
backupCode: 'Use a backup code',
|
|
42
|
+
code: 'Verification code',
|
|
43
|
+
codeFailed: 'The code is invalid.',
|
|
44
|
+
codeHint: 'Enter the code from your authenticator app.',
|
|
45
|
+
confirm: 'Confirm',
|
|
46
|
+
continue: 'I have signed in – continue',
|
|
47
|
+
deny: 'Deny',
|
|
48
|
+
email: 'Email',
|
|
49
|
+
expired: 'This page was open for too long. Please confirm again.',
|
|
50
|
+
intro: '{client} wants to access {app} on your behalf through MCP – with your permissions.',
|
|
51
|
+
loopback: 'an application on this computer',
|
|
52
|
+
noscript: 'Signing in requires JavaScript.',
|
|
53
|
+
noSignIn: 'Sign-in for MCP clients is not set up on this server.',
|
|
54
|
+
otherMethod: 'Sign in with a passkey or another method',
|
|
55
|
+
password: 'Password',
|
|
56
|
+
signedInAs: 'Signed in as {user}',
|
|
57
|
+
switchAccount: 'Use another account',
|
|
58
|
+
signIn: 'Sign in',
|
|
59
|
+
signInFailed: 'Sign-in failed. Please check your email and password.',
|
|
60
|
+
signInIntro: 'Sign in to {app} to allow {client} access.',
|
|
61
|
+
target: 'Access is granted to:',
|
|
62
|
+
title: 'Allow access',
|
|
63
|
+
unnamedClient: 'An unnamed application',
|
|
64
|
+
warning: 'Only allow access if you just started this connection yourself.',
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
function mcpOAuthConsentLocale(acceptLanguage) {
|
|
68
|
+
const first = typeof acceptLanguage === 'string' ? acceptLanguage.split(',')[0].trim().toLowerCase() : '';
|
|
69
|
+
return first.startsWith('de') ? 'de' : 'en';
|
|
70
|
+
}
|
|
71
|
+
function mcpOAuthConsentHeaders(nonce) {
|
|
72
|
+
return {
|
|
73
|
+
'Cache-Control': 'no-store',
|
|
74
|
+
'Content-Security-Policy': `default-src 'none'; script-src 'nonce-${nonce}'; style-src 'nonce-${nonce}'; connect-src 'self'; ` +
|
|
75
|
+
"img-src 'self' data:; base-uri 'none'; frame-ancestors 'none'",
|
|
76
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
77
|
+
'Referrer-Policy': 'no-referrer',
|
|
78
|
+
'X-Content-Type-Options': 'nosniff',
|
|
79
|
+
'X-Frame-Options': 'DENY',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function renderMcpOAuthConsentPage(view) {
|
|
83
|
+
const t = TEXTS[view.locale];
|
|
84
|
+
const clientName = view.client.name?.trim() || t.unnamedClient;
|
|
85
|
+
const fill = (text) => escapeHtml(text).replace(/\{(client|app)\}/g, (_, key) => `<strong>${escapeHtml(key === 'client' ? clientName : view.appHost)}</strong>`);
|
|
86
|
+
const body = view.user ? consentView(view, t, fill) : signInView(view, t, fill);
|
|
87
|
+
return `<!doctype html>
|
|
88
|
+
<html lang="${view.locale}">
|
|
89
|
+
<head>
|
|
90
|
+
<meta charset="utf-8">
|
|
91
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
92
|
+
<meta name="robots" content="noindex">
|
|
93
|
+
<link rel="icon" href="data:,">
|
|
94
|
+
<title>${escapeHtml(t.title)} – ${escapeHtml(view.appHost)}</title>
|
|
95
|
+
<style nonce="${escapeHtml(view.nonce)}">${STYLES}</style>
|
|
96
|
+
</head>
|
|
97
|
+
<body>
|
|
98
|
+
<main class="card">
|
|
99
|
+
${body}
|
|
100
|
+
</main>
|
|
101
|
+
${pageScript(view)}
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
function consentView(view, t, fill) {
|
|
107
|
+
const target = view.redirect.loopback
|
|
108
|
+
? `${escapeHtml(t.loopback)} (${escapeHtml(view.redirect.host)})`
|
|
109
|
+
: escapeHtml(view.redirect.host);
|
|
110
|
+
const who = view.user?.email || view.user?.name;
|
|
111
|
+
const hidden = Object.entries(view.oauthParams)
|
|
112
|
+
.map(([name, value]) => `<input type="hidden" name="${escapeHtml(name)}" value="${escapeHtml(value)}">`)
|
|
113
|
+
.join('\n');
|
|
114
|
+
const account = who
|
|
115
|
+
? `<p class="account">${escapeHtml(t.signedInAs).replace('{user}', () => `<strong>${escapeHtml(who)}</strong>`)}${view.authBasePath
|
|
116
|
+
? `<a href="" id="lt-mcp-switch" class="switch" data-sign-out="${escapeHtml(`${view.authBasePath}/sign-out`)}">${escapeHtml(t.switchAccount)}</a>`
|
|
117
|
+
: ''}</p>`
|
|
118
|
+
: '';
|
|
119
|
+
return `<h1>${escapeHtml(t.title)}</h1>
|
|
120
|
+
${account}
|
|
121
|
+
${view.notice === 'expired' ? `<p class="notice" role="alert">${escapeHtml(t.expired)}</p>` : ''}
|
|
122
|
+
<p>${fill(t.intro)}</p>
|
|
123
|
+
<p class="target">${escapeHtml(t.target)} <strong>${target}</strong></p>
|
|
124
|
+
<p class="warning">${escapeHtml(t.warning)}</p>
|
|
125
|
+
<noscript><p class="notice">${escapeHtml(t.approveNoscript)}</p></noscript>
|
|
126
|
+
<form method="post" action="authorize" class="actions">
|
|
127
|
+
${hidden}
|
|
128
|
+
<input type="hidden" name="${exports.MCP_OAUTH_CONSENT_TOKEN_FIELD}" value="${escapeHtml(view.csrfToken ?? '')}">
|
|
129
|
+
<button type="submit" name="${exports.MCP_OAUTH_CONSENT_DECISION_FIELD}" value="deny" class="secondary">${escapeHtml(t.deny)}</button>
|
|
130
|
+
<button type="submit" name="${exports.MCP_OAUTH_CONSENT_DECISION_FIELD}" value="approve" class="primary" id="lt-mcp-allow" disabled>${escapeHtml(t.allow)}</button>
|
|
131
|
+
</form>`;
|
|
132
|
+
}
|
|
133
|
+
function pageScript(view) {
|
|
134
|
+
const script = view.user ? exports.MCP_OAUTH_CONSENT_GUARD_SCRIPT : view.authBasePath ? exports.MCP_OAUTH_CONSENT_SCRIPT : '';
|
|
135
|
+
return script ? `<script nonce="${escapeHtml(view.nonce)}">${script}</script>` : '';
|
|
136
|
+
}
|
|
137
|
+
function signInView(view, t, fill) {
|
|
138
|
+
const other = view.loginUrl
|
|
139
|
+
? `<div class="other">
|
|
140
|
+
<a href="${escapeHtml(view.loginUrl.href)}"${view.loginUrl.returnsHere ? '' : ' target="_blank" rel="noopener noreferrer"'}>${escapeHtml(t.otherMethod)}</a>
|
|
141
|
+
${view.loginUrl.returnsHere ? '' : `<a href="" class="continue">${escapeHtml(t.continue)}</a>`}
|
|
142
|
+
</div>`
|
|
143
|
+
: '';
|
|
144
|
+
if (!view.authBasePath) {
|
|
145
|
+
return `<h1>${escapeHtml(t.signIn)}</h1>
|
|
146
|
+
<p>${escapeHtml(t.noSignIn)}</p>
|
|
147
|
+
${other}`;
|
|
148
|
+
}
|
|
149
|
+
return `<h1>${escapeHtml(t.signIn)}</h1>
|
|
150
|
+
<p>${fill(t.signInIntro)}</p>
|
|
151
|
+
<noscript><p class="notice">${escapeHtml(t.noscript)}</p></noscript>
|
|
152
|
+
<div id="lt-mcp-signin" data-auth-base="${escapeHtml(view.authBasePath)}" data-sign-in-failed="${escapeHtml(t.signInFailed)}" data-code-failed="${escapeHtml(t.codeFailed)}">
|
|
153
|
+
<form id="lt-mcp-password" method="post" hidden>
|
|
154
|
+
<label>${escapeHtml(t.email)}<input name="email" type="email" autocomplete="username" required></label>
|
|
155
|
+
<label>${escapeHtml(t.password)}<input name="password" type="password" autocomplete="current-password" required></label>
|
|
156
|
+
<button type="submit" class="primary">${escapeHtml(t.signIn)}</button>
|
|
157
|
+
</form>
|
|
158
|
+
<form id="lt-mcp-second-factor" method="post" hidden>
|
|
159
|
+
<p class="muted">${escapeHtml(t.codeHint)}</p>
|
|
160
|
+
<label>${escapeHtml(t.code)}<input name="code" autocomplete="one-time-code" inputmode="numeric" required></label>
|
|
161
|
+
<label class="inline"><input name="backup" type="checkbox"> ${escapeHtml(t.backupCode)}</label>
|
|
162
|
+
<button type="submit" class="primary">${escapeHtml(t.confirm)}</button>
|
|
163
|
+
</form>
|
|
164
|
+
<p id="lt-mcp-error" class="notice" role="alert" hidden></p>
|
|
165
|
+
</div>
|
|
166
|
+
${other}`;
|
|
167
|
+
}
|
|
168
|
+
exports.MCP_OAUTH_CONSENT_GUARD_SCRIPT = `(function () {
|
|
169
|
+
var allow = document.getElementById('lt-mcp-allow');
|
|
170
|
+
if (allow) {
|
|
171
|
+
var since = 0;
|
|
172
|
+
var armed = false;
|
|
173
|
+
var reset = function () { since = 0; armed = false; };
|
|
174
|
+
var watch = function () {
|
|
175
|
+
if (document.visibilityState === 'visible' && document.hasFocus()) { if (!since) { since = Date.now(); } } else { reset(); }
|
|
176
|
+
};
|
|
177
|
+
var intent = function (event) {
|
|
178
|
+
watch();
|
|
179
|
+
var stationaryClick = event.type === 'pointerdown' && event.pointerType === 'mouse';
|
|
180
|
+
if (since && !stationaryClick && Date.now() - since >= 600) { armed = true; }
|
|
181
|
+
};
|
|
182
|
+
window.addEventListener('blur', reset);
|
|
183
|
+
window.addEventListener('focus', watch);
|
|
184
|
+
document.addEventListener('visibilitychange', function () { if (document.visibilityState === 'visible') { watch(); } else { reset(); } });
|
|
185
|
+
document.addEventListener('pointermove', intent);
|
|
186
|
+
document.addEventListener('pointerdown', intent);
|
|
187
|
+
document.addEventListener('keydown', intent);
|
|
188
|
+
allow.addEventListener('click', function (event) { if (!armed) { event.preventDefault(); } });
|
|
189
|
+
allow.disabled = false;
|
|
190
|
+
watch();
|
|
191
|
+
}
|
|
192
|
+
var other = document.getElementById('lt-mcp-switch');
|
|
193
|
+
if (other) {
|
|
194
|
+
other.addEventListener('click', function (event) {
|
|
195
|
+
event.preventDefault();
|
|
196
|
+
var done = function () { location.reload(); };
|
|
197
|
+
fetch(other.getAttribute('data-sign-out'), { body: '{}', credentials: 'same-origin', headers: { 'content-type': 'application/json' }, method: 'POST' }).then(done, done);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
})();`;
|
|
201
|
+
exports.MCP_OAUTH_CONSENT_SCRIPT = `(function () {
|
|
202
|
+
var root = document.getElementById('lt-mcp-signin');
|
|
203
|
+
if (!root) { return; }
|
|
204
|
+
var base = root.getAttribute('data-auth-base');
|
|
205
|
+
var passwordForm = document.getElementById('lt-mcp-password');
|
|
206
|
+
var factorForm = document.getElementById('lt-mcp-second-factor');
|
|
207
|
+
var error = document.getElementById('lt-mcp-error');
|
|
208
|
+
// Both forms are rendered hidden and with method="post": without this script nothing can be
|
|
209
|
+
// submitted, and nothing would ever put the password into a URL.
|
|
210
|
+
passwordForm.hidden = false;
|
|
211
|
+
function fail(message) { error.textContent = message; error.hidden = false; }
|
|
212
|
+
function post(path, body) {
|
|
213
|
+
return fetch(base + path, {
|
|
214
|
+
body: JSON.stringify(body),
|
|
215
|
+
credentials: 'same-origin',
|
|
216
|
+
headers: { 'content-type': 'application/json' },
|
|
217
|
+
method: 'POST'
|
|
218
|
+
}).then(function (response) {
|
|
219
|
+
return response.json().then(function (json) { return json; }, function () { return {}; }).then(function (json) {
|
|
220
|
+
return { body: json || {}, ok: response.ok };
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
passwordForm.addEventListener('submit', function (event) {
|
|
225
|
+
event.preventDefault();
|
|
226
|
+
error.hidden = true;
|
|
227
|
+
post('/sign-in/email', { email: passwordForm.elements.email.value, password: passwordForm.elements.password.value })
|
|
228
|
+
.then(function (result) {
|
|
229
|
+
if (result.body.requiresTwoFactor || result.body.twoFactorRedirect) {
|
|
230
|
+
passwordForm.hidden = true;
|
|
231
|
+
factorForm.hidden = false;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!result.ok) { fail(root.getAttribute('data-sign-in-failed')); return; }
|
|
235
|
+
location.reload();
|
|
236
|
+
}, function () { fail(root.getAttribute('data-sign-in-failed')); });
|
|
237
|
+
});
|
|
238
|
+
factorForm.addEventListener('submit', function (event) {
|
|
239
|
+
event.preventDefault();
|
|
240
|
+
error.hidden = true;
|
|
241
|
+
var path = factorForm.elements.backup.checked ? '/two-factor/verify-backup-code' : '/two-factor/verify-totp';
|
|
242
|
+
post(path, { code: factorForm.elements.code.value })
|
|
243
|
+
.then(function (result) {
|
|
244
|
+
if (!result.ok) { fail(root.getAttribute('data-code-failed')); return; }
|
|
245
|
+
location.reload();
|
|
246
|
+
}, function () { fail(root.getAttribute('data-code-failed')); });
|
|
247
|
+
});
|
|
248
|
+
})();`;
|
|
249
|
+
function escapeHtml(value) {
|
|
250
|
+
return String(value ?? '')
|
|
251
|
+
.replace(/&/g, '&')
|
|
252
|
+
.replace(/</g, '<')
|
|
253
|
+
.replace(/>/g, '>')
|
|
254
|
+
.replace(/"/g, '"')
|
|
255
|
+
.replace(/'/g, ''');
|
|
256
|
+
}
|
|
257
|
+
const STYLES = `
|
|
258
|
+
:root{color-scheme:light dark;--bg:#f4f5f7;--card:#fff;--text:#1d2129;--muted:#5b6270;--border:#d9dde3;--primary:#1f6feb;--primary-text:#fff;--notice:#b42318}
|
|
259
|
+
@media (prefers-color-scheme:dark){:root{--bg:#0f1115;--card:#181b21;--text:#e6e8eb;--muted:#9aa1ad;--border:#2c313a;--primary:#4c8dff;--notice:#ff8a80}}
|
|
260
|
+
*{box-sizing:border-box}
|
|
261
|
+
body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:16px;background:var(--bg);color:var(--text);font:15px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}
|
|
262
|
+
.card{width:100%;max-width:440px;background:var(--card);border:1px solid var(--border);border-radius:12px;padding:28px}
|
|
263
|
+
h1{font-size:20px;margin:0 0 16px}
|
|
264
|
+
p{margin:0 0 12px}
|
|
265
|
+
.muted{color:var(--muted)}
|
|
266
|
+
.account{padding:8px 10px;border:1px solid var(--border);border-radius:8px;font-size:14px}
|
|
267
|
+
.account .switch{display:block;margin-top:4px}
|
|
268
|
+
button:disabled{opacity:.6;cursor:default}
|
|
269
|
+
.target strong{overflow-wrap:anywhere}
|
|
270
|
+
.warning{color:var(--muted);font-size:13px;border-left:3px solid var(--border);padding-left:10px}
|
|
271
|
+
.notice{color:var(--notice)}
|
|
272
|
+
label{display:block;margin:0 0 12px;font-size:13px;color:var(--muted)}
|
|
273
|
+
label.inline{display:flex;gap:8px;align-items:center}
|
|
274
|
+
input:not([type=checkbox]){display:block;width:100%;margin-top:4px;padding:9px 10px;border:1px solid var(--border);border-radius:8px;background:transparent;color:var(--text);font:inherit}
|
|
275
|
+
.actions{display:flex;gap:10px;justify-content:flex-end;margin-top:20px}
|
|
276
|
+
button{padding:9px 16px;border-radius:8px;border:1px solid var(--border);background:transparent;color:var(--text);font:inherit;cursor:pointer}
|
|
277
|
+
button.primary{background:var(--primary);border-color:var(--primary);color:var(--primary-text)}
|
|
278
|
+
form#lt-mcp-password button,form#lt-mcp-second-factor button{width:100%}
|
|
279
|
+
.other{margin-top:20px;padding-top:16px;border-top:1px solid var(--border);display:flex;flex-direction:column;gap:8px;font-size:13px}
|
|
280
|
+
a{color:var(--primary)}
|
|
281
|
+
`;
|
|
282
|
+
//# sourceMappingURL=ai-mcp-oauth-consent.helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-mcp-oauth-consent.helper.js","sourceRoot":"","sources":["../../../../../src/core/modules/ai/helpers/ai-mcp-oauth-consent.helper.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import { CoreAiMcpOAuthService } from '../services/core-ai-mcp-oauth.service';
|
|
2
|
+
import { IMountedMcpOAuth } from './ai-mcp-oauth.registry';
|
|
3
|
+
export interface IAiMcpOAuthMountTarget {
|
|
2
4
|
get: (token: any) => any;
|
|
5
|
+
getHttpAdapter?: () => {
|
|
6
|
+
getInstance?: () => unknown;
|
|
7
|
+
} | undefined;
|
|
3
8
|
use: (...args: any[]) => any;
|
|
4
|
-
}
|
|
9
|
+
}
|
|
10
|
+
export interface IAiMcpOAuthRouterOptions {
|
|
11
|
+
authBasePath?: string;
|
|
12
|
+
baseUrl?: unknown;
|
|
13
|
+
baseUrlSource?: string;
|
|
14
|
+
mcpPath: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function mountAiMcpOAuth(app: IAiMcpOAuthMountTarget, options?: {
|
|
5
17
|
baseUrl?: string;
|
|
6
18
|
mcpPath?: string;
|
|
7
19
|
}): Promise<void>;
|
|
20
|
+
export declare function mountAiMcpOAuthRouter(target: object, use: (router: unknown) => void, oauthService: CoreAiMcpOAuthService, options: IAiMcpOAuthRouterOptions): Promise<IMountedMcpOAuth>;
|
|
@@ -1,25 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mountAiMcpOAuth = mountAiMcpOAuth;
|
|
4
|
+
exports.mountAiMcpOAuthRouter = mountAiMcpOAuthRouter;
|
|
4
5
|
const common_1 = require("@nestjs/common");
|
|
5
6
|
const cookies_helper_1 = require("../../../common/helpers/cookies.helper");
|
|
6
7
|
const config_service_1 = require("../../../common/services/config.service");
|
|
7
8
|
const core_ai_mcp_oauth_service_1 = require("../services/core-ai-mcp-oauth.service");
|
|
8
9
|
const ai_mcp_oauth_registry_1 = require("./ai-mcp-oauth.registry");
|
|
10
|
+
const logger = new common_1.Logger('mountAiMcpOAuth');
|
|
11
|
+
const mountedTargets = new WeakMap();
|
|
9
12
|
async function mountAiMcpOAuth(app, options = {}) {
|
|
10
|
-
const
|
|
13
|
+
const target = mountTargetOf(app);
|
|
14
|
+
const existing = mountedTargets.get(target);
|
|
15
|
+
if (existing) {
|
|
16
|
+
reportRedundantMount(await existing, options);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
await mountAiMcpOAuthRouter(target, (router) => app.use(router), app.get(core_ai_mcp_oauth_service_1.CoreAiMcpOAuthService), {
|
|
20
|
+
baseUrl: options.baseUrl,
|
|
21
|
+
baseUrlSource: 'options.baseUrl',
|
|
22
|
+
mcpPath: options.mcpPath ?? '/ai/mcp',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function mountAiMcpOAuthRouter(target, use, oauthService, options) {
|
|
26
|
+
const existing = mountedTargets.get(target);
|
|
27
|
+
if (existing) {
|
|
28
|
+
return existing;
|
|
29
|
+
}
|
|
30
|
+
const pending = mountRouter(use, oauthService, options);
|
|
31
|
+
mountedTargets.set(target, pending);
|
|
32
|
+
try {
|
|
33
|
+
return await pending;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
mountedTargets.delete(target);
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function mountRouter(use, oauthService, options) {
|
|
41
|
+
const { baseUrl, source } = resolveMcpOAuthBaseUrl(options.baseUrl, options.baseUrlSource ?? 'options.baseUrl');
|
|
11
42
|
const issuerUrl = parseAbsoluteUrl(baseUrl, source);
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const mcpPath = options.mcpPath ?? '/ai/mcp';
|
|
15
|
-
let router;
|
|
43
|
+
const resourceServerUrl = new URL(`${baseUrl.replace(/\/$/, '')}${options.mcpPath}`);
|
|
44
|
+
const { getOAuthProtectedResourceMetadataUrl, mcpAuthRouter } = await Promise.resolve().then(() => require('@modelcontextprotocol/sdk/server/auth/router.js'));
|
|
16
45
|
const provider = oauthService.buildOAuthProvider();
|
|
46
|
+
let router;
|
|
17
47
|
try {
|
|
18
|
-
router = mcpAuthRouter({
|
|
19
|
-
issuerUrl,
|
|
20
|
-
provider,
|
|
21
|
-
resourceServerUrl: new URL(`${baseUrl.replace(/\/$/, '')}${mcpPath}`),
|
|
22
|
-
});
|
|
48
|
+
router = mcpAuthRouter({ issuerUrl, provider, resourceServerUrl });
|
|
23
49
|
}
|
|
24
50
|
catch (error) {
|
|
25
51
|
const reason = error.message;
|
|
@@ -27,18 +53,46 @@ async function mountAiMcpOAuth(app, options = {}) {
|
|
|
27
53
|
cause: error,
|
|
28
54
|
});
|
|
29
55
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
56
|
+
use(router);
|
|
57
|
+
const mounted = {
|
|
58
|
+
authBasePath: options.authBasePath,
|
|
59
|
+
baseUrl: baseUrl.replace(/\/$/, ''),
|
|
60
|
+
issuerUrl: issuerUrl.href,
|
|
61
|
+
mcpPath: options.mcpPath,
|
|
33
62
|
registration: !!provider?.clientsStore?.registerClient,
|
|
63
|
+
resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(resourceServerUrl),
|
|
64
|
+
resourceUrl: resourceServerUrl.href,
|
|
34
65
|
revocation: !!provider?.revokeToken,
|
|
35
|
-
}
|
|
36
|
-
|
|
66
|
+
};
|
|
67
|
+
(0, ai_mcp_oauth_registry_1.setMountedMcpOAuth)(mounted);
|
|
68
|
+
logger.log(`MCP OAuth issuer: ${issuerUrl.href} (from ${source}), resource: ${resourceServerUrl.href}`);
|
|
69
|
+
return mounted;
|
|
70
|
+
}
|
|
71
|
+
function mountTargetOf(app) {
|
|
72
|
+
const instance = app.getHttpAdapter?.()?.getInstance?.();
|
|
73
|
+
return (typeof instance === 'object' && instance !== null) || typeof instance === 'function'
|
|
74
|
+
? instance
|
|
75
|
+
: app;
|
|
76
|
+
}
|
|
77
|
+
function reportRedundantMount(mounted, options) {
|
|
78
|
+
const ignored = [];
|
|
79
|
+
const baseUrl = options.baseUrl?.trim().replace(/\/$/, '');
|
|
80
|
+
if (baseUrl && baseUrl !== mounted.baseUrl) {
|
|
81
|
+
ignored.push(`baseUrl "${options.baseUrl}" (mounted: "${mounted.baseUrl}"; set ai.mcp.oauthBaseUrl instead)`);
|
|
82
|
+
}
|
|
83
|
+
if (options.mcpPath && options.mcpPath !== mounted.mcpPath) {
|
|
84
|
+
ignored.push(`mcpPath "${options.mcpPath}" (mounted: "${mounted.mcpPath}", derived from the MCP controller's route)`);
|
|
85
|
+
}
|
|
86
|
+
if (ignored.length) {
|
|
87
|
+
logger.warn(`The MCP OAuth router is already mounted; this call changed nothing and ignored ${ignored.join(', ')}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
logger.log('The MCP OAuth router is already mounted (CoreModule mounts it when ai.mcp.oauth is enabled); this call can be removed');
|
|
37
91
|
}
|
|
38
|
-
function resolveMcpOAuthBaseUrl(explicit) {
|
|
39
|
-
const given = explicit
|
|
92
|
+
function resolveMcpOAuthBaseUrl(explicit, explicitSource) {
|
|
93
|
+
const given = typeof explicit === 'string' ? explicit.trim() : undefined;
|
|
40
94
|
if (given) {
|
|
41
|
-
return { baseUrl: given, source:
|
|
95
|
+
return { baseUrl: given, source: explicitSource };
|
|
42
96
|
}
|
|
43
97
|
const config = config_service_1.ConfigService.configFastButReadOnly;
|
|
44
98
|
const resolved = (0, cookies_helper_1.resolveServerUrls)({ baseUrl: config?.baseUrl, env: config?.env });
|
|
@@ -49,7 +103,7 @@ function resolveMcpOAuthBaseUrl(explicit) {
|
|
|
49
103
|
};
|
|
50
104
|
}
|
|
51
105
|
throw new Error(`mountAiMcpOAuth: no public server URL for the OAuth issuer (env: ${config?.env ?? 'unset'}). ` +
|
|
52
|
-
'Set `baseUrl` in the server config (NSC__BASE_URL) or
|
|
106
|
+
'Set `baseUrl` in the server config (NSC__BASE_URL) or `ai.mcp.oauthBaseUrl`. ' +
|
|
53
107
|
'A guessed localhost URL would send MCP clients to their own machine.');
|
|
54
108
|
}
|
|
55
109
|
function parseAbsoluteUrl(value, source) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-mcp-oauth.helper.js","sourceRoot":"","sources":["../../../../../src/core/modules/ai/helpers/ai-mcp-oauth.helper.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"ai-mcp-oauth.helper.js","sourceRoot":"","sources":["../../../../../src/core/modules/ai/helpers/ai-mcp-oauth.helper.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export interface IMountedMcpOAuth {
|
|
2
|
+
authBasePath?: string;
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
issuerUrl: string;
|
|
2
5
|
mcpPath: string;
|
|
3
6
|
registration: boolean;
|
|
7
|
+
resourceMetadataUrl: string;
|
|
8
|
+
resourceUrl: string;
|
|
4
9
|
revocation: boolean;
|
|
5
10
|
}
|
|
6
11
|
export declare function setMountedMcpOAuth(value: IMountedMcpOAuth | undefined): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-mcp-oauth.registry.js","sourceRoot":"","sources":["../../../../../src/core/modules/ai/helpers/ai-mcp-oauth.registry.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"ai-mcp-oauth.registry.js","sourceRoot":"","sources":["../../../../../src/core/modules/ai/helpers/ai-mcp-oauth.registry.ts"],"names":[],"mappings":";;AAkCA,gDAEC;AAGD,gDAEC;AAVD,IAAI,OAAqC,CAAC;AAG1C,SAAgB,kBAAkB,CAAC,KAAmC;IACpE,OAAO,GAAG,KAAK,CAAC;AAClB,CAAC;AAGD,SAAgB,kBAAkB;IAChC,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./core-ai-mcp-oauth.initializer"), exports);
|
|
17
18
|
__exportStar(require("./core-ai-mcp.controller"), exports);
|
|
18
19
|
__exportStar(require("./helpers/ai-mcp-oauth.helper"), exports);
|
|
19
20
|
__exportStar(require("./core-ai.controller"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/modules/ai/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,2DAAyC;AACzC,gEAA8C;AAC9C,uDAAqC;AACrC,mDAAiC;AACjC,qDAAmC;AACnC,6EAA2D;AAC3D,sEAAoD;AACpD,2EAAyD;AACzD,+EAA6D;AAC7D,oEAAkD;AAClD,6EAA2D;AAC3D,sEAAoD;AACpD,4EAA0D;AAC1D,qEAAmD;AACnD,uEAAqD;AACrD,uEAAqD;AACrD,qEAAmD;AACnD,qEAAmD;AACnD,gEAA8C;AAC9C,uDAAqC;AACrC,2DAAyC;AACzC,+EAA6D;AAC7D,iEAA+C;AAC/C,wEAAsD;AACtD,iEAA+C;AAC/C,sEAAoD;AACpD,gFAA8D;AAC9D,gEAA8C;AAC9C,8EAA4D;AAC5D,sEAAoD;AACpD,+EAA6D;AAC7D,oEAAkD;AAClD,sEAAoD;AACpD,qEAAmD;AACnD,iEAA+C;AAC/C,qEAAmD;AACnD,gEAA8C;AAC9C,8DAA4C;AAC5C,oEAAkD;AAClD,8DAA4C;AAC5C,qEAAmD;AACnD,kEAAgD;AAChD,oEAAkD;AAClD,+DAA6C;AAC7C,kEAAgD;AAChD,mEAAiD;AACjD,yEAAuD;AACvD,+DAA6C;AAC7C,oEAAkD;AAClD,mFAAiE;AACjE,iFAA+D;AAC/D,wEAAsD;AACtD,0EAAwD;AACxD,yEAAuD;AACvD,uEAAqD;AACrD,iEAA+C;AAC/C,0EAAwD;AACxD,4EAA0D;AAC1D,yEAAuD;AACvD,oEAAkD;AAClD,kEAAgD;AAChD,wEAAsD;AACtD,wEAAsD;AACtD,kEAAgD;AAChD,yEAAuD;AACvD,6DAA2C;AAC3C,uDAAqC;AACrC,2DAAyC;AACzC,iEAA+C;AAC/C,4DAA0C"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/core/modules/ai/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,kEAAgD;AAChD,2DAAyC;AACzC,gEAA8C;AAC9C,uDAAqC;AACrC,mDAAiC;AACjC,qDAAmC;AACnC,6EAA2D;AAC3D,sEAAoD;AACpD,2EAAyD;AACzD,+EAA6D;AAC7D,oEAAkD;AAClD,6EAA2D;AAC3D,sEAAoD;AACpD,4EAA0D;AAC1D,qEAAmD;AACnD,uEAAqD;AACrD,uEAAqD;AACrD,qEAAmD;AACnD,qEAAmD;AACnD,gEAA8C;AAC9C,uDAAqC;AACrC,2DAAyC;AACzC,+EAA6D;AAC7D,iEAA+C;AAC/C,wEAAsD;AACtD,iEAA+C;AAC/C,sEAAoD;AACpD,gFAA8D;AAC9D,gEAA8C;AAC9C,8EAA4D;AAC5D,sEAAoD;AACpD,+EAA6D;AAC7D,oEAAkD;AAClD,sEAAoD;AACpD,qEAAmD;AACnD,iEAA+C;AAC/C,qEAAmD;AACnD,gEAA8C;AAC9C,8DAA4C;AAC5C,oEAAkD;AAClD,8DAA4C;AAC5C,qEAAmD;AACnD,kEAAgD;AAChD,oEAAkD;AAClD,+DAA6C;AAC7C,kEAAgD;AAChD,mEAAiD;AACjD,yEAAuD;AACvD,+DAA6C;AAC7C,oEAAkD;AAClD,mFAAiE;AACjE,iFAA+D;AAC/D,wEAAsD;AACtD,0EAAwD;AACxD,yEAAuD;AACvD,uEAAqD;AACrD,iEAA+C;AAC/C,0EAAwD;AACxD,4EAA0D;AAC1D,yEAAuD;AACvD,oEAAkD;AAClD,kEAAgD;AAChD,wEAAsD;AACtD,wEAAsD;AACtD,kEAAgD;AAChD,yEAAuD;AACvD,6DAA2C;AAC3C,uDAAqC;AACrC,2DAAyC;AACzC,iEAA+C;AAC/C,4DAA0C"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type * as OAuthErrors from '@modelcontextprotocol/sdk/server/auth/errors.js';
|
|
1
2
|
import { Logger, OnModuleInit } from '@nestjs/common';
|
|
2
3
|
import { Connection } from 'mongoose';
|
|
4
|
+
import { IMcpOAuthConsentView } from '../helpers/ai-mcp-oauth-consent.helper';
|
|
3
5
|
export interface AiMcpOAuthClient {
|
|
4
6
|
client_id: string;
|
|
5
7
|
client_name?: string;
|
|
@@ -14,6 +16,12 @@ export interface AiMcpAccessTokenPayload {
|
|
|
14
16
|
sub: string;
|
|
15
17
|
type: 'mcp_access';
|
|
16
18
|
}
|
|
19
|
+
export interface IAiMcpOAuthConsentBinding {
|
|
20
|
+
clientId: string;
|
|
21
|
+
codeChallenge: string;
|
|
22
|
+
redirectUri: string;
|
|
23
|
+
userId: string;
|
|
24
|
+
}
|
|
17
25
|
export declare class CoreAiMcpOAuthService implements OnModuleInit {
|
|
18
26
|
protected readonly connection: Connection;
|
|
19
27
|
protected readonly logger: Logger;
|
|
@@ -25,6 +33,9 @@ export declare class CoreAiMcpOAuthService implements OnModuleInit {
|
|
|
25
33
|
constructor(connection: Connection);
|
|
26
34
|
onModuleInit(): void;
|
|
27
35
|
assertProductionSafe(): void;
|
|
36
|
+
protected allowsDevelopmentSecret(): boolean;
|
|
37
|
+
isOAuthEnabled(): boolean;
|
|
38
|
+
usesBuiltInConsent(): boolean;
|
|
28
39
|
protected oauthEnabled(): boolean;
|
|
29
40
|
protected resolveSecret(): string | undefined;
|
|
30
41
|
signAccessToken(userId: string, clientId: string, ttlSeconds?: number): string;
|
|
@@ -43,7 +54,7 @@ export declare class CoreAiMcpOAuthService implements OnModuleInit {
|
|
|
43
54
|
codeChallenge: string;
|
|
44
55
|
userId: string;
|
|
45
56
|
} | null>;
|
|
46
|
-
consumeAuthorizationCode(code: string): Promise<{
|
|
57
|
+
consumeAuthorizationCode(code: string, clientId?: string): Promise<{
|
|
47
58
|
clientId: string;
|
|
48
59
|
codeChallenge: string;
|
|
49
60
|
userId: string;
|
|
@@ -59,7 +70,24 @@ export declare class CoreAiMcpOAuthService implements OnModuleInit {
|
|
|
59
70
|
roles: string[];
|
|
60
71
|
} | null>;
|
|
61
72
|
buildOAuthProvider(accessTtlSeconds?: number): Record<string, any>;
|
|
62
|
-
protected
|
|
73
|
+
protected readonly consentTokenTtlSeconds = 600;
|
|
74
|
+
protected authorizeConsent(client: any, params: any, res: any): Promise<void>;
|
|
75
|
+
protected renderConsentPage(view: IMcpOAuthConsentView): string;
|
|
76
|
+
protected consentUser(req: any): Promise<{
|
|
77
|
+
email?: string;
|
|
78
|
+
id: string;
|
|
79
|
+
name?: string;
|
|
80
|
+
} | undefined>;
|
|
81
|
+
protected buildConsentView(req: any, client: any, params: any, user: {
|
|
82
|
+
email?: string;
|
|
83
|
+
id: string;
|
|
84
|
+
name?: string;
|
|
85
|
+
} | undefined, notice?: IMcpOAuthConsentView['notice']): IMcpOAuthConsentView;
|
|
86
|
+
protected consentAppHost(): string;
|
|
87
|
+
protected consentLoginUrl(req: any): IMcpOAuthConsentView['loginUrl'];
|
|
88
|
+
protected signConsentToken(binding: IAiMcpOAuthConsentBinding): string;
|
|
89
|
+
protected verifyConsentToken(token: unknown, binding: IAiMcpOAuthConsentBinding): 'expired' | 'invalid' | 'valid';
|
|
90
|
+
protected loadOAuthErrors(): Promise<typeof OAuthErrors>;
|
|
63
91
|
protected hmac(payloadStr: string): string;
|
|
64
92
|
protected safeEqual(a: string, b: string): boolean;
|
|
65
93
|
protected getSecret(): string;
|