gehgassi_backend/gehGassi.Core/Services/RefreshTokenService.cs

241 lines
8.7 KiB
C#

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
{
/// <summary>
/// Service der Token erstellt
/// </summary>
public class TokenService : ITokenService
{
private readonly IConfiguration _configuration;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="configuration">Instanz einer IConfiguration</param>
public TokenService(IConfiguration configuration)
{
_configuration = configuration;
}
/// <summary>
/// Erstellen eines Tokens
/// </summary>
/// <param name="claims">Zu setzende Claims</param>
/// <returns>Token als string</returns>
public string GenerateAccessToken(IEnumerable<Claim> 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);
}
/// <summary>
/// Gibt das ClaimsPricipal basierend auf einem abgelaufenen Token zurück
/// </summary>
/// <param name="token">Abgelaufenes Token</param>
/// <returns>ClaimsPrincipal</returns>
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;
}
}
/// <summary>
/// Service der die Verwaltung von Refresh-Tokens realisiert
/// </summary>
public class RefreshTokenService : ServiceBase<RefreshToken>, IRefreshTokenService
{
private readonly IConfiguration _configuration;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="unitOfWork">Instanz eines IUnitOfWork</param>
/// <param name="configuration">Instanz eines IConfiguration</param>
public RefreshTokenService(IUnitOfWork unitOfWork, IConfiguration configuration) : base(unitOfWork)
{
_configuration = configuration;
}
/// <summary>
/// Erstellten eines Tokens
/// </summary>
/// <returns>RefreshToken</returns>
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;
}
/// <summary>
/// Gibt eine Liste aller RefreshTokens eines Benutzers zurück
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <returns>Liste Refreshtokens</returns>
public async Task<List<RefreshToken>> GetAllAsync(string userId)
{
return (await Repository.FindAsync(c => c.UserId == userId).ConfigureAwait(false)).ToList();
}
/// <summary>
/// Gibt ein Refreshtoken zurück
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <param name="token">Refreshtoken</param>
/// <returns>RefreshToken</returns>
public async Task<RefreshToken> GetAsync(string userId, string token)
{
return await Repository.SingleOrDefaultAsync(c => c.UserId == userId && c.Token == token);
}
/// <summary>
/// Gibt ein Refreshtoken anhand des Tokens selbst zurück
/// </summary>
/// <param name="token">Token</param>
/// <returns>RefreshToken</returns>
public async Task<RefreshToken> GetByTokenAsync(string token)
{
return await Repository.SingleOrDefaultAsync(c => c.Token == token);
}
/// <summary>
/// Gibt zurück ob ein Token gültig ist. Bedeutet, gibt es das Token und ist es noch nicht abgelaufen
/// </summary>
/// <param name="userId">Id des Benutzers</param>
/// <param name="token">Refreshtoken</param>
/// <returns>true wenn gültig, false sonst</returns>
public async Task<bool> IsValidAsync(string userId, string token)
{
var item = await GetAsync(userId, token);
if (item != null)
{
return item.Expires > DateTimeOffset.UtcNow;
}
return false;
}
/// <summary>
/// Löschen eines Refreshtokens
/// </summary>
/// <param name="id">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn gelöscht, false sonst</returns>
public async Task<bool> RemoveAsync(long id, string userName)
{
var item = await GetAsync(id);
if (item != null)
{
Repository.Remove(item);
await CommitAsync(userName);
return true;
}
return false;
}
/// <summary>
/// Löscht alle Refreshtokens eines Benutzers
/// </summary>
/// <param name="userId">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> RemoveAllAsync(string userId, string userName)
{
var items = await Repository.FindAsync(c => c.UserId == userId);
if (items.Any())
{
Repository.RemoveRange(items);
return true;
}
return false;
}
/// <summary>
/// Löscht alle abgelaufenen Refreshtokens eines Benutzers
/// </summary>
/// <param name="userId">Id des Tokens</param>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> 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;
}
/// <summary>
/// Löscht alle abgelaufenen Refreshtokens
/// </summary>
/// <param name="userName">Benutzername</param>
/// <returns>true wenn erfolgreich, false sonst</returns>
public async Task<bool> RemoveExpiredAsync(string userName)
{
var items = await Repository.FindAsync(c => c.Expires < DateTimeOffset.UtcNow);
if (items.Any())
{
Repository.RemoveRange(items);
return true;
}
return false;
}
}
}