using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using FindAndDrive.Domain.Helpers; using FindAndDrive.Domain.Interfaces.Providers; using FindAndDrive.Domain.Interfaces.Services; using FindAndDrive.Domain.Models.DTO; using FindAndDrive.Domain.Models.DTO.Response; using FindAndDrive.Domain.Models.Entities; using FindAndDrive.Domain.Models.Settings; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace FindAndDrive.Domain.Services; public class AuthenticationService : IAuthenticationService { private readonly IApiKeyService _apiKeyService; private readonly IRoleService _roleService; private readonly IDateTimeProvider _dateTimeProvider; private readonly SecuritySettings _securitySettings; private readonly ILogger _logger; public AuthenticationService(IApiKeyService apiKeyService, IRoleService roleService, IDateTimeProvider dateTimeProvider, IOptions securitySettings, ILogger logger) { _apiKeyService = apiKeyService; _roleService = roleService; _dateTimeProvider = dateTimeProvider; _securitySettings = securitySettings.Value; _logger = logger; } public async Task> Authenticate(string apiKeyId, string apiSecret) { var apiKey = await _apiKeyService.GetApiKey(apiKeyId); if (apiKey.KeySecretHash != ApiKeyHelper.HashString(apiSecret)) return new ServiceResponse(false, "Api key not found or invalid"); var role = await _roleService.GetRole(apiKey.RoleId); var token = GenerateToken(apiKey.ClientId, apiKey.ApiKeyId, role); return new ServiceResponse(true, "", token); } public async Task> RefreshToken(string token) { var handler = new JwtSecurityTokenHandler(); var decodedToken = handler.ReadToken(token) as JwtSecurityToken; var apiKeyId = decodedToken.Claims.First(w => w.Type == "apiKeyId").Value; if (apiKeyId is null || !Guid.TryParse(apiKeyId, out var keyGuid)) return new ServiceResponse(false, "Refresh failed key has been revoked or does not exist"); var exists = await _apiKeyService.IsNotRevoked(keyGuid); if (!exists) return new ServiceResponse(false, "Refresh failed key has been revoked or does not exist"); var response = GenerateToken(decodedToken.Claims); return new ServiceResponse(true, "Refresh succeeded", response); } private TokenResponse GenerateToken(Guid clientId, Guid keyId, Role role) { var expirationDate = _dateTimeProvider.GetUtcDateTime().AddMinutes(_securitySettings.LifetimeInMinutes); var claimsList = GetClaimsList(clientId.ToString(), keyId.ToString(), role.Permissions); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claimsList), Expires = expirationDate, SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_securitySettings.Key)), SecurityAlgorithms.HmacSha256Signature ), }; var tokenHandler = new JwtSecurityTokenHandler(); var securityToken = tokenHandler.CreateToken(tokenDescriptor); var token = tokenHandler.WriteToken(securityToken); return new TokenResponse { Token = token, TokenExpires = expirationDate }; } private TokenResponse GenerateToken(IEnumerable claimsList) { var expirationDate = _dateTimeProvider.GetUtcDateTime().AddMinutes(_securitySettings.LifetimeInMinutes); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claimsList), Expires = expirationDate, SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_securitySettings.Key)), SecurityAlgorithms.HmacSha256Signature ), }; var tokenHandler = new JwtSecurityTokenHandler(); var securityToken = tokenHandler.CreateToken(tokenDescriptor); var token = tokenHandler.WriteToken(securityToken); return new TokenResponse { Token = token, TokenExpires = expirationDate }; } private static IEnumerable GetClaimsList(string clientId, string keyId, IEnumerable permissions) { var claims = new List { new ("ClientId", clientId), new ("apiKeyId", keyId), }; foreach (var permission in permissions) { claims.Add(new Claim("Permissions", permission.ToString())); } return claims; } }