using ClaudeDo.Data.Repositories; using Microsoft.AspNetCore.Http; namespace ClaudeDo.Worker.Planning; public sealed class PlanningTokenAuthMiddleware { private readonly RequestDelegate _next; public PlanningTokenAuthMiddleware(RequestDelegate next) => _next = next; public async Task InvokeAsync(HttpContext ctx, TaskRepository tasks) { if (!ctx.Request.Path.StartsWithSegments("/mcp")) { await _next(ctx); return; } var auth = ctx.Request.Headers["Authorization"].ToString(); if (!auth.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { ctx.Response.StatusCode = 401; await ctx.Response.WriteAsync("Missing bearer token"); return; } var token = auth.Substring("Bearer ".Length).Trim(); var parent = await tasks.FindByPlanningTokenAsync(token, ctx.RequestAborted); if (parent is null || parent.PlanningPhase != ClaudeDo.Data.Models.PlanningPhase.Active) { ctx.Response.StatusCode = 401; await ctx.Response.WriteAsync("Invalid or expired planning token"); return; } ctx.Items["PlanningContext"] = new PlanningMcpContext { ParentTaskId = parent.Id }; await _next(ctx); } }