feat(mcp): OAuth so claude.ai and ChatGPT can connect as connectors
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m49s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m49s
MindSift becomes the OAuth authorization server for its own MCP endpoint: discovery (RFC 9728/8414), dynamic client registration, authorization code with PKCE (S256), rotating refresh tokens with reuse detection. The consent page lives in the app at /oauth/authorize. A grant is an api_keys entry (kind 'oauth'), so scopes, origin badges, renaming and revoking work as for keys; tokens are valid for /mcp only. Unused connections expire after OAUTH_IDLE_DAYS (default 90). Requires BASE_URL; off in demo mode. Release 0.51.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
994f6dc554
commit
75aea4a7d4
31 files changed
+1581
-49
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
APIKey,
|
||||
OAuthAuthorizeInfo,
|
||||
AuthStatus,
|
||||
DashboardStats,
|
||||
Document,
|
||||
@@ -1322,6 +1323,32 @@ export const api = {
|
||||
await request(`/api/v1/keys/${id}`, { method: 'DELETE' });
|
||||
},
|
||||
|
||||
// --- OAUTH CONSENT (claude.ai, ChatGPT connect to MCP) ---
|
||||
/** query: the search string of /oauth/authorize as the client sent it */
|
||||
async getOAuthAuthorizeInfo(query: string): Promise<OAuthAuthorizeInfo> {
|
||||
return request<OAuthAuthorizeInfo>(`/api/v1/oauth/authorize${query}`);
|
||||
},
|
||||
|
||||
/** Allow (with the chosen rights) or refuse; returns where to send the browser */
|
||||
async answerOAuthAuthorize(query: string, approve: boolean, scopes: ApiKeyScope[]): Promise<{ redirect: string }> {
|
||||
const q = new URLSearchParams(query);
|
||||
return request<{ redirect: string }>('/api/v1/oauth/authorize', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
responseType: q.get('response_type') || '',
|
||||
clientId: q.get('client_id') || '',
|
||||
redirectUri: q.get('redirect_uri') || '',
|
||||
codeChallenge: q.get('code_challenge') || '',
|
||||
codeChallengeMethod: q.get('code_challenge_method') || '',
|
||||
state: q.get('state') || '',
|
||||
scope: q.get('scope') || '',
|
||||
resource: q.get('resource') || '',
|
||||
approve,
|
||||
scopes,
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
/** Simulator: sends like an external system would, with the given key or else the own session. */
|
||||
async testWebhookIngest(
|
||||
apiKey: string | null,
|
||||
|
||||
Reference in new issue
Block a user