using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using System.Security.Cryptography; using Manager.Interfaces.Models; using Microsoft.IdentityModel.Tokens; using Manager.Framework.Business; using Manager.Interfaces.DTO; using System.IdentityModel.Tokens.Jwt; using Manager.Services; namespace ManagerService.Service.Services { /// /// Tokens service /// public class TokensService { private readonly ILogger _logger; private readonly TokensSettings _tokenSettings; private readonly ProfileLogic _profileLogic; private InstanceDatabaseService _instanceService; private readonly SigningCredentials _signingCredentials; /// /// Constructor /// /// Logger /// Tokens settings /// Database context /// Profile logic /// Email client public TokensService(ILogger logger, IOptions tokenSettings, ProfileLogic profileLogic, InstanceDatabaseService instanceService) { _logger = logger; _tokenSettings = tokenSettings.Value; _profileLogic = profileLogic; _instanceService = instanceService; var key = Encoding.UTF8.GetBytes(_tokenSettings.Secret); _signingCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature); } /// /// Authenticate /// /// Email /// Password /// Token DTO in case of success public TokenDTO Authenticate(User user, string password) { try { var claims = new List(); var expiration = DateTime.UtcNow.AddMinutes(_tokenSettings.AccessTokenExpiration); _profileLogic.TestPassword(user.Email, user.Password, password); claims.Add(new Claim(ClaimTypes.Email, user.Email)); // TODO: add refresh token support var tokenHandler = new JwtSecurityTokenHandler(); var tokenDescriptor = new SecurityTokenDescriptor() { Subject = new ClaimsIdentity(claims), Expires = expiration, SigningCredentials = _signingCredentials }; var token = tokenHandler.CreateToken(tokenDescriptor); var instance = _instanceService.GetById(user.InstanceId); return new TokenDTO() { access_token = tokenHandler.WriteToken(token), expires_in = _tokenSettings.AccessTokenExpiration * 60, expiration = new DateTimeOffset(token.ValidTo), token_type = "Bearer", scope = Security.Scope, instanceId = user.InstanceId, pinCode = instance.PinCode }; } catch (UnauthorizedAccessException ex) { _logger?.LogError(ex, $"Authenticate error for user '{user.Email}': unauthorized access"); throw; } catch (Exception ex) { _logger?.LogError(ex, $"Authenticate error for user '{user.Email}': {ex.Message}"); throw; } } public object GenerateToken(string username) { var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenSettings.Secret)); // Put the secret in a file or something var claims = new Claim[] { new Claim(ClaimTypes.Name, username), new Claim(JwtRegisteredClaimNames.Email, "john.doe@blinkingcaret.com"), new Claim(ClaimTypes.Role, "Admin") }; var token = new JwtSecurityToken( issuer: "Manager App", audience: "Manager client", claims: claims, notBefore: DateTime.Now, expires: DateTime.Now.AddDays(28), signingCredentials: new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256) ); string jwtToken = new JwtSecurityTokenHandler().WriteToken(token); return jwtToken; } public static string GenerateSHA256String(string inputString) { SHA256 sha256 = SHA256Managed.Create(); byte[] bytes = Encoding.UTF8.GetBytes(inputString); byte[] hash = sha256.ComputeHash(bytes); return GetStringFromHash(hash); } public static string GetStringFromHash(byte[] hash) { StringBuilder result = new StringBuilder(); for (int i = 0; i < hash.Length; i++) { result.Append(hash[i].ToString("X2")); } return result.ToString(); } } }