User Context
El servicio UserContext
proporciona información sobre el usuario autenticado actual durante una solicitud HTTP. Este servicio es esencial para acceder a datos del usuario, como su ID, nombre, email, claims y headers personalizados, facilitando la implementación de lógica basada en el usuario autenticado. Además, esta libreria trabaja con los claims standar de Azure AD B2C, lo cual hace que sea más facil de entender.
namespace CodeDesignPlus.Net.Security.Abstractions;
/// <summary>/// Provides information about the authenticated user during the request./// </summary>public interface IUserContext{ /// <summary> /// Gets a value indicating whether the current user is an application. /// </summary> bool IsApplication { get; }
/// <summary> /// Gets the ID of the authenticated user. /// </summary> Guid IdUser { get; }
/// <summary> /// Gets a value indicating whether the user has been authenticated. /// </summary> bool IsAuthenticated { get; }
/// <summary> /// Gets the name of the current user. /// </summary> string Name { get; }
/// <summary> /// Gets the first name of the current user. /// </summary> string FirstName { get; }
/// <summary> /// Gets the last name of the current user. /// </summary> string LastName { get; }
/// <summary> /// Gets the city of the current user. /// </summary> string City { get; }
/// <summary> /// Gets the country of the current user. /// </summary> string Country { get; }
/// <summary> /// Gets the postal code of the current user. /// </summary> string PostalCode { get; }
/// <summary> /// Gets the street address of the current user. /// </summary> string StreetAddress { get; }
/// <summary> /// Gets the state of the current user. /// </summary> string State { get; }
/// <summary> /// Gets the job title of the current user. /// </summary> string JobTitle { get; }
/// <summary> /// Gets the email addresses of the current user. /// </summary> string[] Emails { get; }
/// <summary> /// Gets the tenant ID of the current user. /// </summary> Guid Tenant { get; }
/// <summary> /// Gets the claims principal containing the user's claims. /// </summary> ClaimsPrincipal User { get; }
/// <summary> /// Gets the value of a specified claim for the authenticated user. /// </summary> /// <typeparam name="TValue">The type of the claim value.</typeparam> /// <param name="claimType">The type of claim to retrieve.</param> /// <returns>The value of the specified claim.</returns> TValue GetClaim<TValue>(string claimType);
/// <summary> /// Gets the value of a specified header from the request. /// </summary> /// <typeparam name="TValue">The type of the header value.</typeparam> /// <param name="header">The name of the header to retrieve.</param> /// <returns>The value of the specified header.</returns> TValue GetHeader<TValue>(string header);}
¿Cómo Funciona?
El UserContext
obtiene la información del usuario a través del IHttpContextAccessor
, que proporciona acceso al contexto HTTP actual. Los datos del usuario se obtienen principalmente de dos fuentes:
- Claims: Los claims son pares clave-valor que se encuentran dentro del token JWT. Esta librería trabaja con los claims definidos en la clase
ClaimTypes
y corresponden a los claims estandar de Azure AD B2C. Estos claims pueden incluir información como elObjectIdentifier
(ID de usuario), nombre, email, rol, ciudad, pais, dirección y otros datos personales. - Headers: Los headers HTTP proporcionan información adicional sobre la solicitud. El
UserContext
puede acceder a headers personalizados, como el “X-Tenant”.
El UserContext
implementa la interfaz IUserContext
, la cual define las propiedades y métodos para acceder a la información del usuario de manera estandarizada.
Métodos
El UserContext
proporciona métodos y propiedades para acceder a la información del usuario autenticado. Algunos de los métodos más comunes son:
GetClaim
Type: public TValue GetClaim<TValue>(string claimType)
Obtiene el valor de un claim específico del usuario autenticado y se utiliza para extraer información del token JWT basada en el tipo de claim solicitado. Esta librería proporciona la clase ClaimTypes
con todos los claims estandar de Azure AD B2C.
GetHeader
Type: public TValue GetHeader<TValue>(string header)
Obtiene el valor de un header específico de la solicitud HTTP actual y se utiliza para extraer información adicional de la solicitud, como el ID del tenant.
Implementación
El UserContext
se implementa como un servicio singleton y depende de los siguientes componentes:
IHttpContextAccessor
: Proporciona acceso al contexto HTTP actual.IOptions<SecurityOptions>
: Permite acceder a las opciones de seguridad configuradas.
El servicio extrae información de los claims del token JWT y los headers HTTP, utilizando el IHttpContextAccessor
para acceder a los datos de la solicitud. La conversión de los valores se realiza utilizando Convert.ChangeType
para asegurar que el tipo de valor obtenido corresponda al tipo TValue
especificado. En el caso de los claims, se verifica si el valor es un GUID, y se realiza la conversion de forma adecuada.
using CodeDesignPlus.Net.Core.Abstractions;
namespace CodeDesignPlus.Net.Security.Services;
/// <summary>/// Provides user context information based on the current HTTP context./// </summary>/// <remarks>/// Initializes a new instance of the <see cref="UserContext"/> class./// </remarks>/// <param name="httpContextAccessor">The HTTP context accessor.</param>/// <param name="options">The security options.</param>/// <param name="eventContext">The event context.</param>public class UserContext(IHttpContextAccessor httpContextAccessor, IOptions<SecurityOptions> options, IEventContext eventContext) : IUserContext{
/// <summary> /// Gets a value indicating whether the current user is an application. /// </summary> public bool IsApplication => options.Value.Applications.Contains(this.GetClaim<string>(ClaimTypes.Audience));
/// <summary> /// Gets the user ID. /// </summary> public Guid IdUser => this.GetClaim<Guid>(ClaimTypes.ObjectIdentifier);
/// <summary> /// Gets a value indicating whether the current user is authenticated. /// </summary> public bool IsAuthenticated => this.User.Identity.IsAuthenticated;
/// <summary> /// Gets the user's name. /// </summary> public string Name => this.GetClaim<string>(ClaimTypes.Name);
/// <summary> /// Gets the user's email addresses. /// </summary> public string[] Emails => this.GetClaim<string[]>(ClaimTypes.Emails);
/// <summary> /// Gets the tenant ID from the request headers. /// </summary> public Guid Tenant => this.GetTenant();
/// <summary> /// Gets the current user's claims principal. /// </summary> public System.Security.Claims.ClaimsPrincipal User => httpContextAccessor.HttpContext.User;
/// <summary> /// Gets the user's first name. /// </summary> public string FirstName => this.GetClaim<string>(ClaimTypes.FirstName);
/// <summary> /// Gets the user's last name. /// </summary> public string LastName => this.GetClaim<string>(ClaimTypes.LastName);
/// <summary> /// Gets the user's city. /// </summary> public string City => this.GetClaim<string>(ClaimTypes.City);
/// <summary> /// Gets the user's country. /// </summary> public string Country => this.GetClaim<string>(ClaimTypes.Country);
/// <summary> /// Gets the user's postal code. /// </summary> public string PostalCode => this.GetClaim<string>(ClaimTypes.PostalCode);
/// <summary> /// Gets the user's street address. /// </summary> public string StreetAddress => this.GetClaim<string>(ClaimTypes.StreetAddress);
/// <summary> /// Gets the user's state. /// </summary> public string State => this.GetClaim<string>(ClaimTypes.State);
/// <summary> /// Gets the user's job title. /// </summary> public string JobTitle => this.GetClaim<string>(ClaimTypes.JobTitle);
/// <summary> /// Gets the value of a specified claim type. /// </summary> /// <typeparam name="TValue">The type of the claim value.</typeparam> /// <param name="claimType">The claim type.</param> /// <returns>The claim value.</returns> public TValue GetClaim<TValue>(string claimType) { var claimValue = this.User.FindFirst(claimType)?.Value;
if (typeof(TValue) == typeof(Guid) && Guid.TryParse(claimValue, out var guidValue)) { return (TValue)(object)guidValue; }
if (typeof(TValue) == typeof(string[])) { return (TValue)(object)new string[] { claimValue }; }
return (TValue)Convert.ChangeType(claimValue, typeof(TValue)); }
/// <summary> /// Gets the value of a specified header. /// </summary> /// <typeparam name="TValue">The type of the header value.</typeparam> /// <param name="header">The header name.</param> /// <returns>The header value.</returns> public TValue GetHeader<TValue>(string header) { if (httpContextAccessor.HttpContext == null) return default;
if (httpContextAccessor.HttpContext.Request.Headers.TryGetValue(header, out var values)) { var headerValue = values.FirstOrDefault();
if (typeof(TValue) == typeof(Guid) && Guid.TryParse(headerValue, out var guidValue)) { return (TValue)(object)guidValue; }
return (TValue)Convert.ChangeType(headerValue, typeof(TValue)); }
return default; }
/// <summary> /// Gets the tenant identifier. /// </summary> /// <returns>The tenant identifier.</returns> public Guid GetTenant() { var tenant = this.GetHeader<Guid>("X-Tenant");
if (tenant == Guid.Empty) tenant = eventContext.Tenant;
return tenant; }}
Ejemplo de Uso
using CodeDesignPlus.Net.Security.Abstractions;using Microsoft.AspNetCore.Mvc;
[ApiController][Route("api/[controller]")]public class UserController : ControllerBase{ private readonly IUserContext userContext;
public UserController(IUserContext userContext) { this.userContext = userContext; }
[HttpGet("info")] public IActionResult GetUserInfo() { return Ok(new { IsAuthenticated = userContext.IsAuthenticated, IdUser = userContext.IdUser, Name = userContext.Name, Email = userContext.Emails, Tenant = userContext.Tenant, FirstName = userContext.FirstName, LastName = userContext.LastName, City = userContext.City, Country = userContext.Country, PostalCode = userContext.PostalCode, StreetAddress = userContext.StreetAddress, State = userContext.State, JobTitle = userContext.JobTitle, IsApplication = userContext.IsApplication }); }}
En este ejemplo:
- Se inyecta
IUserContext
a través del constructor del controlador. - Se accede a las propiedades de
IUserContext
, comoIsAuthenticated
,IdUser
,Name
yEmails
, para obtener la información del usuario autenticado. - Se acceden a los properties personalizados para obtener informacion adicional como el
FirstName
o elJobTitle
. - Se verifica si el usuario es una aplicación a traves de la propiedad
IsApplication
Conclusiones
El servicio UserContext
facilita la gestión del contexto del usuario en una aplicación .NET. Al abstraer la complejidad de acceder a claims y headers, este servicio permite a los desarrolladores implementar lógica basada en el usuario autenticado de manera más sencilla y eficiente. Es recomendable utilizar este servicio en cualquier proyecto que requiera acceder a información del usuario autenticado. Además, al usar los ClaimTypes
que esta libreria provee, facilita el uso de claims de Azure AD B2C.