feat(mcp): per-user auth for the embedded /mcp endpoint
The embedded MCP server acted as a single service account; now each /mcp session authenticates as the current user, so tools run under that user's CASL and edits attribute to them. - HTTP Basic (chosen path): Authorization: Basic email:password, validated server-side via AuthService; the session carries the issued user JWT (not the raw password). Password may contain ':' (split on first only). - Bearer fallback: Authorization: Bearer <access JWT>, verified as ACCESS and additionally checked for an active session + non-disabled user (matching JwtStrategy), so revoked/disabled users are rejected. - Service account stays as an optional fallback (no creds + env configured). - packages/mcp createMcpHttpHandler accepts a per-request config resolver (back-compat: static config / stdio unchanged); identity is bound to the mcp-session-id at init and re-validated from the caller's own credentials on every request (anti session-fixation: a guessed session id can't be reused without matching creds). - A full login (session + audit) happens only once at session init; later requests re-verify credentials via a new non-side-effecting AuthService.verifyUserCredentials (no session/audit spam). - Failed-login limiter (5/60s, keyed per-IP, per-IP+email, and per-email so IP rotation can't brute one account) since direct login bypasses the controller throttler. Only real credential failures count. - MCP_TOKEN shared guard moved off Authorization to an X-MCP-Token header (timing-safe compare); credsConfigured 503 gate replaced by a clear 401. - No secrets logged; all auth resolved before res.hijack() so failures return clean 401 JSON. .env.example marks the service account optional. Implements docs/backlog/mcp-per-user-auth.md (variant L). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,13 +3,16 @@ import { McpController } from './mcp.controller';
|
||||
import { McpService } from './mcp.service';
|
||||
import { DatabaseModule } from '@docmost/db/database.module';
|
||||
import { EnvironmentModule } from '../environment/environment.module';
|
||||
import { AuthModule } from '../../core/auth/auth.module';
|
||||
import { TokenModule } from '../../core/auth/token.module';
|
||||
|
||||
// Community MCP feature: the server itself serves the Model Context Protocol
|
||||
// over HTTP at /mcp. DatabaseModule (global) provides WorkspaceRepo and
|
||||
// EnvironmentModule (global) provides EnvironmentService; both are imported
|
||||
// explicitly for clarity.
|
||||
// EnvironmentModule (global) provides EnvironmentService. AuthModule supplies
|
||||
// AuthService (per-user HTTP-Basic login validation) and TokenModule supplies
|
||||
// TokenService (Bearer access-JWT verification for the token fallback).
|
||||
@Module({
|
||||
imports: [DatabaseModule, EnvironmentModule],
|
||||
imports: [DatabaseModule, EnvironmentModule, AuthModule, TokenModule],
|
||||
controllers: [McpController],
|
||||
providers: [McpService],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user