Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/building blocks/JSE.WebAPI.Core/User/AspNetUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public string ObterUserToken()
return EstaAutenticado() ? _accessor.HttpContext.User.GetUserToken() : "";
}

public string ObterUserRefreshToken()
{
return EstaAutenticado() ? _accessor.HttpContext.User.GetUserRefreshToken() : "";
}

public bool EstaAutenticado()
{
return _accessor.HttpContext.User.Identity.IsAuthenticated;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,17 @@ public static string GetUserToken(this ClaimsPrincipal principal)
var claim = principal.FindFirst("JWT");
return claim?.Value;
}

public static string GetUserRefreshToken(this ClaimsPrincipal principal)
{
if (principal == null)
{
throw new ArgumentException(nameof(principal));
}

var claim = principal.FindFirst("RefreshToken");
return claim?.Value;
}

}
}
1 change: 1 addition & 0 deletions src/building blocks/JSE.WebAPI.Core/User/IAspNetUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public interface IAspNetUser
Guid ObterUserId();
string ObterUserEmail();
string ObterUserToken();
string ObterUserRefreshToken();
bool EstaAutenticado();
bool PossuiRole(string role);
IEnumerable<Claim> ObterClaims();
Expand Down
66 changes: 17 additions & 49 deletions src/web/JSE.WebApp.MVC/Controllers/IdentidadeController.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
using JSE.WebApp.MVC.Models;
using JSE.WebApp.MVC.Controllers;
using JSE.WebApp.MVC.Models;
using JSE.WebApp.MVC.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace JSE.WebApp.MVC.Controllers
namespace NSE.WebApp.MVC.Controllers
{
public class IdentidadeController : MainController
{

private readonly IAutenticacaoService _autenticacaoService;

public IdentidadeController(IAutenticacaoService autenticacaoService)
public IdentidadeController(
IAutenticacaoService autenticacaoService)
{
_autenticacaoService = autenticacaoService;
}
Expand All @@ -27,18 +24,17 @@ public IActionResult Registro()

[HttpPost]
[Route("nova-conta")]
public async Task<IActionResult> Registro(UsuarioRegistroViewModel usuarioRegistroViewModel)
public async Task<IActionResult> Registro(UsuarioRegistroViewModel usuarioRegistro)
{
if (!ModelState.IsValid) return View(usuarioRegistroViewModel);
if (!ModelState.IsValid) return View(usuarioRegistro);

var resposta = await _autenticacaoService.Registro(usuarioRegistroViewModel);
var resposta = await _autenticacaoService.Registro(usuarioRegistro);

if (ResponsePossuiErros(resposta.ResponseResult)) return View(usuarioRegistroViewModel);
if (ResponsePossuiErros(resposta.ResponseResult)) return View(usuarioRegistro);

await RealizarLogin(resposta);
await _autenticacaoService.RealizarLogin(resposta);

return RedirectToAction("Index", "Catalogo");

}

[HttpGet]
Expand All @@ -51,17 +47,16 @@ public IActionResult Login(string returnUrl = null)

[HttpPost]
[Route("login")]
public async Task<IActionResult> Login(UsuarioLoginViewModel usuarioLoginViewModel, string returnUrl = null)
public async Task<IActionResult> Login(UsuarioLoginViewModel usuarioLogin, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (!ModelState.IsValid) return View(usuarioLogin);

if (!ModelState.IsValid) return View(usuarioLoginViewModel);
var resposta = await _autenticacaoService.Login(usuarioLogin);

var resposta = await _autenticacaoService.Login(usuarioLoginViewModel);
if (ResponsePossuiErros(resposta.ResponseResult)) return View(usuarioLogin);

if (ResponsePossuiErros(resposta.ResponseResult)) return View(usuarioLoginViewModel);

await RealizarLogin(resposta);
await _autenticacaoService.RealizarLogin(resposta);

if (string.IsNullOrEmpty(returnUrl)) return RedirectToAction("Index", "Catalogo");

Expand All @@ -72,35 +67,8 @@ public async Task<IActionResult> Login(UsuarioLoginViewModel usuarioLoginViewMod
[Route("sair")]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await _autenticacaoService.Logout();
return RedirectToAction("Index", "Catalogo");
}

private async Task RealizarLogin(UsuarioRespostaLoginViewModel usuarioRespostaLoginViewModel)
{
var token = ObterTokenFormatado(usuarioRespostaLoginViewModel.AccessToken);

var claims = new List<Claim>();
claims.Add(new Claim("JWT", usuarioRespostaLoginViewModel.AccessToken));
claims.AddRange(token.Claims);

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

var authProperties = new AuthenticationProperties
{
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(60),
IsPersistent = true
};

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
}

private static JwtSecurityToken ObterTokenFormatado(string jwtToken)
{
return new JwtSecurityTokenHandler().ReadToken(jwtToken) as JwtSecurityToken;
}
}
}
}
19 changes: 17 additions & 2 deletions src/web/JSE.WebApp.MVC/Extensions/ExceptionMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Polly.CircuitBreaker;
using JSE.WebApp.MVC.Services;
using Polly.CircuitBreaker;
using Refit;
using System.Net;

