// *********************************************************************** // // AgileWebs // // *********************************************************************** 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 { /// /// Service responsible for manage authenticacion. /// public class TokenService : ITokenService { private readonly JwtSecurityTokenHandler tokenHandler; private readonly IConfiguration configuration; private readonly JwtIssuerOptions jwtOptions; private readonly JsonSerializerOptions jsonOptions; /// /// Initializes a new instance of the class. /// public TokenService( IConfiguration configuration, IOptions jwtOptions ) { tokenHandler = new JwtSecurityTokenHandler(); this.configuration = configuration; this.jwtOptions = jwtOptions.Value; jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false }; } /// /// Refreshes the token. /// 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"); } /// /// Generates a JWT token for the provided user data. /// /// The user data. /// The user DTO with the generated token. 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 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; } /// /// Extracts the user email claim from the http context. /// 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 : ""; } } }