| 1 | using AgentForge.Plugin.Sdk; |
| 2 | using AgentForge.Services; |
| 3 | |
| 4 | namespace AgentForge.Middleware; |
| 5 | |
| 6 | public sealed class ForgeAuthMiddleware(RequestDelegate next) |
| 7 | { |
| 8 | public async Task InvokeAsync(HttpContext context, ForgeAuthService auth) |
| 9 | { |
| 10 | if (!auth.RequireAuth || BypassesApiToken(context.Request)) |
| 11 | { |
| 12 | await next(context); |
| 13 | return; |
| 14 | } |
| 15 | |
| 16 | var bearer = ForgePluginHttpAuth.GetBearerToken(context); |
| 17 | if (auth.ValidateBootstrapToken(bearer)) |
| 18 | { |
| 19 | await next(context); |
| 20 | return; |
| 21 | } |
| 22 | |
| 23 | var token = ForgePluginHttpAuth.ResolveApiToken(context, auth); |
| 24 | if (token is null) |
| 25 | { |
| 26 | context.Response.StatusCode = StatusCodes.Status401Unauthorized; |
| 27 | await context.Response.WriteAsJsonAsync(new { detail = "Bearer API token required." }); |
| 28 | return; |
| 29 | } |
| 30 | |
| 31 | context.Items[ForgePluginHttpAuth.ApiTokenItemKey] = token; |
| 32 | await next(context); |
| 33 | } |
| 34 | |
| 35 | private static bool BypassesApiToken(HttpRequest request) |
| 36 | { |
| 37 | var path = request.Path; |
| 38 | if (path.StartsWithSegments("/health", StringComparison.OrdinalIgnoreCase)) |
| 39 | return true; |
| 40 | if (path == "/") |
| 41 | return true; |
| 42 | if (path.StartsWithSegments("/view", StringComparison.OrdinalIgnoreCase)) |
| 43 | return true; |
| 44 | if (path.StartsWithSegments("/api/v1/ci/callback", StringComparison.OrdinalIgnoreCase)) |
| 45 | return true; |
| 46 | if (path.StartsWithSegments("/api/v1/git", StringComparison.OrdinalIgnoreCase)) |
| 47 | return true; |
| 48 | if (path.StartsWithSegments("/api/v1/admin", StringComparison.OrdinalIgnoreCase)) |
| 49 | return true; |
| 50 | if (path.StartsWithSegments("/api/v1/orgs", StringComparison.OrdinalIgnoreCase)) |
| 51 | return true; |
| 52 | if (path.StartsWithSegments("/api/v1/auth", StringComparison.OrdinalIgnoreCase)) |
| 53 | return true; |
| 54 | if (path.StartsWithSegments("/api/v1/capabilities", StringComparison.OrdinalIgnoreCase)) |
| 55 | return true; |
| 56 | return false; |
| 57 | } |
| 58 | } |
| 59 | |