refactor(back): make code more declarative

This commit is contained in:
cuqmbr 2022-09-06 10:09:52 +03:00
parent a486bdfc80
commit 777c4b16b6
6 changed files with 65 additions and 73 deletions

View File

@ -1,9 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Server.Services; using Server.Services;
using Server.Settings; using Server.Settings;
using SharedModels.Requests; using SharedModels.Requests;
using SharedModels.Responses;
namespace Server.Controllers; namespace Server.Controllers;
@ -21,29 +21,31 @@ public class AuthenticationController : ControllerBase
} }
[HttpPost("register")] [HttpPost("register")]
public async Task<IActionResult> RegisterAsync([FromBody] RegistrationRequest model) public async Task<IActionResult> RegisterAsync([FromBody] RegistrationRequest registerRequest)
{ {
var result = await _authService.RegisterAsync(model); var (succeeded, message) =
await _authService.RegisterAsync(registerRequest);
if (!result.succeeded) if (!succeeded)
{ {
return BadRequest(result.message); return BadRequest(new ResponseBase {Message = message});
} }
return Ok(result); return Ok(new ResponseBase{ Message = message });
} }
[HttpPost("authenticate")] [HttpPost("authenticate")]
public async Task<IActionResult> GetTokenAsync(AuthenticationRequest authRequest) public async Task<IActionResult> GetTokenAsync(AuthenticationRequest authRequest)
{ {
var authResponse = await _authService.AuthenticateAsync(authRequest); var (succeeded, authResponse, refreshToken) =
await _authService.AuthenticateAsync(authRequest);
if (!authResponse.IsAuthenticated) if (!succeeded)
{ {
return BadRequest(authResponse); return BadRequest(authResponse);
} }
SetRefreshTokenInCookie(authResponse.RefreshToken); SetRefreshTokenInCookie(refreshToken!);
return Ok(authResponse); return Ok(authResponse);
} }
@ -53,16 +55,17 @@ public class AuthenticationController : ControllerBase
{ {
var refreshToken = Request.Cookies["refreshToken"]; var refreshToken = Request.Cookies["refreshToken"];
var authResponse = await _authService.RenewRefreshTokenAsync(refreshToken); var (succeeded, authResponse, newRefreshToken) =
await _authService.RenewRefreshTokenAsync(refreshToken);
if (!authResponse.IsAuthenticated) if (!succeeded)
{ {
return BadRequest(authResponse); return BadRequest(authResponse);
} }
if (!String.IsNullOrEmpty(authResponse.RefreshToken)) if (!String.IsNullOrEmpty(newRefreshToken))
{ {
SetRefreshTokenInCookie(authResponse.RefreshToken); SetRefreshTokenInCookie(newRefreshToken);
} }
return Ok(authResponse); return Ok(authResponse);
@ -72,19 +75,19 @@ public class AuthenticationController : ControllerBase
public async Task<IActionResult> RevokeToken([FromBody] RevokeRefreshTokenRequest revokeRequest) public async Task<IActionResult> RevokeToken([FromBody] RevokeRefreshTokenRequest revokeRequest)
{ {
// accept token from request body or cookie // accept token from request body or cookie
var token = revokeRequest?.Token ?? Request.Cookies["refreshToken"]; var token = revokeRequest.Token ?? Request.Cookies["refreshToken"];
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
{ {
return BadRequest(new { message = "Refresh token is required." }); return BadRequest(new ResponseBase{ Message = "Refresh token is required." });
} }
var response = await _authService.RevokeRefreshToken(token); var response = await _authService.RevokeRefreshToken(token);
if (!response) if (!response)
{ {
return NotFound(new { message = "Refresh token not found." }); return NotFound(new ResponseBase{ Message = "Refresh token not found." });
} }
return Ok(new { message = "Refresh token revoked." }); return Ok(new ResponseBase{ Message = "Refresh token revoked." });
} }
private void SetRefreshTokenInCookie(string refreshToken) private void SetRefreshTokenInCookie(string refreshToken)

View File

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SharedModels.Responses;
namespace Server.Controllers; namespace Server.Controllers;
@ -11,6 +12,9 @@ public class SecuredController : ControllerBase
[HttpGet] [HttpGet]
public async Task<IActionResult> GetSecuredData() public async Task<IActionResult> GetSecuredData()
{ {
return Ok("This Secured Data is available only for Authenticated Users with Admin role."); return Ok(new ResponseBase
{
Message = "This Secured Data is available only for Authenticated Users with Admin role."
});
} }
} }

