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;
}
}
}