using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using gehGassi.Core.Interfaces; using gehGassi.Domain.Users; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; namespace gehGassi.Core.Services { /// /// Service der Token erstellt /// public class TokenService : ITokenService { private readonly IConfiguration _configuration; /// /// Erstellt eine Instanz /// /// Instanz einer IConfiguration public TokenService(IConfiguration configuration) { _configuration = configuration; } /// /// Erstellen eines Tokens /// /// Zu setzende Claims /// Token als string public string GenerateAccessToken(IEnumerable claims) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"])); var jwtToken = new JwtSecurityToken( issuer: _configuration["JWT:ValidIssuer"], audience: _configuration["JWT:ValidAudience"], claims: claims, notBefore: DateTime.UtcNow, expires: DateTime.UtcNow.AddMinutes(int.Parse(_configuration["JWT:TokenMinutes"])), signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256) ); return new JwtSecurityTokenHandler().WriteToken(jwtToken); } /// /// Gibt das ClaimsPricipal basierend auf einem abgelaufenen Token zurück /// /// Abgelaufenes Token /// ClaimsPrincipal public ClaimsPrincipal GetPrincipalFromExpiredToken(string token) { try { var tokenValidationParameters = new TokenValidationParameters { ValidateAudience = true, //you might want to validate the audience and issuer depending on your use case ValidAudience = _configuration["JWT:ValidAudience"], ValidateIssuer = true, ValidIssuer = _configuration["JWT:ValidIssuer"], ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"])), ValidateLifetime = false //here we are saying that we don't care about the token's expiration date }; var tokenHandler = new JwtSecurityTokenHandler(); var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var securityToken); if (securityToken != null) { var jwtSecurityToken = securityToken as JwtSecurityToken; if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase)) return null; return principal; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); } return null; } } /// /// Service der die Verwaltung von Refresh-Tokens realisiert /// public class RefreshTokenService : ServiceBase, IRefreshTokenService { private readonly IConfiguration _configuration; /// /// Erstellt eine Instanz /// /// Instanz eines IUnitOfWork /// Instanz eines IConfiguration public RefreshTokenService(IUnitOfWork unitOfWork, IConfiguration configuration) : base(unitOfWork) { _configuration = configuration; } /// /// Erstellten eines Tokens /// /// RefreshToken public RefreshToken Create() { var item = new RefreshToken(); var randomNumber = new byte[32]; using var rng = RandomNumberGenerator.Create(); rng.GetBytes(randomNumber); item.Token = Convert.ToBase64String(randomNumber); item.Expires = DateTimeOffset.UtcNow.AddMinutes(int.Parse(_configuration["JWT:RefreshTokenMinutes"])); return item; } /// /// Gibt eine Liste aller RefreshTokens eines Benutzers zurück /// /// Id des Benutzers /// Liste Refreshtokens public async Task> GetAllAsync(string userId) { return (await Repository.FindAsync(c => c.UserId == userId).ConfigureAwait(false)).ToList(); } /// /// Gibt ein Refreshtoken zurück /// /// Id des Benutzers /// Refreshtoken /// RefreshToken public async Task GetAsync(string userId, string token) { return await Repository.SingleOrDefaultAsync(c => c.UserId == userId && c.Token == token); } /// /// Gibt ein Refreshtoken anhand des Tokens selbst zurück /// /// Token /// RefreshToken public async Task GetByTokenAsync(string token) { return await Repository.SingleOrDefaultAsync(c => c.Token == token); } /// /// Gibt zurück ob ein Token gültig ist. Bedeutet, gibt es das Token und ist es noch nicht abgelaufen /// /// Id des Benutzers /// Refreshtoken /// true wenn gültig, false sonst public async Task IsValidAsync(string userId, string token) { var item = await GetAsync(userId, token); if (item != null) { return item.Expires > DateTimeOffset.UtcNow; } return false; } /// /// Löschen eines Refreshtokens /// /// Id des Tokens /// Benutzername /// true wenn gelöscht, false sonst public async Task RemoveAsync(long id, string userName) { var item = await GetAsync(id); if (item != null) { Repository.Remove(item); await CommitAsync(userName); return true; } return false; } /// /// Löscht alle Refreshtokens eines Benutzers /// /// Id des Tokens /// Benutzername /// true wenn erfolgreich, false sonst public async Task RemoveAllAsync(string userId, string userName) { var items = await Repository.FindAsync(c => c.UserId == userId); if (items.Any()) { Repository.RemoveRange(items); return true; } return false; } /// /// Löscht alle abgelaufenen Refreshtokens eines Benutzers /// /// Id des Tokens /// Benutzername /// true wenn erfolgreich, false sonst public async Task RemoveExpiredAsync(string userId, string userName) { var items = await Repository.FindAsync(c => c.UserId == userId && c.Expires < DateTimeOffset.UtcNow); if (items.Any()) { Repository.RemoveRange(items); return true; } return false; } /// /// Löscht alle abgelaufenen Refreshtokens /// /// Benutzername /// true wenn erfolgreich, false sonst public async Task RemoveExpiredAsync(string userName) { var items = await Repository.FindAsync(c => c.Expires < DateTimeOffset.UtcNow); if (items.Any()) { Repository.RemoveRange(items); return true; } return false; } } }