View File

@ -29,7 +29,7 @@ public class AuthenticationService : IAuthenticationService
_jwt = jwt.Value; _jwt = jwt.Value;
} }
public async Task<(bool, string)> RegisterAsync(RegistrationRequest regRequest) public async Task<(bool succeeded, string message)> RegisterAsync(RegistrationRequest regRequest)
{ {
var userWithSameEmail = await _userManager.FindByEmailAsync(regRequest.Email); var userWithSameEmail = await _userManager.FindByEmailAsync(regRequest.Email);
if (userWithSameEmail != null) if (userWithSameEmail != null)
@ -40,18 +40,17 @@ public class AuthenticationService : IAuthenticationService
var user = new ApplicationUser {UserName = regRequest.Username, Email = regRequest.Email}; var user = new ApplicationUser {UserName = regRequest.Username, Email = regRequest.Email};
var result = await _userManager.CreateAsync(user, regRequest.Password); var result = await _userManager.CreateAsync(user, regRequest.Password);
if (result.Succeeded) if (!result.Succeeded)
{ {
await _userManager.AddToRoleAsync(user, Authorization.DefaultRole.ToString()); return (false, $"{result.Errors?.First().Description}");
return (true, $"User registered with email {user.Email}.");
}
else
{
return (false, $"{result.Errors?.First().Description}.");
} }
await _userManager.AddToRoleAsync(user, Authorization.DefaultRole.ToString());
return (true, $"User registered with email {user.Email}.");
} }
public async Task<AuthenticationResponse> AuthenticateAsync(AuthenticationRequest authRequest) public async Task<(bool succeeded, AuthenticationResponse authResponse,
string? refreshToken)> AuthenticateAsync(AuthenticationRequest authRequest)
{ {
var authResponse = new AuthenticationResponse(); var authResponse = new AuthenticationResponse();
@ -59,46 +58,42 @@ public class AuthenticationService : IAuthenticationService
if (user == null) if (user == null)
{ {
authResponse.IsAuthenticated = false;
authResponse.Message = $"No accounts registered with {authRequest.Email}."; authResponse.Message = $"No accounts registered with {authRequest.Email}.";
return authResponse; return (false, authResponse, null);
} }
if (!await _userManager.CheckPasswordAsync(user, authRequest.Password)) if (!await _userManager.CheckPasswordAsync(user, authRequest.Password))
{ {
authResponse.IsAuthenticated = false;
authResponse.Message = $"Incorrect login or password."; authResponse.Message = $"Incorrect login or password.";
return authResponse; return (false, authResponse, null);
} }
authResponse.IsAuthenticated = true;
authResponse.Email = user.Email;
authResponse.UserName = user.UserName;
var roles = await _userManager.GetRolesAsync(user);
authResponse.Roles = roles.ToList();
var jwtSecurityToken = await CreateJwtToken(user); var jwtSecurityToken = await CreateJwtToken(user);
authResponse.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); authResponse.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
string refreshTokenString;
if (user.RefreshTokens.Any(t => t.IsActive)) if (user.RefreshTokens.Any(t => t.IsActive))
{ {
var activeRefreshToken = var activeRefreshToken =
user.RefreshTokens.First(t => t.IsActive); user.RefreshTokens.First(t => t.IsActive);
authResponse.RefreshToken = activeRefreshToken.Token; refreshTokenString = activeRefreshToken.Token;
authResponse.RefreshTokenExpiration = activeRefreshToken.Expires; authResponse.RefreshTokenExpirationDate = activeRefreshToken.Expires;
} }
else else
{ {
var refreshToken = CreateRefreshToken(); var refreshToken = CreateRefreshToken();
authResponse.RefreshToken = refreshToken.Token; refreshTokenString = refreshToken.Token;
authResponse.RefreshTokenExpiration = refreshToken.Expires; authResponse.RefreshTokenExpirationDate = refreshToken.Expires;
user.RefreshTokens.Add(refreshToken); user.RefreshTokens.Add(refreshToken);
await _userManager.UpdateAsync(user); await _userManager.UpdateAsync(user);
} }
return authResponse; return (true, authResponse, refreshTokenString);
} }
public async Task<AuthenticationResponse> RenewRefreshTokenAsync(string? token) public async Task<(bool succeeded, AuthenticationResponse authResponse,
string? refreshToken)> RenewRefreshTokenAsync(string? token)
{ {
var authResponse = new AuthenticationResponse(); var authResponse = new AuthenticationResponse();
@ -107,18 +102,16 @@ public class AuthenticationService : IAuthenticationService
if (user == null) if (user == null)
{ {
authResponse.IsAuthenticated = false;
authResponse.Message = "Refresh token did not mach any user."; authResponse.Message = "Refresh token did not mach any user.";
return authResponse; return (false, authResponse, null);
} }
var refreshToken = user.RefreshTokens.Single(t => t.Token == token); var refreshToken = user.RefreshTokens.Single(t => t.Token == token);
if (!refreshToken.IsActive) if (!refreshToken.IsActive)
{ {
authResponse.IsAuthenticated = false;
authResponse.Message = "Refresh token expired."; authResponse.Message = "Refresh token expired.";
return authResponse; return (false, authResponse, null);
} }
//Revoke Current Refresh Token //Revoke Current Refresh Token
@ -130,20 +123,12 @@ public class AuthenticationService : IAuthenticationService
await _userManager.UpdateAsync(user); await _userManager.UpdateAsync(user);
//Generates new jwt //Generates new jwt
authResponse.IsAuthenticated = true;
authResponse.Email = user.Email;
authResponse.UserName = user.UserName;
var roles = await _userManager.GetRolesAsync(user);
authResponse.Roles = roles.ToList();
var jwtSecurityToken = await CreateJwtToken(user); var jwtSecurityToken = await CreateJwtToken(user);
authResponse.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); authResponse.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
authResponse.RefreshToken = newRefreshToken.Token; authResponse.RefreshTokenExpirationDate = newRefreshToken.Expires;
authResponse.RefreshTokenExpiration = newRefreshToken.Expires;
return authResponse; return (true, authResponse, newRefreshToken.Token);
} }
public async Task<bool> RevokeRefreshToken(string? token) public async Task<bool> RevokeRefreshToken(string? token)
@ -183,10 +168,10 @@ public class AuthenticationService : IAuthenticationService
var claims = new[] var claims = new[]
{ {
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, user.Email), new Claim("uid", user.Id),
new Claim("uid", user.Id) new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email)
} }
.Union(userClaims) .Union(userClaims)
.Union(roleClaims); .Union(roleClaims);