Expand All @@ -7,14 +8,17 @@ namespace JSE.WebApp.MVC.Extensions
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
private static IAutenticacaoService _autenticacaoService;

public ExceptionMiddleware(RequestDelegate next)
{
_next = next;
}

public async Task InvokeAsync(HttpContext httpContext)
public async Task InvokeAsync(HttpContext httpContext, IAutenticacaoService autenticacaoService)
{
_autenticacaoService = autenticacaoService;

try
{
await _next(httpContext);
Expand All @@ -41,6 +45,17 @@ private static void HandleRequestExceptionAsync(HttpContext context, HttpStatusC
{
if (statusCode == HttpStatusCode.Unauthorized)
{
if(_autenticacaoService.TokenExpirado())
{
if(_autenticacaoService.RefreshTokenValido().Result)
{
context.Response.Redirect(context.Request.Path);
return;
}
}

_autenticacaoService.Logout();

context.Response.Redirect($"/login?ReturnUrl={context.Request.Path}");
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace JSE.WebApp.MVC.Models
public class UsuarioRespostaLoginViewModel
{
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
public double ExpiresIn { get; set; }
public UsuarioTokenViewModel UsuarioToken { get; set; }
public ResponseResult ResponseResult { get; set; }
Expand Down
92 changes: 90 additions & 2 deletions src/web/JSE.WebApp.MVC/Services/AutenticacaoService.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
using JSE.Core.Communication;
using JSE.WebAPI.Core.User;
using JSE.WebApp.MVC.Extensions;
using JSE.WebApp.MVC.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Options;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace JSE.WebApp.MVC.Services
{
public class AutenticacaoService : Service, IAutenticacaoService
{
private readonly HttpClient _httpClient;
private readonly IAuthenticationService _authenticationService;
private readonly IAspNetUser _user;


public AutenticacaoService(HttpClient httpClient,
IOptions<AppSettings> settings)
IOptions<AppSettings> settings,
IAuthenticationService authenticationService,
IAspNetUser user)
{
httpClient.BaseAddress = new Uri(settings.Value.AutenticacaoUrl);

_httpClient = httpClient;
_httpClient = httpClient;
_authenticationService = authenticationService;
_user = user;
}

public async Task<UsuarioRespostaLoginViewModel> Login(UsuarioLoginViewModel usuarioLogin)
Expand Down Expand Up @@ -50,5 +62,81 @@ public async Task<UsuarioRespostaLoginViewModel> Registro(UsuarioRegistroViewMod

return await DeserializarObjetoResponse<UsuarioRespostaLoginViewModel>(response);
}

public async Task RealizarLogin(UsuarioRespostaLoginViewModel resposta)
{
var token = ObterTokenFormatado(resposta.AccessToken);

var claims = new List<Claim>();
claims.Add(new Claim("JWT", resposta.AccessToken));
claims.Add(new Claim("RefreshToken", resposta.RefreshToken));
claims.AddRange(token.Claims);

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

var authProperties = new AuthenticationProperties
{
ExpiresUtc = DateTimeOffset.UtcNow.AddHours(8),
IsPersistent = true
};

await _authenticationService.SignInAsync(
_user.ObterHttpContext(),
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
}

public async Task Logout()
{
await _authenticationService.SignOutAsync(
_user.ObterHttpContext(),
CookieAuthenticationDefaults.AuthenticationScheme,
null);
}

public static JwtSecurityToken ObterTokenFormatado(string jwtToken)
{
return new JwtSecurityTokenHandler().ReadToken(jwtToken) as JwtSecurityToken;
}

public async Task<UsuarioRespostaLoginViewModel> UtilizarRefreshToken(string refreshToken)
{
var refreshTokenContent = ObterConteudo(refreshToken);

var response = await _httpClient.PostAsync("/api/identidade/refresh-token", refreshTokenContent);

if (!TratarErrosResponse(response))
{
return new UsuarioRespostaLoginViewModel
{
ResponseResult = await DeserializarObjetoResponse<ResponseResult>(response)
};
}

return await DeserializarObjetoResponse<UsuarioRespostaLoginViewModel>(response);
}

public bool TokenExpirado()
{
var jwt = _user.ObterUserToken();
if (jwt is null) return false;

var token = ObterTokenFormatado(jwt);
return token.ValidTo.ToLocalTime() < DateTime.Now;
}

public async Task<bool> RefreshTokenValido()
{
var resposta = await UtilizarRefreshToken(_user.ObterUserRefreshToken());

if (resposta.AccessToken != null && resposta.ResponseResult == null)
{
await RealizarLogin(resposta);
return true;
}

return false;
}
}
}
5 changes: 4 additions & 1 deletion src/web/JSE.WebApp.MVC/Services/IAutenticacaoService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ namespace JSE.WebApp.MVC.Services
public interface IAutenticacaoService
{
Task<UsuarioRespostaLoginViewModel> Login(UsuarioLoginViewModel usuarioLogin);

Task<UsuarioRespostaLoginViewModel> Registro(UsuarioRegistroViewModel usuarioRegistro);
Task RealizarLogin(UsuarioRespostaLoginViewModel resposta);
Task Logout();
bool TokenExpirado();
Task<bool> RefreshTokenValido();
}
}
Loading