241 lines
8.7 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|