scoreboard-api/Server/Services/AuthenticationService.cs
2022-07-27 20:04:39 +03:00

111 lines
3.3 KiB
C#

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using DatabaseModels.InitialObjects;
using DatabaseModels.Requests;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Server.Data;
using Server.Models;
namespace Server.Services;
public class AuthenticationService
{
private readonly ServerDbContext _dbContext;
private readonly Jwt _jwt;
public AuthenticationService(ServerDbContext dbContext, Jwt jwt)
{
_dbContext = dbContext;
_jwt = jwt;
}
public async Task<(bool success, string content)> Register(AuthenticationRequest request)
{
if (await _dbContext.Users.AnyAsync(u => u.Username == request.Username))
{
return (false, "Username is taken.");
}
var user = new User {
Username = request.Username,
PasswordHash = request.Password,
Role = "Default"
};
ProvideSaltAndHash(user);
await _dbContext.Users.AddAsync(user);
await _dbContext.SaveChangesAsync();
return (true, "");
}
public async Task<(bool success, string content)> Login(AuthenticationRequest request)
{
var user = await _dbContext.Users.SingleOrDefaultAsync(u => u.Username == request.Username);
if (user == null)
{
return (false, "Invalid username.");
}
if (user.PasswordHash != ComputeHash(request.Password, user.PasswordSalt))
{
return (false, "Invalid password.");
}
return (true, GenerateJwtToken(AssembleClaimsIdentity(user)));
}
private ClaimsIdentity AssembleClaimsIdentity(User user)
{
var subject = new ClaimsIdentity(new[] {
new Claim("Id", user.Id.ToString()),
new Claim(ClaimTypes.Role, user.Role),
});
return subject;
}
private string GenerateJwtToken(ClaimsIdentity subject)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_jwt.Key);
var tokenDescriptor = new SecurityTokenDescriptor {
Subject = subject,
Audience = _jwt.Audience,
Issuer = _jwt.Issuer,
Expires = DateTime.UtcNow.AddMinutes(15),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
private void ProvideSaltAndHash(User user)
{
var salt = GenerateSalt();
user.PasswordSalt = Convert.ToBase64String(salt);
user.PasswordHash = ComputeHash(user.PasswordHash, user.PasswordSalt);
}
private byte[] GenerateSalt()
{
var rng = RandomNumberGenerator.Create();
var salt = new byte[24];
rng.GetBytes(salt);
return salt;
}
private string ComputeHash(string password, string saltString)
{
var salt = Convert.FromBase64String(saltString);
using var hashGenerator = new Rfc2898DeriveBytes(password, salt);
hashGenerator.IterationCount = 10101;
var bytes = hashGenerator.GetBytes(24);
return Convert.ToBase64String(bytes);
}
}