Configuración de Políticas de Autorizaicón Básicas
En ASP.NET Core, el sistema de autorización permite definir reglas detalladas para controlar el acceso a los recursos. A continuación, se muestra cómo registrar una política basada en notificaciones (claims) en el contenedor de dependencias.
// Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireITDepartment", policy =>
policy.RequireClaim("Department", "IT"));
});
Aplicación de Políticas en Controladores
Una vez definida la política, se puede aplicar a controladores o acciones específicas utilizando el atributo [Authorize].
[ApiController]
[Route("api/[controller]")]
public class SecureDataController : ControllerBase
{
[Authorize(Policy = "RequireITDepartment")]
[HttpGet("confidential")]
public IActionResult GetConfidentialData()
{
return Ok(new { Message = "Datos sensibles obtenidos con éxito.", Timestamp = DateTime.UtcNow });
}
}
Manejador de Autenticación Personalizado
Para validar credenciales no estándar, como una clave de API en los encabezados, podemos implementar IAuthenticationHandler.
public class ApiKeyAuthenticationHandler : IAuthenticationHandler
{
private AuthenticationScheme _scheme;
private HttpContext _httpContext;
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
_scheme = scheme;
_httpContext = context;
return Task.CompletedTask;
}
public Task<AuthenticateResult> AuthenticateAsync()
{
var apiKey = _httpContext.Request.Headers["X-Api-Key"].FirstOrDefault();
if (string.IsNullOrEmpty(apiKey) || apiKey != "super-secret-key-123")
{
return Task.FromResult(AuthenticateResult.Fail("Clave de API inválida o ausente."));
}
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "system_admin"),
new Claim("Department", "IT"),
new Claim("ClearanceLevel", "High")
};
var identity = new ClaimsIdentity(claims, _scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, _scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
public Task ChallengeAsync(AuthenticationProperties properties)
{
_httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
}
public Task ForbidAsync(AuthenticationProperties properties)
{
_httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
return Task.CompletedTask;
}
}
Si el manejador genera un valor de Department diferente al exigido por la política (por ejemplo, "HR" en lugar de "IT"), el acceso será denegado con un código 403.
Conceptos Clave: Requisitos de Autorización (Requirements)
El núcleo de la autorizaicón basada en políticas son los IAuthorizationRequirement. Cada requisito representa una condición que debe cumplirse. El framework incluye varias implementaciones predeterminadas:
- AssertionRequirement: Evalúa una condición mediante un delegado o expresión booleana.
- DenyAnonymousAuthorizationRequirement: Bloquea el acceso a usuarios no autenticados (configurado por defecto).
- ClaimsAuthorizationRequirement: Verifica la presencia y el valor de un claim específico.
- RolesAuthorizationRequirement: Comprueba si el usuario pertenece a uno o varios roles mediante
IsInRole. - NameAuthorizationRequirement: Valida que el nombre del usuario coincida con el esperado.
- OperationAuthorizationRequirement: Diseñado para autorizar operaciones específicas sobre recursos.
Cuando estas implementaciones no son suficientes, es posible crear requisitos y manejadores personalizados.
Creación de un Requirement y Handler Personalizado
Podemos definir un reqiusito que implemente tanto la interfaz de marcador como el manejador de lógica.
// Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("HighClearancePolicy", policy =>
policy.Requirements.Add(new ClearanceLevelRequirement("High")));
});
public class ClearanceLevelRequirement : IAuthorizationRequirement
{
public string RequiredLevel { get; }
public ClearanceLevelRequirement(string level) => RequiredLevel = level;
}
public class ClearanceLevelHandler : AuthorizationHandler<ClearanceLevelRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ClearanceLevelRequirement requirement)
{
var userClearance = context.User.FindFirst("ClearanceLevel")?.Value;
if (userClearance != null && userClearance.Equals(requirement.RequiredLevel, StringComparison.OrdinalIgnoreCase))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
Nota: Para que el framework resuelva el manejador, debe registrarse en el contenedor de servicios: builder.Services.AddSingleton<IAuthorizationHandler, ClearanceLevelHandler>();
Aplicación de Múltiples Políticas
Es posible exigir que un endpoint cumpla con varias políticas simultáneamente. En este caso, todas las políticas deben evaluarse como exitosas.
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireITDepartment", policy =>
policy.RequireClaim("Department", "IT"));
options.AddPolicy("HighClearancePolicy", policy =>
policy.Requirements.Add(new ClearanceLevelRequirement("High")));
});
[ApiController]
[Route("api/[controller]")]
public class SecureDataController : ControllerBase
{
[Authorize(Policy = "RequireITDepartment")]
[Authorize(Policy = "HighClearancePolicy")]
[HttpGet("top-secret")]
public IActionResult GetTopSecretData()
{
return Ok(new { Data = "Información clasificada de nivel alto." });
}
}
Integración de Múltiples Esquemas de Autenticación
En aplicaciones híbridas, es común soportar múltiples esquemas, como cookies para navegadores web y tokens/claves para clientes API.
// Program.cs
builder.Services.AddAuthentication("Cookies")
.AddCookie(options =>
{
options.LoginPath = "/auth/login";
options.AccessDeniedPath = "/auth/access-denied";
});
builder.Services.AddAuthentication()
.AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>("ApiKeyScheme", options => { });
[ApiController]
[Route("api/[controller]")]
public class SecureDataController : ControllerBase
{
[Authorize(Policy = "RequireITDepartment", AuthenticationSchemes = "ApiKeyScheme,Cookies")]
[HttpGet("hybrid-endpoint")]
public IActionResult GetHybridData()
{
return Ok("Acceso concedido mediante Cookie o API Key.");
}
}
Vinculación Directa de Políticas a Esquemas Específicos
Para un control más granular, una política puede restringirse para que solo se evalúe si la autenticación proviene de un esquema particular.
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiOnlyPolicy", policy =>
{
policy.AddAuthenticationSchemes("ApiKeyScheme");
policy.Requirements.Add(new ClearanceLevelRequirement("High"));
});
options.AddPolicy("WebOnlyPolicy", policy =>
{
policy.AddAuthenticationSchemes("Cookies");
policy.RequireClaim("Department", "IT");
});
});