146 lines
5.9 KiB
C#
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 : "";
|
|
}
|
|
}
|
|
} |