Files
Core.Thalos.BuildingBlocks/Core.Thalos.BuildingBlocks/Services/TokenService.cs

142 lines
5.5 KiB
C#

// ***********************************************************************
// <copyright file="TokenService.cs">
// AgileWebs
// </copyright>
// ***********************************************************************
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.Thalos.BuildingBlocks
{
/// <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, IEnumerable<ModuleAdapter>) 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.Id, adapter?.User?.Id ?? string.Empty),
new Claim(Claims.Email, adapter?.User?.Email ?? string.Empty),
new Claim(Claims.Tenant, adapter?.Tenant?.Name ?? string.Empty),
new Claim(Claims.Tenant, adapter?.Tenant?.Id ?? 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.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, adapter.Modules);
}
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 : "";
}
}
}