Authorization¶
Author: Olivera Lucas¶
Se incorporo lógica para que los endpoints de nuestras aplicaciones puedas autorizar. Esto nos permite bloquear el acceso a los endpoint a usuarios que no tienen el nivel de privilegios adecuado.
Prerequsistos¶
- .NET 6
- Andreani.ARQ.WebHost >= 6.3.0
- ver: CustomPolicy
- ver: Administración de usuarios
Configuración¶
Para configurar nuestras políticas de autorización debemos:
- Definir las políticas.
- Registrar las políticas al flujo de autorización.
- Identificar los endpoints que requieren autorización.
Definición de politicas de autorización.¶
Las políticas serán clases que implementen la interface IAzureAdB2CPolicy
public interface IAzureAdB2CPolicy<T>
{
public Task<AuthorizeResponse> IsAuthorized(UserAzureAdB2C<T> user);
}
NOTA: El generic
T
corresponde a la estructura que almacenamos en Azure AD B2C.
Ejemplo 1:¶
public class Policy : IAzureAdB2CPolicy<UserData>
{
public Task<AuthorizeResponse> IsAuthorized(UserAzureAdB2C<UserData> user)
{
if (user.Raw.Role != null)
return Task.FromResult(new AuthorizeResponse()
{
IsAuthorized = true
});
return Task.FromResult(new AuthorizeResponse()
{
IsAuthorized = false, // no tiene permiso
StatusCode = System.Net.HttpStatusCode.Forbidden,
Body = new AuthorizeBodyResponse
{
Message = "El usuario no tiene las claims de raw"
}
});
}
}
En este ejemplo UserData
corresponde a la estructura que se almacena en Azure AD B2C, esto permite la deserialización del token y la asignación del campo RAW.
public record struct UserData
{
public string Role { get; set; }
public List<string> Apps { get; set; }
}
Como podemos ver, la clase Policy
implementa IsAuthorized
que es el método que se ejecutará para detectar si se autoriza o no al usuario autenticado. Este método devuelve una clase AuthorizeResponse
la cual permite manipular la información que se mostrará en el response de la petición
Definición: AuthorizeResponse
public class AuthorizeResponse
{
public bool IsAuthorized { get; set; }
public HttpStatusCode StatusCode { get; set; }
public Dictionary<string, string> Headers { get; set; }
public AuthorizeBodyResponse Body { get; set; }
}
public class AuthorizeBodyResponse
{
public string Message { get; set; }
}
Campo | Descripción |
---|---|
IsAuthorized | boleano que identifica si el usuario tiene permiso o no. true tiene permisos, false no tiene permiso |
StatusCode | Status code que se devolverá en la petición http |
Headers | (opcional) Headers que se incorporarán en la respuesta |
Body | (opcional) Mensaje que se devolverá en el body de la respuesta |
Ejemplo 2:¶
Creamos una política que valide si el role es admin
public class PolicyIsAdmin : IAzureAdB2CPolicy<UserData>
{
public Task<AuthorizeResponse> IsAuthorized(UserAzureAdB2C<UserData> user)
{
if (user.Raw.Role == "Admin")
return Task.FromResult(new AuthorizeResponse()
{
IsAuthorized = true
});
return Task.FromResult(new AuthorizeResponse()
{
IsAuthorized = false,
StatusCode = System.Net.HttpStatusCode.Forbidden,
Body = new AuthorizeBodyResponse
{
Message = "El usuario no es administrador"
},
Headers = new Dictionary<string, string>
{
{ "Invalid", "Is not Admin" }
}
});
}
}
Registrar las politicas al flujo de autorización.¶
Debemos ir a nuestro archivo Program.cs
o Startup.cs
(en la vieja configuración) y configurar el middleware.
// Ejemplo con nueva defición de Program
// Configuramos el Services para las DI
builder.Services.AddScoped<Policy>();
builder.Services.AddScoped<PolicyIsAdmin>();
builder.Services.AddAuthorizationCustomPolicy<UserData>();
var app = builder.Build();
// Configuramos el middleware
app.ConfigureAndreani(
middleware: (opt) => opt.UseCustomPolicy<UserData>()
.AddDefaultCustomPolicy<Policy>()
.AddCustomPolicy<PolicyIsAdmin>("isAdmin")
.Build()
);
NOTA: ver Configuración Middleware WebHost
Podemos ver que en la configuración del middleware se están configurando dos políticas.
- Default Policy: Se ejecuta cuando un endpoint o controller tiene el atributo por defecto o el atributo con el valor
DefaultPolicy
- Custom Policy: Se ejecuta cuando un endpoint o controller tiene el atributo configurado con el valor que se le asigna en el middleware, en este caso
isAdmin
.
Configurar los endpoint.¶
Debemos configurar los endpoints para que se ejecute el análisis de las políticas cuando estos son invocados, para eso utilizaremos el atributo AuthorizeCustomPolicy
, El atributo puede autorizar a un endpoint especifico o a todo un controller con la misma autorización
Ejemplo 1: Controller¶
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[AuthorizeCustomPolicy()]
public class PersonController : ApiControllerBase
{
}
Esto nos asegura que todos los endpoints dentro del controller ejecutaran la política por defecto. En el Ejemplo 1: tenemos la política que se configuro por defecto. Por lo que todos los endpoints del controller tendrán esta validación.
Warning
Si configuramos el atributo de manera global en el controller todos los endpoint heredaran esta propiedad, por lo que si en algunos endpoints tenemos diferentes políticas se sumaran a la que configuramos globalmente.
Ejemplo 2: Endpoint¶
Configuración default policy
[HttpGet()]
[ProducesResponseType(typeof(List<PersonDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(List<Notify>), StatusCodes.Status400BadRequest)]
[Authorize(AuthenticationSchemes = WebHostConstants.AzureAdB2cAuthenticationScheme)] // config de autenticación con azure Ad b2c
[AuthorizeCustomPolicy()] // config autorización
public async Task<IActionResult> Get()
{
return Ok();
}
Configuración custom policy
[HttpGet()]
[ProducesResponseType(typeof(List<PersonDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(List<Notify>), StatusCodes.Status400BadRequest)]
[Authorize(AuthenticationSchemes = WebHostConstants.AzureAdB2cAuthenticationScheme)] // config de autenticación con azure Ad b2c
[AuthorizeCustomPolicy("isAdmin")] // config autorización
public async Task<IActionResult> GetAdmin()
{
return Ok();
}
Configuration multi custom policy
[HttpGet()]
[ProducesResponseType(typeof(List<PersonDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(List<Notify>), StatusCodes.Status400BadRequest)]
[Authorize(AuthenticationSchemes = WebHostConstants.AzureAdB2cAuthenticationScheme)] // config de autenticación con azure Ad b2c
[AuthorizeCustomPolicy("isAdmin", "isUser", "isValidSucursal")] // config autorización
public async Task<IActionResult> Get()
{
return Ok();
}
Warning
Las policy isUser
y isValidSucursal
son políticas que no hemos configurados en este ejemplo y su documentación es meramente ejemplar.
Importante
El atributo AuthorizeCustomPolicy puede funcionar con o sin la configuración de autenticación. En caso de no implementar Authorize
debemos asegurarnos de que llegue un token valido en el campo Authorization
del header del request.
Obtener el usuario¶
Para obtener el usuario autorizado, utilizaremos el método GetUserAzureAdB2c
que poseen los controllers. Este método nos devolverá un objeto hidratado con la información del token y la deserialización del campo raw
Ejemplo de Obtener usuario:¶
[HttpGet()]
[ProducesResponseType(typeof(List<PersonDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(List<Notify>), StatusCodes.Status400BadRequest)]
[Authorize(AuthenticationSchemes = WebHostConstants.AzureAdB2cAuthenticationScheme)] // config de autenticación con azure Ad b2c
[AuthorizeCustomPolicy()] // config autorización
public async Task<IActionResult> Get()
{
var user = GetUserAzureAdB2c<UserData>();
return Ok();
}
user
tendrá la información del token.
public class UserAzureAdB2C<T>
{
public string Email { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string Tfp { get; set; }
public T Raw { get; set; }
}