Files
gitmost/apps/server/src/integrations/mcp/mcp.module.ts
claude code agent 227 4a00dfc3b2 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>
2026-06-20 07:19:31 +03:00

20 lines
940 B
TypeScript

import { Module } from '@nestjs/common';
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. 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, AuthModule, TokenModule],
controllers: [McpController],
providers: [McpService],
})
export class McpModule {}