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.