feat(worker): MCP bearer-token auth middleware
This commit is contained in:
6
src/ClaudeDo.Worker/Planning/PlanningMcpContext.cs
Normal file
6
src/ClaudeDo.Worker/Planning/PlanningMcpContext.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace ClaudeDo.Worker.Planning;
|
||||
|
||||
public sealed class PlanningMcpContext
|
||||
{
|
||||
public required string ParentTaskId { get; init; }
|
||||
}
|
||||
40
src/ClaudeDo.Worker/Planning/PlanningTokenAuth.cs
Normal file
40
src/ClaudeDo.Worker/Planning/PlanningTokenAuth.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
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.Status != ClaudeDo.Data.Models.TaskStatus.Planning)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user