View File

@ -8,9 +8,9 @@ public interface IAuthenticationService
{ {
Task<(bool succeeded, string message)> RegisterAsync(RegistrationRequest regRequest); Task<(bool succeeded, string message)> RegisterAsync(RegistrationRequest regRequest);
Task<AuthenticationResponse> AuthenticateAsync(AuthenticationRequest authRequest); Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)> AuthenticateAsync(AuthenticationRequest authRequest);
Task<AuthenticationResponse> RenewRefreshTokenAsync(string? token); Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)> RenewRefreshTokenAsync(string? token);
Task<bool> RevokeRefreshToken(string? token); Task<bool> RevokeRefreshToken(string? token);
} }

View File

@ -1,16 +1,7 @@
using System.Text.Json.Serialization;
namespace SharedModels.Responses; namespace SharedModels.Responses;
public class AuthenticationResponse public class AuthenticationResponse : ResponseBase
{ {
public string Message { get; set; } = null!; public string? Token { get; set; }
public bool IsAuthenticated { get; set; } public DateTime? RefreshTokenExpirationDate { get; set; }
public string UserName { get; set; } = null!;
public string Email { get; set; } = null!;
public List<string> Roles { get; set; } = null!;
public string Token { get; set; } = null!;
[JsonIgnore]
public string RefreshToken { get; set; } = null!;
public DateTime RefreshTokenExpiration { get; set; }
} }

View File

@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace SharedModels.Responses;
public class ResponseBase
{
[JsonInclude]
public string? Message;
}