From e587ec71da3f457a945c1d3c5d6cf8c89445cd11 Mon Sep 17 00:00:00 2001 From: HombreLaser Date: Sun, 20 Nov 2022 19:38:18 -0600 Subject: Añadido método refresh a controlador de sesiones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Controllers/UserAccountSessionsController.cs | 18 +++++++++++-- Errors/ExpiredSessionError.cs | 7 +++++ Logics/BaseUserAccountLogic.cs | 3 +-- Logics/CreateUserAccountLogic.cs | 1 + Logics/CreateUserAccountSessionLogic.cs | 1 + Logics/RefreshTokenLogic.cs | 40 ++++++++++++++++++++++++++++ Models/Raffle.cs | 2 ++ Services/ITokenGenerator.cs | 2 ++ Services/TokenGenerator.cs | 21 +++++++++++++++ 9 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 Errors/ExpiredSessionError.cs create mode 100644 Logics/RefreshTokenLogic.cs create mode 100644 Models/Raffle.cs diff --git a/Controllers/UserAccountSessionsController.cs b/Controllers/UserAccountSessionsController.cs index 1309c8e..217c05c 100644 --- a/Controllers/UserAccountSessionsController.cs +++ b/Controllers/UserAccountSessionsController.cs @@ -1,5 +1,5 @@ -using AutoMapper; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using BackendPIA.Forms; using BackendPIA.Models; @@ -11,12 +11,14 @@ namespace BackendPIA.Controllers { [Route("api/")] [ApiController] public class AdministratorSessionsController : ControllerBase { + private readonly ApplicationDbContext _context; private readonly ITokenGenerator _token_generator; private readonly UserManager _manager; - public AdministratorSessionsController(ITokenGenerator token_generator, UserManager manager) { + public AdministratorSessionsController(ITokenGenerator token_generator, UserManager manager, ApplicationDbContext context) { _token_generator = token_generator; _manager = manager; + _context = context; } [HttpPost("login")] @@ -29,5 +31,17 @@ namespace BackendPIA.Controllers { return StatusCode(401, new InvalidLoginError(401, "Check your credentials")); } + + // [Authorize] + [HttpPost("refresh")] + public async Task> Refresh(AuthenticationToken form) { + RefreshTokenLogic logic = new RefreshTokenLogic(_token_generator, _manager, form); + var result = await logic.Call(); + + if(result) + return Ok(logic.Token); + + return StatusCode(403, new ExpiredSessionError(401, "Check your refresh token")); + } } } \ No newline at end of file diff --git a/Errors/ExpiredSessionError.cs b/Errors/ExpiredSessionError.cs new file mode 100644 index 0000000..761bbec --- /dev/null +++ b/Errors/ExpiredSessionError.cs @@ -0,0 +1,7 @@ +namespace BackendPIA.Errors { + public class ExpiredSessionError : ErrorBase { + public int Status { get { return base.Status; } } + public string Message { get { return base.Message; }} + public ExpiredSessionError(int status, string message) : base(status, message) {} + } +} \ No newline at end of file diff --git a/Logics/BaseUserAccountLogic.cs b/Logics/BaseUserAccountLogic.cs index 82c28eb..4ce17e0 100644 --- a/Logics/BaseUserAccountLogic.cs +++ b/Logics/BaseUserAccountLogic.cs @@ -19,11 +19,10 @@ namespace BackendPIA.Logics { var roles = await _manager.GetRolesAsync(user); _token = new AuthenticationToken { Token = _token_generator.Generate(user, roles[0]), RefreshToken = _token_generator.GenerateRefreshToken() }; - await SetUserRefreshToken(user); } // We overwrite or set the value of the session token in the database: all other previous logins are invalid. - private async Task SetUserRefreshToken(UserAccount user) { + protected async Task SetUserRefreshToken(UserAccount user) { user.SessionToken = _token.RefreshToken; user.SessionTokenExpiryTime = DateTime.UtcNow.AddHours(3); await _manager.UpdateAsync(user); diff --git a/Logics/CreateUserAccountLogic.cs b/Logics/CreateUserAccountLogic.cs index dd37837..93859a4 100644 --- a/Logics/CreateUserAccountLogic.cs +++ b/Logics/CreateUserAccountLogic.cs @@ -28,6 +28,7 @@ namespace BackendPIA.Logics{ if(result.Succeeded) { await SetAuthenticationToken(user); + await SetUserRefreshToken(user); return true; } diff --git a/Logics/CreateUserAccountSessionLogic.cs b/Logics/CreateUserAccountSessionLogic.cs index 2e51791..d5c3aeb 100644 --- a/Logics/CreateUserAccountSessionLogic.cs +++ b/Logics/CreateUserAccountSessionLogic.cs @@ -21,6 +21,7 @@ namespace BackendPIA.Logics { if(result) { await SetAuthenticationToken(user); + await SetUserRefreshToken(user); return true; } diff --git a/Logics/RefreshTokenLogic.cs b/Logics/RefreshTokenLogic.cs new file mode 100644 index 0000000..200438a --- /dev/null +++ b/Logics/RefreshTokenLogic.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Identity; +using BackendPIA.Services; +using BackendPIA.Models; +using BackendPIA.Forms; + +namespace BackendPIA.Logics { + public class RefreshTokenLogic : BaseUserAccountLogic { + private readonly AuthenticationToken _form; + + public RefreshTokenLogic(ITokenGenerator token_generator, UserManager manager, AuthenticationToken form) : base(token_generator, manager) { + _form = form; + } + + public async Task Call() { + var email = _token_generator.GetPrincipalFromToken(_form.Token); + + if(email == null) + return false; + // Checks. + var user = await _manager.FindByEmailAsync(email); + + if(user == null) + return false; + + if(user.SessionTokenExpiryTime == null || user.SessionTokenExpiryTime < DateTime.UtcNow + || user.SessionToken == null || user.SessionToken != _form.RefreshToken) { + user.SessionToken = null; + user.SessionTokenExpiryTime = null; + _manager.UpdateAsync(user); + + return false; + } + + await SetAuthenticationToken(user); + _token.RefreshToken = user.SessionToken; + + return true; + } + } +} \ No newline at end of file diff --git a/Models/Raffle.cs b/Models/Raffle.cs new file mode 100644 index 0000000..e06b173 --- /dev/null +++ b/Models/Raffle.cs @@ -0,0 +1,2 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Identity; \ No newline at end of file diff --git a/Services/ITokenGenerator.cs b/Services/ITokenGenerator.cs index 3738f81..32db2b6 100644 --- a/Services/ITokenGenerator.cs +++ b/Services/ITokenGenerator.cs @@ -1,8 +1,10 @@ +using System.Security.Claims; using BackendPIA.Models; namespace BackendPIA.Services { public interface ITokenGenerator { public string Generate(UserAccount user, string role); public string GenerateRefreshToken(); + public string? GetPrincipalFromToken(string token); } } \ No newline at end of file diff --git a/Services/TokenGenerator.cs b/Services/TokenGenerator.cs index 514417d..f792cf9 100644 --- a/Services/TokenGenerator.cs +++ b/Services/TokenGenerator.cs @@ -36,5 +36,26 @@ namespace BackendPIA.Services { return Convert.ToBase64String(random_number); } + + public string? GetPrincipalFromToken(string token) { + var tokenValidationParameters = new TokenValidationParameters { + ValidateAudience = false, + ValidateIssuer = false, + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_key)), + ValidateLifetime = false + }; + var tokenHandler = new JwtSecurityTokenHandler(); + SecurityToken security_token; + var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out security_token); + var jwtSecurityToken = security_token as JwtSecurityToken; + + if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase)) + return null; + + var jwt = tokenHandler.ReadJwtToken(token); + + return jwt.Claims.Where(c => c.Type == "email").First().Value; + } } } \ No newline at end of file -- cgit v1.2.3