Files

141 lines
5.0 KiB
C#
Raw Normal View History

2024-04-27 22:36:16 +02:00
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SimpleToolkit.Extensions;
using WatchIt.Common.Model.Accounts;
using WatchIt.Database;
using WatchIt.Database.Model.Account;
using WatchIt.WebAPI.Services.Controllers.Common;
using WatchIt.WebAPI.Services.Utility.Tokens;
using WatchIt.WebAPI.Services.Utility.Tokens.Exceptions;
using WatchIt.WebAPI.Services.Utility.User;
using AccountProfilePicture = WatchIt.Common.Model.Accounts.AccountProfilePicture;
2024-04-27 22:36:16 +02:00
namespace WatchIt.WebAPI.Services.Controllers.Accounts;
public class AccountsControllerService(
ILogger<AccountsControllerService> logger,
DatabaseContext database,
ITokensService tokensService,
IUserService userService
) : IAccountsControllerService
{
#region PUBLIC METHODS
public async Task<RequestResult> Register(RegisterRequest data)
{
string leftSalt = StringExtensions.CreateRandom(20);
string rightSalt = StringExtensions.CreateRandom(20);
byte[] hash = ComputeHash(data.Password, leftSalt, rightSalt);
Account account = new Account
{
Username = data.Username,
Email = data.Email,
Password = hash,
LeftSalt = leftSalt,
RightSalt = rightSalt,
};
await database.Accounts.AddAsync(account);
await database.SaveChangesAsync();
logger.LogInformation($"New account with ID {account.Id} was created (username: {account.Username}; email: {account.Email})");
return RequestResult.Created($"accounts/{account.Id}", new RegisterResponse(account));
}
public async Task<RequestResult> Authenticate(AuthenticateRequest data)
{
Account? account = await database.Accounts.FirstOrDefaultAsync(x => string.Equals(x.Email, data.UsernameOrEmail) || string.Equals(x.Username, data.UsernameOrEmail));
if (account is null || !ComputeHash(data.Password, account.LeftSalt, account.RightSalt).SequenceEqual(account.Password))
{
return RequestResult.Unauthorized();
}
Task<string> refreshTokenTask = tokensService.CreateRefreshTokenAsync(account, true);
Task<string> accessTokenTask = tokensService.CreateAccessTokenAsync(account);
AuthenticateResponse response = new AuthenticateResponse
{
AccessToken = await accessTokenTask,
RefreshToken = await refreshTokenTask,
};
logger.LogInformation($"Account with ID {account.Id} was authenticated");
return RequestResult.Ok(response);
}
public async Task<RequestResult> AuthenticateRefresh()
{
Guid jti = userService.GetJti();
AccountRefreshToken? token = await database.AccountRefreshTokens.FirstOrDefaultAsync(x => x.Id == jti);
if (token is null || token.ExpirationDate < DateTime.UtcNow)
{
return RequestResult.Unauthorized();
}
string refreshToken;
2024-04-27 22:36:16 +02:00
try
{
refreshToken = await tokensService.ExtendRefreshTokenAsync(token.Account, token.Id);
2024-04-27 22:36:16 +02:00
}
catch (TokenNotFoundException)
{
return RequestResult.Unauthorized();
}
catch (TokenNotExtendableException)
{
refreshToken = userService.GetRawToken().Replace("Bearer ", string.Empty);
2024-04-27 22:36:16 +02:00
}
string accessToken = await tokensService.CreateAccessTokenAsync(token.Account);
2024-04-27 22:36:16 +02:00
logger.LogInformation($"Account with ID {token.AccountId} was authenticated by token refreshing");
return RequestResult.Ok(new AuthenticateResponse
{
AccessToken = accessToken,
RefreshToken = refreshToken,
});
}
public async Task<RequestResult> Logout()
{
Guid jti = userService.GetJti();
AccountRefreshToken? token = await database.AccountRefreshTokens.FirstOrDefaultAsync(x => x.Id == jti);
if (token is not null)
{
database.AccountRefreshTokens.Attach(token);
database.AccountRefreshTokens.Remove(token);
await database.SaveChangesAsync();
}
return RequestResult.NoContent();
}
public async Task<RequestResult> GetAccountProfilePicture(long id)
{
Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id);
if (account is null)
{
return RequestResult.BadRequest()
.AddValidationError("id", "Account with this id does not exists");
}
if (account.ProfilePicture is null)
{
return RequestResult.NotFound();
}
AccountProfilePictureResponse picture = new AccountProfilePictureResponse(account.ProfilePicture);
return RequestResult.Ok(picture);
2024-04-27 22:36:16 +02:00
}
#endregion
#region PRIVATE METHODS
protected byte[] ComputeHash(string password, string leftSalt, string rightSalt) => SHA512.HashData(Encoding.UTF8.GetBytes($"{leftSalt}{password}{rightSalt}"));
#endregion
}