Core.Thalos.BuildingBlocks/Core.Cerberos.Adapters/Services/TokenService.cs
Sergio Matias Urquin d5925a6476 Add project files.
2025-04-29 18:57:20 -06:00

146 lines
5.9 KiB
C#

// ***********************************************************************
// <copyright file="T5okenService.cs">
// Heath
// </copyright>
// ***********************************************************************
using Core.Cerberos.Adapters.Common.Constants;
using Core.Cerberos.Adapters.Contracts;
using Core.Cerberos.Adapters.Options;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.Data;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text.Json;
namespace Core.Cerberos.Adapters.Services
{
/// <summary>
/// Service responsible for manage authenticacion.
/// </summary>
public class TokenService : ITokenService
{
private readonly JwtSecurityTokenHandler tokenHandler;
private readonly IConfiguration configuration;
private readonly JwtIssuerOptions jwtOptions;
private readonly JsonSerializerOptions jsonOptions;
/// <summary>
/// Initializes a new instance of the <see cref="TokenService"/> class.
/// </summary>
public TokenService(
IConfiguration configuration,
IOptions<JwtIssuerOptions> jwtOptions
)
{
tokenHandler = new JwtSecurityTokenHandler();
this.configuration = configuration;
this.jwtOptions = jwtOptions.Value;
jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};
}
/// <summary>
/// Refreshes the token.
/// </summary>
public IActionResult RefreshAccessToken(HttpContext httpContext, TokenAdapter tokenAdapter)
{
var tokenString = httpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (tokenString is not null)
{
var oldToken = tokenHandler.ReadJwtToken(tokenString);
var tokenExpiration = oldToken.Claims.FirstOrDefault(c => c.Type == "exp")?.Value;
var difference = ValidateTokenExpiration(tokenExpiration ?? "");
if (difference.Value.TotalMinutes <= 5)
return new OkObjectResult(GenerateAccessToken(tokenAdapter));
}
return new BadRequestObjectResult("The token could not be refreshed");
}
/// <summary>
/// Generates a JWT token for the provided user data.
/// </summary>
/// <param name="user">The user data.</param>
/// <returns>The user DTO with the generated token.</returns>
public string GenerateAccessToken(TokenAdapter adapter)
{
var hours = 1;
var minutes = 0;
var expires = DateTime.UtcNow
.AddHours(hours)
.AddMinutes(minutes);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(Claims.Name, adapter?.User?.DisplayName ?? string.Empty),
new Claim(Claims.GUID, adapter?.User?.Guid ?? string.Empty),
new Claim(Claims.Email, adapter?.User?.Email ?? string.Empty),
new Claim(Claims.Role, adapter?.Role?.Name ?? string.Empty),
new Claim(Claims.RoleId, adapter?.Role?.Id ?? string.Empty),
new Claim(Claims.Applications, JsonSerializer.Serialize(adapter?.Role?.Applications), JsonClaimValueTypes.JsonArray),
new Claim(Claims.Modules, JsonSerializer.Serialize(adapter?.Modules?.Select(m => new { m.Name, m.Application, m.Route, m.Icon, m.Order }), jsonOptions), JsonClaimValueTypes.JsonArray),
new Claim(Claims.Companies, JsonSerializer.Serialize(adapter?.User?.Companies), JsonClaimValueTypes.JsonArray),
new Claim(Claims.Projects, JsonSerializer.Serialize(adapter?.User?.Projects), JsonClaimValueTypes.JsonArray),
new Claim(Claims.Permissions, JsonSerializer.Serialize(adapter?.Permissions?.Select(p => $"{p.Name}.{p.AccessLevel}".Replace(" ", "")).ToArray()), JsonClaimValueTypes.JsonArray),
}),
Expires = expires,
Issuer = jwtOptions.Issuer,
Audience = jwtOptions.Audience,
SigningCredentials = jwtOptions.SigningCredentials
};
var token = tokenHandler.CreateEncodedJwt(tokenDescriptor);
return token;
}
public ActionResult<TimeSpan> ValidateTokenExpiration(string tokenExpiration)
{
long unixTimestamp = long.Parse(tokenExpiration ?? "0");
DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds(unixTimestamp);
DateTime dateTimeExpiration = dateTimeOffset.UtcDateTime;
var difference = dateTimeExpiration - DateTime.UtcNow;
if (difference.TotalMinutes <= 0)
return new BadRequestObjectResult("Expired token");
else return difference;
}
/// <summary>
/// Extracts the user email claim from the http context.
/// </summary>
public string GetEmailClaim(HttpContext httpContext)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenString = httpContext.Request.Headers.Authorization.FirstOrDefault()?.Split(" ").Last();
var token = tokenHandler.ReadJwtToken(tokenString);
var email = !string.IsNullOrEmpty(token.Claims.FirstOrDefault(c => c.Type == "email")?.Value)
? token.Claims.FirstOrDefault(c => c.Type == "email")?.Value
: token.Claims.FirstOrDefault(c => c.Type == "preferred_username")?.Value;
return (email is not null) ? email : "";
}
}
}