Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions src/AzLocal.Middleware/AuthStubMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;

namespace AzLocal.Middleware;

/// <summary>
/// Bypasses Azure AD authentication for local development.
/// Injects a fake authenticated identity on every request so [Authorize] checks pass
/// without needing a real Azure AD token or tenant.
/// </summary>
public class AuthStubMiddleware
{
private readonly RequestDelegate _next;

// Static so the same fake identity is reused on every request — no per-request allocation.
// authenticationType must be non-null/non-empty for IsAuthenticated to return true.
private static readonly ClaimsPrincipal FakeUser = new(new ClaimsIdentity(
[
new Claim("oid", "00000000-0000-0000-0000-000000000001"), // fake Azure object ID
new Claim("tid", "00000000-0000-0000-0000-000000000002"), // fake tenant ID
new Claim(ClaimTypes.Name, "azlocal-dev"),
], authenticationType: "AzLocalStub"));

public AuthStubMiddleware(RequestDelegate next) => _next = next;

public async Task InvokeAsync(HttpContext context) => await _next(context);
public async Task InvokeAsync(HttpContext context)
{
// Skip real token validation — stamp every request as authenticated locally.
context.User = FakeUser;
await _next(context);
}
}
22 changes: 21 additions & 1 deletion src/AzLocal.Middleware/ImdsMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
using Microsoft.AspNetCore.Http;
using System.Text.Json;

namespace AzLocal.Middleware;

public class ImdsMiddleware
{
private readonly RequestDelegate _next;

private static readonly string FakeTokenJson = JsonSerializer.Serialize(new
{
access_token = "fake-azlocal-token",
expires_on = "99999999999",
resource = "https://management.azure.com/",
token_type = "Bearer"
});

public ImdsMiddleware(RequestDelegate next) => _next = next;

public async Task InvokeAsync(HttpContext context) => await _next(context);
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/metadata/identity/oauth2/token"))
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = 200;
await context.Response.WriteAsync(FakeTokenJson);
return; // Do NOT call _next — response is already written
}
await _next(context);
}
}


10 changes: 7 additions & 3 deletions src/AzLocal.Middleware/RequestContextMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using Microsoft.AspNetCore.Http;
using AzLocal.Core.Routing;
using Microsoft.AspNetCore.Http;

namespace AzLocal.Middleware;

public class RequestContextMiddleware
{
private readonly RequestDelegate _next;

public RequestContextMiddleware(RequestDelegate next) => _next = next;

public async Task InvokeAsync(HttpContext context) => await _next(context);
public async Task InvokeAsync(HttpContext context)
{
context.Items["RequestContext"] = ServiceRouter.BuildContext(context.Request);
await _next(context);
}
}

19 changes: 17 additions & 2 deletions src/AzLocal.Middleware/RequestLoggingMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Diagnostics;

namespace AzLocal.Middleware;

public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;

public RequestLoggingMiddleware(RequestDelegate next) => _next = next;
public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}

public async Task InvokeAsync(HttpContext context) => await _next(context);
public async Task InvokeAsync(HttpContext context)
{
var sw = Stopwatch.StartNew();
_logger.LogDebug("→ {Method} {Path}", context.Request.Method, context.Request.Path);
await _next(context);
_logger.LogInformation("← {StatusCode} {Method} {Path} in {Elapsed}ms",
context.Response.StatusCode, context.Request.Method,
context.Request.Path, sw.ElapsedMilliseconds);
}
}

Loading