Let me give you some context: I know it's a fairly specific question which probably can be answered by googling. Which I've done and followed some guide but I feel like there is something I am doing wrong or maybe I am doing a weird combination of functionality that is in conflict.
You see right now I've set up the options of tokes with this setup:
public static void AddIdentityConfig(this IServiceCollection services)
{
services.AddIdentity<Usuario, IdentityRole>(options =>
{
options.Password.RequiredLength = 6;
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.SignIn.RequireConfirmedEmail = true;
}).AddEntityFrameworkStores<AppDbContext>()
.AddTokenProvider<DataProtectorTokenProvider<Usuario>>(TokenOptions.DefaultProvider);
}
As you can see, it seems to be a fairly simplistic setup.
How I am handling the creation of said validation token and then the reading of the token is as follows:
This creates the token:
public async Task<string> CreateVerificationTokenIdentity(Usuario usuario)
{
return await _userManager.GenerateEmailConfirmationTokenAsync(usuario);
}
And this verifies it:
public async Task<bool> ConfirmEmailAsync(Usuario usuario, string token)
{
var result = await _userManager.ConfirmEmailAsync(usuario, token);
return result.Succeeded;
}
I've done some testing within the same endpoint like so:
public async Task<IActionResult> TestByRegister([FromBody] RegisterRequestDto registerRequest)
{
var register = await _authServices.RegisterUserByEmailAndUsernameAsync(registerRequest);
if (register == null)
{
return BadRequest();
}
var generateToken = await _tokenServices.CreateVerificationTokenIdentity(register);
var confirmEmail = await _authServices.ConfirmEmail(generateToken, register.Id);
if (!confirmEmail)
{
return BadRequest();
}
return Ok();
}
And the verification does work!
It is only when the endpoints are separated and the verification is done through 2 request that it does fail.
This makes me believe that somehow the token generated isn't being stored somewhere its supposed to be.
These are my two endpoints that will handle both those methods:
verification endpoint
public async Task<IActionResult> VerificateEmail([FromBody] VerificateEmailRequestDto dto)
{
var confirmedEmail = await _authServices.ConfirmEmail(dto.Token, dto.UserId);
if (!confirmedEmail)
{
// Could add an extra layer of protection and erase the Token
return BadRequest("Something went wrong while trying to verify your email.");
}
// Could add a redirect to the Password Setup
return Ok();
}
And the register method:
public async Task<IActionResult> Register([FromBody] RegisterRequestDto registerRequest)
{
var errors = await _authServices.CheckDataUniquenessAsync(registerRequest.Email, registerRequest.Username);
if (errors.Count != 0)
return BadRequest(new { errors });
var newUsuario = await _authServices.RegisterUserByEmailAndUsernameAsync(registerRequest);
if (newUsuario == null)
{
return BadRequest("Something went wrong while creating the user.");
}
var token = await _tokenServices.CreateVerificationTokenIdentity(newUsuario);
var newEmail = _emailServices.CreateEmail(newUsuario, token);
_emailServices.SendEmail(newEmail!);
return Created("/api/auth/register", new { message = "Usuario registrado exitosamente!" });
}
The first thing I thought was: maybe I am sending the incorrect data like its reaching differently. But I've checked. The token as well as the Id is correctly reaching the endpoint. But the .ConfirmEmailAsync() just breaks. Just sends back false regardless if its correct or not.
As you can see I am fairly lost when it comes to handling user email verification with ASP.NET Core Identity.
If anyone has any advice, resource or even comment into how to implement email verification I would highly appreciate it!
Thank you for your time!