Saltar al contenido

Asp.NET Identity 2 muestra el error “Token no válido”

Siéntete libre de divulgar nuestro sitio y códigos con otro, ayúdanos a hacer crecer esta comunidad.

Solución:

Encontré este problema y lo resolví. Hay varias razones posibles.

1. Problemas de codificación de URL (si el problema ocurre “al azar”)

Si esto sucede al azar, es posible que tenga problemas de codificación de URL. Por razones desconocidas, el token no está diseñado para ser seguro para URL, lo que significa que puede contener caracteres no válidos cuando se pasa a través de una URL (por ejemplo, si se envía a través de un correo electrónico).

En este caso, HttpUtility.UrlEncode(token) y HttpUtility.UrlDecode(token) debería ser usado.

Como dijo oão Pereira en sus comentarios, UrlDecode no es (¿oa veces no?) Pruebe ambos, por favor. Gracias.

2. Métodos que no coinciden (tokens de correo electrónico frente a contraseña)

Por ejemplo:

    var code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);

y

    var result = await userManager.ResetPasswordAsync(user.Id, code, newPassword);

El token generado por el proveedor de token de correo electrónico no puede ser confirmado por el proveedor de token de restablecimiento de contraseña.

Pero veremos la causa raíz de por qué sucede esto.

3. Diferentes instancias de proveedores de tokens

Incluso si está utilizando:

var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id);

junto con

var result = await _userManager.ResetPasswordAsync(user.Id, HttpUtility.UrlDecode(token), newPassword);

el error aún podría ocurrir.

Mi antiguo código muestra por qué:

public class AccountController : Controller
{
    private readonly UserManager _userManager = UserManager.CreateUserManager(); 

    [AllowAnonymous]
    [HttpPost]
    public async Task ForgotPassword(FormCollection collection)
    
        var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id);
        var callbackUrl = Url.Action("ResetPassword", "Account", new  area = "", UserId = user.Id, token = HttpUtility.UrlEncode(token) , Request.Url.Scheme);

        Mail.Send(...);
    

y:

public class UserManager : UserManager
{
    private static readonly UserStore UserStore = new UserStore();
    private static readonly UserManager Instance = new UserManager();

    private UserManager()
        : base(UserStore)
    
    

    public static UserManager CreateUserManager()
    
        var dataProtectionProvider = new DpapiDataProtectionProvider();
        Instance.UserTokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create());

        return Instance;
    

Preste atención a que en este código, cada vez que un UserManager se crea (o new-ed), un nuevo dataProtectionProvider también se genera. Entonces, cuando un usuario recibe el correo electrónico y hace clic en el enlace:

public class AccountController : Controller
{
    private readonly UserManager _userManager = UserManager.CreateUserManager();
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task ResetPassword(string userId, string token, FormCollection collection)
    
        var result = await _userManager.ResetPasswordAsync(user.Id, HttpUtility.UrlDecode(token), newPassword);
        if (result != IdentityResult.Success)
            return Content(result.Errors.Aggregate("", (current, error) => current + error + "rn"));
        return RedirectToAction("Login");
    

los AccountController ya no es el viejo, y tampoco lo son los _userManager y su proveedor de tokens. Entonces, el nuevo proveedor de tokens fallará porque no tiene ese token en su memoria.

Por lo tanto, necesitamos usar una única instancia para el proveedor de tokens. Aquí está mi nuevo código y funciona bien:

public class UserManager : UserManager
{
    private static readonly UserStore UserStore = new UserStore();
    private static readonly UserManager Instance = new UserManager();

    private UserManager()
        : base(UserStore)
    
    

    public static UserManager CreateUserManager()
    
        //...
        Instance.UserTokenProvider = TokenProvider.Provider;

        return Instance;
    

y:

public static class TokenProvider

    [UsedImplicitly] private static DataProtectorTokenProvider _tokenProvider;

    public static DataProtectorTokenProvider Provider
    
        get
        

            if (_tokenProvider != null)
                return _tokenProvider;
            var dataProtectionProvider = new DpapiDataProtectionProvider();
            _tokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create());
            return _tokenProvider;
        
    

No se podría llamar una solución elegante, pero llegó a la raíz y resolvió mi problema.

Porque está generando token para restablecer la contraseña aquí:

string code = UserManager.GeneratePasswordResetToken(user.Id);

Pero en realidad tratando de validar el token para el correo electrónico:

result = await UserManager.ConfirmEmailAsync(id, code);

Estos son 2 tokens diferentes.

En su pregunta, dice que está intentando verificar el correo electrónico, pero su código es para restablecer la contraseña. Cual estas haciendo

Si necesita una confirmación por correo electrónico, genere el token a través de

var emailConfirmationCode = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);

y confírmalo vía

var confirmResult = await UserManager.ConfirmEmailAsync(userId, code);

Si necesita restablecer la contraseña, genere un token como este:

var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);

y confírmalo así:

var resetResult = await userManager.ResetPasswordAsync(user.Id, code, newPassword);

Recibía el error “Token no válido” incluso con un código como este:

var emailCode = UserManager.GenerateEmailConfirmationToken(id);
var result = UserManager.ConfirmEmail(id, emailCode);

En mi caso, el problema resultó ser que Estaba creando el usuario manualmente y agregándolo a la base de datos sin usar el UserManager.Create(...) método. El usuario existía en la base de datos pero sin un sello de seguridad.

Es interesante que el GenerateEmailConfirmationToken devolvió un token sin quejarse de la falta de sello de seguridad, pero ese token nunca pudo ser validado.

Si conservas alguna incertidumbre y forma de beneficiar nuestro post eres capaz de realizar una aclaración y con mucho placer lo estudiaremos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *