59 lines
2.2 KiB
C#
59 lines
2.2 KiB
C#
|
|
using System.Text.Json;
|
||
|
|
using Microsoft.AspNetCore.Authorization;
|
||
|
|
using Microsoft.AspNetCore.Authorization.Policy;
|
||
|
|
|
||
|
|
namespace SIGCM2.Api.Authorization;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Custom IAuthorizationMiddlewareResultHandler that emits a structured ProblemDetails
|
||
|
|
/// response for 403 Forbidden outcomes (authenticated user, missing permission).
|
||
|
|
///
|
||
|
|
/// For 401 Unauthorized and successful outcomes, delegates to the default handler
|
||
|
|
/// so the existing JWT Bearer challenge flow is unaffected (REQ-B-07).
|
||
|
|
///
|
||
|
|
/// Registered as singleton in Program.cs — depends only on framework services.
|
||
|
|
/// </summary>
|
||
|
|
public sealed class ForbiddenProblemDetailsHandler : IAuthorizationMiddlewareResultHandler
|
||
|
|
{
|
||
|
|
private static readonly AuthorizationMiddlewareResultHandler DefaultHandler = new();
|
||
|
|
|
||
|
|
private static readonly JsonSerializerOptions SerializerOptions = new()
|
||
|
|
{
|
||
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||
|
|
};
|
||
|
|
|
||
|
|
public async Task HandleAsync(
|
||
|
|
RequestDelegate next,
|
||
|
|
HttpContext context,
|
||
|
|
AuthorizationPolicy policy,
|
||
|
|
PolicyAuthorizationResult authorizeResult)
|
||
|
|
{
|
||
|
|
// Only intercept 403s for authenticated users.
|
||
|
|
// If the user is not authenticated, the 401 challenge is handled by JwtBearer (REQ-B-07).
|
||
|
|
if (authorizeResult.Forbidden && context.User.Identity?.IsAuthenticated == true)
|
||
|
|
{
|
||
|
|
var requiredPermission = context.Items["RequiredPermission"] as string;
|
||
|
|
|
||
|
|
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||
|
|
context.Response.ContentType = "application/problem+json; charset=utf-8";
|
||
|
|
|
||
|
|
var problem = new
|
||
|
|
{
|
||
|
|
type = "https://sigcm2.local/errors/forbidden",
|
||
|
|
title = "Acceso denegado",
|
||
|
|
status = 403,
|
||
|
|
detail = "No tenés el permiso requerido para ejecutar esta acción.",
|
||
|
|
permisoRequerido = requiredPermission,
|
||
|
|
};
|
||
|
|
|
||
|
|
await context.Response.WriteAsync(
|
||
|
|
JsonSerializer.Serialize(problem, SerializerOptions));
|
||
|
|
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Delegate 401 challenges and successful outcomes to the default handler
|
||
|
|
await DefaultHandler.HandleAsync(next, context, policy, authorizeResult);
|
||
|
|
}
|
||
|
|
}
|