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
Binary file removed Certificado ASP.NET Core Enterprise Application.png
Binary file not shown.
Binary file removed Certificado Formação Full Stack Developer.png
Binary file not shown.
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
4 changes: 3 additions & 1 deletion src/services/JSE.Identidade.API/Configuration/ApiConfig.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using JSE.WebAPI.Core.IdentityConfiguration;
using JSE.Identidade.API.Services;
using JSE.WebAPI.Core.IdentityConfiguration;
using JSE.WebAPI.Core.User;
using NetDevPack.Security.JwtSigningCredentials.AspNetCore;

Expand All @@ -10,6 +11,7 @@ public static IServiceCollection AddApiConfiguration(this IServiceCollection ser
{
services.AddControllers();

services.AddScoped<AuthenticationService>();
services.AddScoped<IAspNetUser, AspNetUser>();

return services;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ public static IServiceCollection AddIdentityConfiguration(this IServiceCollectio
IConfiguration configuration)
{

var appSettingsSection = configuration.GetSection("AppTokenSettings");
services.Configure<AppTokenSettings>(appSettingsSection);

services.AddJwksManager(options => options.Algorithm = Algorithm.ES256)
.PersistKeysToDatabaseStore<ApplicationDbContext>();

Expand Down
145 changes: 40 additions & 105 deletions src/services/JSE.Identidade.API/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
@@ -1,44 +1,29 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System;
using System.Threading.Tasks;
using JSE.Core.Messages.Integration;
using JSE.MessageBus;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using JSE.Core.Messages.Integration;
using JSE.Identidade.API.Models;
using JSE.WebAPI.Core.Controllers;
using JSE.Identidade.API.Services;
using JSE.MessageBus;
using JSE.Core.Messages.Integration;
using JSE.WebAPI.Core.IdentityConfiguration;
using JSE.WebAPI.Core.User;
using NetDevPack.Security.JwtSigningCredentials.Interfaces;
using JSE.WebAPI.Core.Controllers;

namespace JSE.Identidade.API.Controllers
{
[Route("api/identidade")]
public class AuthController : MainController
{
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
private readonly AppSettings _appSettings;
private readonly IAspNetUser _aspNetUser;
private readonly IJsonWebKeySetService _jwksService;

private readonly AuthenticationService _authenticationService;
private readonly IMessageBus _bus;

public AuthController(SignInManager<IdentityUser> signInManager,
UserManager<IdentityUser> userManager,
IOptions<AppSettings> appSettings,
IMessageBus bus,
IAspNetUser aspNetUser,
IJsonWebKeySetService jwksService)
public AuthController(
AuthenticationService authenticationService,
IMessageBus bus)
{
_signInManager = signInManager;
_userManager = userManager;
_appSettings = appSettings.Value;
_authenticationService = authenticationService;
_bus = bus;
_aspNetUser = aspNetUser;
_jwksService = jwksService;
}

[HttpPost("nova-conta")]
Expand All @@ -53,19 +38,19 @@ public async Task<ActionResult> Registrar(UsuarioRegistroViewModel usuarioRegist
EmailConfirmed = true
};

var result = await _userManager.CreateAsync(user, usuarioRegistro.Senha);
var result = await _authenticationService.UserManager.CreateAsync(user, usuarioRegistro.Senha);

if (result.Succeeded)
{
var clienteResult = await RegistrarCliente(usuarioRegistro);

if(!clienteResult.ValidationResult.IsValid)
if (!clienteResult.ValidationResult.IsValid)
{
await _userManager.DeleteAsync(user);
await _authenticationService.UserManager.DeleteAsync(user);
return CustomResponse(clienteResult.ValidationResult);
}

return CustomResponse(await GerarJwt(usuarioRegistro.Email));
return CustomResponse(await _authenticationService.GerarJwt(usuarioRegistro.Email));
}

foreach (var error in result.Errors)
Expand All @@ -79,15 +64,14 @@ public async Task<ActionResult> Registrar(UsuarioRegistroViewModel usuarioRegist
[HttpPost("autenticar")]
public async Task<ActionResult> Login(UsuarioLoginViewModel usuarioLogin)
{

if (!ModelState.IsValid) return CustomResponse(ModelState);

var result = await _signInManager.PasswordSignInAsync(usuarioLogin.Email, usuarioLogin.Senha,
var result = await _authenticationService.SignInManager.PasswordSignInAsync(usuarioLogin.Email, usuarioLogin.Senha,
false, true);

if (result.Succeeded)
{
return CustomResponse(await GerarJwt(usuarioLogin.Email));
return CustomResponse(await _authenticationService.GerarJwt(usuarioLogin.Email));
}

if (result.IsLockedOut)
Expand All @@ -100,91 +84,42 @@ public async Task<ActionResult> Login(UsuarioLoginViewModel usuarioLogin)
return CustomResponse();
}

private async Task<UsuarioRespostaLoginViewModel> GerarJwt(string email)
{
var user = await _userManager.FindByEmailAsync(email);
var claims = await _userManager.GetClaimsAsync(user);

var identityClaims = await ObterClaimsUsuario(claims, user);
var encodedToken = CodificarToken(identityClaims);

return ObterRespostaToken(encodedToken, user, claims);
}

private async Task<ClaimsIdentity> ObterClaimsUsuario(ICollection<Claim> claims, IdentityUser user)
private async Task<ResponseMessage> RegistrarCliente(UsuarioRegistroViewModel usuarioRegistro)
{
var userRoles = await _userManager.GetRolesAsync(user);
var usuario = await _authenticationService.UserManager.FindByEmailAsync(usuarioRegistro.Email);

claims.Add(new Claim(JwtRegisteredClaimNames.Sub, user.Id));
claims.Add(new Claim(JwtRegisteredClaimNames.Email, user.Email));
claims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
claims.Add(new Claim(JwtRegisteredClaimNames.Nbf, ToUnixEpochDate(DateTime.UtcNow).ToString()));
claims.Add(new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(DateTime.UtcNow).ToString(), ClaimValueTypes.Integer64));
var usuarioRegistrado = new UsuarioRegistradoIntegrationEvent(
Guid.Parse(usuario.Id), usuarioRegistro.Nome, usuarioRegistro.Email, usuarioRegistro.Cpf);

foreach (var userRole in userRoles)
try
{
claims.Add(new Claim("role", userRole));
return await _bus.RequestAsync<UsuarioRegistradoIntegrationEvent, ResponseMessage>(usuarioRegistrado);
}

var identityClaims = new ClaimsIdentity();
identityClaims.AddClaims(claims);

return identityClaims;
}

private string CodificarToken(ClaimsIdentity identityClaims)
{
var tokenHandler = new JwtSecurityTokenHandler();

var currentIssuer = $"{_aspNetUser.ObterHttpContext().Request.Scheme}://{_aspNetUser.ObterHttpContext().Request.Host}";

var key = _jwksService.GetCurrent();
var token = tokenHandler.CreateToken(new SecurityTokenDescriptor
catch
{
Issuer = currentIssuer,
Subject = identityClaims,
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = key
});

return tokenHandler.WriteToken(token);
await _authenticationService.UserManager.DeleteAsync(usuario);
throw;
}
}

private UsuarioRespostaLoginViewModel ObterRespostaToken(string encodedToken, IdentityUser user, IEnumerable<Claim> claims)
[HttpPost("refresh-token")]
public async Task<ActionResult> RefreshToken([FromBody] string refreshToken)
{
return new UsuarioRespostaLoginViewModel
if (string.IsNullOrEmpty(refreshToken))
{
AccessToken = encodedToken,
ExpiresIn = TimeSpan.FromHours(1).TotalSeconds,
UsuarioToken = new UsuarioTokenViewModel
{
Id = user.Id,
Email = user.Email,
Claims = claims.Select(c => new UsuarioClaimViewModel { Type = c.Type, Value = c.Value })
}
};
}

private static long ToUnixEpochDate(DateTime date)
=> (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);

private async Task<ResponseMessage> RegistrarCliente(UsuarioRegistroViewModel usuarioRegistro)
{
var usuario = await _userManager.FindByEmailAsync(usuarioRegistro.Email);

var usuarioRegistrado = new UsuarioRegistradoIntegrationEvent(
Guid.Parse(usuario.Id), usuarioRegistro.Nome, usuarioRegistro.Email, usuarioRegistro.Cpf);
AddProcessingError("Refresh Token inválido");
return CustomResponse();
}

var token = await _authenticationService.ObterRefreshToken(Guid.Parse(refreshToken));

try
{
return await _bus.RequestAsync<UsuarioRegistradoIntegrationEvent, ResponseMessage>(usuarioRegistrado);
}
catch
if (token is null)
{
await _userManager.DeleteAsync(usuario);
throw;
AddProcessingError("Refresh Token expirado");
return CustomResponse();
}

return CustomResponse(await _authenticationService.GerarJwt(token.UserName));
}
}
}
5 changes: 4 additions & 1 deletion src/services/JSE.Identidade.API/Data/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using JSE.Identidade.API.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NetDevPack.Security.JwtSigningCredentials;
using NetDevPack.Security.JwtSigningCredentials.Store.EntityFrameworkCore;
Expand All @@ -10,5 +11,7 @@ public class ApplicationDbContext : IdentityDbContext, ISecurityKeyContext
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

public DbSet<SecurityKeyWithPrivate> SecurityKeys { get; set; }

public DbSet<RefreshToken> RefreshTokens { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace JSE.Identidade.API.Extensions
{
public class AppTokenSettings
{
public int RefreshTokenExpiration { get; set; }
}
}
Loading
Loading