Segue Pacotes de dependênicas usadas na demo:
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.5" />
Passo 1 Criar a classe de db context:
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
Passo 2 Adicionar serviço de db context na aplicação:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
Passo 3 Configurar a conection string no arquivo appsettings.json:
"ConnectionStrings": {
"DefaultConnection": "Server=DESKTOP-0KTTMSL;Database=TestIdentity;Trusted_Connection=True;"
},
Passo 4: Criar o modelo de user:
public class User : IdentityUser
{
// any custom attribute here
}
Passo 5: Adicionar o serviço de Identity na aplicação:
services.AddIdentity<User, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
Passo 6: Configuar a aplicação a usar o authentication e o authorization
app.UseAuthentication();
app.UseAuthorization();
Passo 7: Criar o controller de autenticação:
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly SignInManager<User> signInManager;
private readonly UserManager<User> userManager;
public AuthController(SignInManager<User> signInManager, UserManager<User> userManager)
{
this.signInManager = signInManager;
this.userManager = userManager;
}
}
Passo 8: Criar view model da tela de cadastro:
public class RegisterUserViewModel
{
[Required(ErrorMessage = "O Campo {0} é obrigatório.")]
[EmailAddress(ErrorMessage = "O campo {0} deve ser um email válido.")]
public string Email { get; set; }
[Required(ErrorMessage = "O Campo {0} é obrigatório.")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Compare("Password")]
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; }
}
Passo 9: Adicionar metodo de registrar novo usuário no AuthController:
[HttpPost("register")]
public async Task<ActionResult> Register(RegisterUserViewModel registerUser)
{
if (!ModelState.IsValid) return BadRequest(ModelState.Values.SelectMany(e => e.Errors));
User user = new()
{
UserName = registerUser.Email,
Email = registerUser.Email,
EmailConfirmed = true
};
var result = await userManager.CreateAsync(user, registerUser.Password);
if (!result.Succeeded) return BadRequest(result.Errors);
await signInManager.SignInAsync(user, false);
return Ok();
}
Passo 10: Criar view model da tela de login:
public class LoginUserViewModel
{
[Required(ErrorMessage = "O Campo {0} é obrigatório.")]
[EmailAddress(ErrorMessage = "O campo {0} deve ser um email válido.")]
public string Email { get; set; }
[Required(ErrorMessage = "O Campo {0} é obrigatório.")]
[DataType(DataType.Password)]
public string Password { get; set; }
public bool RememberMe { get; set; } = false;
}
Passo 11: Criar metodo de login no AuthController:
[HttpPost("login")]
public async Task<ActionResult> Login(LoginUserViewModel loginUser)
{
if (!ModelState.IsValid) return BadRequest(ModelState.Values.SelectMany(e => e.Errors));
var result = await signInManager.PasswordSignInAsync(loginUser.Email, loginUser.Password, loginUser.RememberMe, lockoutOnFailure: true);
if (!result.Succeeded) return BadRequest("Login or Password invalid.");
return Ok();
}
Passo 12: Criar metodo de logout no AuthController:
[HttpPost("logout")]
public async Task<ActionResult> Logout()
{
await signInManager.SignOutAsync();
return Ok();
}
Antes de rodar e testa a aplicação, é preciso criar as tabelas do Idenitiy no banco de dados. Para criar as tabelas do Identity rode os seguintes comandos:
Add-Migration Identity
Update-Database
Passo 1: Adicionar a sessão AppSettins no arquivo appsettings.json:
"AppSettings": {
"Secret": "your-secrete-hash-here",
"Expiration": 2,
"Emitter": "",
"ValidIn": "https://localhost"
}
Passo 2: Criar classe de Settings para usar no JWT
public class AppSettings
{
public string Secret { get; set; }
public int Expiration { get; set; }
public string Emitter { get; set; }
public string ValidIn { get; set; }
}
Passo 3: Configurar o serviço de authenticação para usar o JWT:
var appSettingnsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingnsSection);
AppSettings appSettings = appSettingnsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = true;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = appSettings.ValidIn,
ValidIssuer = appSettings.Emitter
};
});
Passo 4: Modificar o AuthControler injetando o appSettings no seu construtor:
private readonly SignInManager<User> signInManager;
private readonly UserManager<User> userManager;
private readonly IOptions<AppSettings> appSettings;
public AuthController(SignInManager<User> signInManager, UserManager<User> userManager, IOptions<AppSettings> appSettings)
{
this.signInManager = signInManager;
this.userManager = userManager;
this.appSettings = appSettings;
}
Passo 5: Criar metodo que gera o token no AuthController:
private string GetToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(appSettings.Value.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Issuer = appSettings.Value.Emitter,
Audience = appSettings.Value.ValidIn,
Expires = DateTime.UtcNow.AddHours(appSettings.Value.Expiration),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Email.ToString())
})
};
return tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor));
}
Passo 6: Modificar o metodo Register do AuthController para funcionar com o JWT:
[HttpPost("register")]
public async Task<ActionResult> Register(RegisterUserViewModel registerUser)
{
if (!ModelState.IsValid) return BadRequest(ModelState.Values.SelectMany(e => e.Errors));
User user = new()
{
UserName = registerUser.Email,
Email = registerUser.Email,
EmailConfirmed = true
};
var result = await userManager.CreateAsync(user, registerUser.Password);
if (!result.Succeeded) return BadRequest(result.Errors);
return Ok(GetToken(user));
}
Passo 7: Modificar o metodo Login do AuthController para funcionar com o JWT:
[HttpPost("login")]
public async Task<ActionResult> Login(LoginUserViewModel loginUser)
{
if (!ModelState.IsValid) return BadRequest(ModelState.Values.SelectMany(e => e.Errors));
var result = await signInManager.PasswordSignInAsync(loginUser.Email, loginUser.Password, loginUser.RememberMe, lockoutOnFailure: true);
if (!result.Succeeded) return BadRequest("Login or Password invalid.");
User user = new()
{
Email = loginUser.Email
};
return Ok(GetToken(user));
}
Passo 8: Remover o metodo Logout, pois é só remover o token do frontend que o mesmo perde o acesso.