143 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| // ***********************************************************************
 | |
| // <copyright file="T5okenService.cs">
 | |
| //     AgileWebs
 | |
| // </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, 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.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.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 : "";
 | |
|         }
 | |
|     }
 | |
| } | 
