diff --git a/BackEnd (ASP.NET Web API)/Server/Controllers/AuthenticationController.cs b/BackEnd (ASP.NET Web API)/Server/Controllers/AuthenticationController.cs index acf21c4..e19f144 100644 --- a/BackEnd (ASP.NET Web API)/Server/Controllers/AuthenticationController.cs +++ b/BackEnd (ASP.NET Web API)/Server/Controllers/AuthenticationController.cs @@ -1,9 +1,9 @@ -using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Server.Services; using Server.Settings; using SharedModels.Requests; +using SharedModels.Responses; namespace Server.Controllers; @@ -21,29 +21,31 @@ public class AuthenticationController : ControllerBase } [HttpPost("register")] - public async Task RegisterAsync([FromBody] RegistrationRequest model) + public async Task 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")] public async Task 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); } - SetRefreshTokenInCookie(authResponse.RefreshToken); + SetRefreshTokenInCookie(refreshToken!); return Ok(authResponse); } @@ -53,16 +55,17 @@ public class AuthenticationController : ControllerBase { 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); } - if (!String.IsNullOrEmpty(authResponse.RefreshToken)) + if (!String.IsNullOrEmpty(newRefreshToken)) { - SetRefreshTokenInCookie(authResponse.RefreshToken); + SetRefreshTokenInCookie(newRefreshToken); } return Ok(authResponse); @@ -72,19 +75,19 @@ public class AuthenticationController : ControllerBase public async Task RevokeToken([FromBody] RevokeRefreshTokenRequest revokeRequest) { // 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)) { - return BadRequest(new { message = "Refresh token is required." }); + return BadRequest(new ResponseBase{ Message = "Refresh token is required." }); } var response = await _authService.RevokeRefreshToken(token); 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) diff --git a/BackEnd (ASP.NET Web API)/Server/Controllers/SecuredController.cs b/BackEnd (ASP.NET Web API)/Server/Controllers/SecuredController.cs index fb1623c..3b369c5 100644 --- a/BackEnd (ASP.NET Web API)/Server/Controllers/SecuredController.cs +++ b/BackEnd (ASP.NET Web API)/Server/Controllers/SecuredController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using SharedModels.Responses; namespace Server.Controllers; @@ -11,6 +12,9 @@ public class SecuredController : ControllerBase [HttpGet] public async Task 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." + }); } } \ No newline at end of file diff --git a/BackEnd (ASP.NET Web API)/Server/Services/AuthenticationService.cs b/BackEnd (ASP.NET Web API)/Server/Services/AuthenticationService.cs index e2bbcb0..1bd15a9 100644 --- a/BackEnd (ASP.NET Web API)/Server/Services/AuthenticationService.cs +++ b/BackEnd (ASP.NET Web API)/Server/Services/AuthenticationService.cs @@ -29,7 +29,7 @@ public class AuthenticationService : IAuthenticationService _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); if (userWithSameEmail != null) @@ -40,18 +40,17 @@ public class AuthenticationService : IAuthenticationService var user = new ApplicationUser {UserName = regRequest.Username, Email = regRequest.Email}; var result = await _userManager.CreateAsync(user, regRequest.Password); - if (result.Succeeded) + if (!result.Succeeded) { - await _userManager.AddToRoleAsync(user, Authorization.DefaultRole.ToString()); - return (true, $"User registered with email {user.Email}."); - } - else - { - return (false, $"{result.Errors?.First().Description}."); + return (false, $"{result.Errors?.First().Description}"); } + + await _userManager.AddToRoleAsync(user, Authorization.DefaultRole.ToString()); + return (true, $"User registered with email {user.Email}."); } - public async Task AuthenticateAsync(AuthenticationRequest authRequest) + public async Task<(bool succeeded, AuthenticationResponse authResponse, + string? refreshToken)> AuthenticateAsync(AuthenticationRequest authRequest) { var authResponse = new AuthenticationResponse(); @@ -59,46 +58,42 @@ public class AuthenticationService : IAuthenticationService if (user == null) { - authResponse.IsAuthenticated = false; authResponse.Message = $"No accounts registered with {authRequest.Email}."; - return authResponse; + return (false, authResponse, null); } if (!await _userManager.CheckPasswordAsync(user, authRequest.Password)) { - authResponse.IsAuthenticated = false; 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); authResponse.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); + string refreshTokenString; + if (user.RefreshTokens.Any(t => t.IsActive)) { var activeRefreshToken = user.RefreshTokens.First(t => t.IsActive); - authResponse.RefreshToken = activeRefreshToken.Token; - authResponse.RefreshTokenExpiration = activeRefreshToken.Expires; + refreshTokenString = activeRefreshToken.Token; + authResponse.RefreshTokenExpirationDate = activeRefreshToken.Expires; } else { var refreshToken = CreateRefreshToken(); - authResponse.RefreshToken = refreshToken.Token; - authResponse.RefreshTokenExpiration = refreshToken.Expires; + refreshTokenString = refreshToken.Token; + authResponse.RefreshTokenExpirationDate = refreshToken.Expires; user.RefreshTokens.Add(refreshToken); await _userManager.UpdateAsync(user); } - return authResponse; + return (true, authResponse, refreshTokenString); } - public async Task RenewRefreshTokenAsync(string? token) + public async Task<(bool succeeded, AuthenticationResponse authResponse, + string? refreshToken)> RenewRefreshTokenAsync(string? token) { var authResponse = new AuthenticationResponse(); @@ -107,18 +102,16 @@ public class AuthenticationService : IAuthenticationService if (user == null) { - authResponse.IsAuthenticated = false; authResponse.Message = "Refresh token did not mach any user."; - return authResponse; + return (false, authResponse, null); } var refreshToken = user.RefreshTokens.Single(t => t.Token == token); if (!refreshToken.IsActive) { - authResponse.IsAuthenticated = false; authResponse.Message = "Refresh token expired."; - return authResponse; + return (false, authResponse, null); } //Revoke Current Refresh Token @@ -130,20 +123,12 @@ public class AuthenticationService : IAuthenticationService await _userManager.UpdateAsync(user); //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); authResponse.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); - authResponse.RefreshToken = newRefreshToken.Token; - authResponse.RefreshTokenExpiration = newRefreshToken.Expires; + authResponse.RefreshTokenExpirationDate = newRefreshToken.Expires; - return authResponse; + return (true, authResponse, newRefreshToken.Token); } public async Task RevokeRefreshToken(string? token) @@ -183,10 +168,10 @@ public class AuthenticationService : IAuthenticationService var claims = new[] { - new Claim(JwtRegisteredClaimNames.Sub, user.UserName), 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(roleClaims); diff --git a/BackEnd (ASP.NET Web API)/Server/Services/IAuthenticationService.cs b/BackEnd (ASP.NET Web API)/Server/Services/IAuthenticationService.cs index b701876..fc8d3e3 100644 --- a/BackEnd (ASP.NET Web API)/Server/Services/IAuthenticationService.cs +++ b/BackEnd (ASP.NET Web API)/Server/Services/IAuthenticationService.cs @@ -8,9 +8,9 @@ public interface IAuthenticationService { Task<(bool succeeded, string message)> RegisterAsync(RegistrationRequest regRequest); - Task AuthenticateAsync(AuthenticationRequest authRequest); + Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)> AuthenticateAsync(AuthenticationRequest authRequest); - Task RenewRefreshTokenAsync(string? token); + Task<(bool succeeded, AuthenticationResponse authResponse, string? refreshToken)> RenewRefreshTokenAsync(string? token); Task RevokeRefreshToken(string? token); } \ No newline at end of file diff --git a/BackEnd (ASP.NET Web API)/SharedModels/Responses/AuthenticationResponse.cs b/BackEnd (ASP.NET Web API)/SharedModels/Responses/AuthenticationResponse.cs index 7b67e9d..4e8f112 100644 --- a/BackEnd (ASP.NET Web API)/SharedModels/Responses/AuthenticationResponse.cs +++ b/BackEnd (ASP.NET Web API)/SharedModels/Responses/AuthenticationResponse.cs @@ -1,16 +1,7 @@ -using System.Text.Json.Serialization; - namespace SharedModels.Responses; -public class AuthenticationResponse +public class AuthenticationResponse : ResponseBase { - public string Message { get; set; } = null!; - public bool IsAuthenticated { get; set; } - public string UserName { get; set; } = null!; - public string Email { get; set; } = null!; - public List Roles { get; set; } = null!; - public string Token { get; set; } = null!; - [JsonIgnore] - public string RefreshToken { get; set; } = null!; - public DateTime RefreshTokenExpiration { get; set; } + public string? Token { get; set; } + public DateTime? RefreshTokenExpirationDate { get; set; } } \ No newline at end of file diff --git a/BackEnd (ASP.NET Web API)/SharedModels/Responses/ResponseBase.cs b/BackEnd (ASP.NET Web API)/SharedModels/Responses/ResponseBase.cs new file mode 100644 index 0000000..6cb8890 --- /dev/null +++ b/BackEnd (ASP.NET Web API)/SharedModels/Responses/ResponseBase.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace SharedModels.Responses; + +public class ResponseBase +{ + [JsonInclude] + public string? Message; +} \ No newline at end of file