Files
ClaudeDo/src/ClaudeDo.Worker/External/ExternalMcpAuthMiddleware.cs
T

49 lines
1.6 KiB
C#

using ClaudeDo.Worker.Config;
using Microsoft.AspNetCore.Http;
namespace ClaudeDo.Worker.External;
public sealed class ExternalMcpAuthMiddleware
{
private const string HeaderName = "X-ClaudeDo-Key";
/// <summary>Header carrying an SSH session's token on the remote MCP listener.</summary>
public const string RemoteTokenHeader = "X-ClaudeDo-Remote-Token";
private readonly RequestDelegate _next;
public ExternalMcpAuthMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext ctx, WorkerConfig cfg, RemoteSessionTokenRegistry remoteTokens)
{
// Tunnelled traffic arrives as loopback too, so the remote listener never falls back to loopback trust.
if (cfg.RemoteMcpPort > 0 && ctx.Connection.LocalPort == cfg.RemoteMcpPort)
{
if (!remoteTokens.IsValid(ctx.Request.Headers[RemoteTokenHeader].ToString()))
{
ctx.Response.StatusCode = 401;
await ctx.Response.WriteAsync($"Missing or invalid {RemoteTokenHeader} header");
return;
}
await _next(ctx);
return;
}
if (string.IsNullOrEmpty(cfg.ExternalMcpApiKey))
{
await _next(ctx);
return;
}
var provided = ctx.Request.Headers[HeaderName].ToString();
if (!string.Equals(provided, cfg.ExternalMcpApiKey, StringComparison.Ordinal))
{
ctx.Response.StatusCode = 401;
await ctx.Response.WriteAsync($"Missing or invalid {HeaderName} header");
return;
}
await _next(ctx);
}
}