Asp.NET identité 2 donnant une erreur "Token invalide"
j'utilise Asp.Net-Identity-2 et je suis en train de vérifier e-mail, un code de vérification à l'aide de la méthode ci-dessous. Mais je suis un "Invalid Token" message d'erreur.
le Gestionnaire D'utilisateurs de mon Application est comme ceci:
public class AppUserManager : UserManager<AppUser> { public AppUserManager(IUserStore<AppUser> store) : base(store) { } public static AppUserManager Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context) { AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db)); manager.PasswordValidator = new PasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = false, RequireDigit = false, RequireLowercase = true, RequireUppercase = true }; manager.UserValidator = new UserValidator<AppUser>(manager) { AllowOnlyAlphanumericUserNames = true, RequireUniqueEmail = true }; var dataProtectionProvider = options.DataProtectionProvider; //token life span is 3 hours if (dataProtectionProvider != null) { manager.UserTokenProvider = new DataProtectorTokenProvider<AppUser> (dataProtectionProvider.Create("ConfirmationToken")) { TokenLifespan = TimeSpan.FromHours(3) }; } manager.EmailService = new EmailService(); return manager; } //Create } //class } //namespace
mon Action pour générer le token est (et même si je coche le token ici, j'obtiens le message "Invalid token"):
[AllowAnonymous] [HttpPost] [ValidateAntiForgeryToken] public ActionResult ForgotPassword(string email) { if (ModelState.IsValid) { AppUser user = UserManager.FindByEmail(email); if (user == null || !(UserManager.IsEmailConfirmed(user.Id))) { // Returning without warning anything wrong... return View("../Home/Index"); } //if string code = UserManager.GeneratePasswordResetToken(user.Id); string callbackUrl = Url.Action("ResetPassword", "Admin", new { Id = user.Id, code = HttpUtility.UrlEncode(code) }, protocol: Request.Url.Scheme); UserManager.SendEmail(user.Id, "Reset password Link", "Use the following link to reset your password: <a href="" + callbackUrl + "">link</a>"); //This 2 lines I use tho debugger propose. The result is: "Invalid token" (???) IdentityResult result; result = UserManager.ConfirmEmail(user.Id, code); } // If we got this far, something failed, redisplay form return View(); } //ForgotPassword
mon Action pour vérifier le token est (ici, j'obtiens toujours "Token invalide" quand je vérifie le résultat):
[AllowAnonymous] public async Task<ActionResult> ResetPassword(string id, string code) { if (id == null || code == null) { return View("Error", new string[] { "Invalid params to reset password." }); } IdentityResult result; try { result = await UserManager.ConfirmEmailAsync(id, code); } catch (InvalidOperationException ioe) { // ConfirmEmailAsync throws when the id is not found. return View("Error", new string[] { "Error to reset password:<br/><br/><li>" + ioe.Message + "</li>" }); } if (result.Succeeded) { AppUser objUser = await UserManager.FindByIdAsync(id); ResetPasswordModel model = new ResetPasswordModel(); model.Id = objUser.Id; model.Name = objUser.UserName; model.Email = objUser.Email; return View(model); } // If we got this far, something failed. string strErrorMsg = ""; foreach(string strError in result.Errors) { strErrorMsg += "<li>" + strError + "</li>"; } //foreach return View("Error", new string[] { strErrorMsg }); } //ForgotPasswordConfirmation
Je ne sais pas ce qui manque ou ce qui ne va pas...
13 réponses
parce que vous générez un token pour réinitialiser le mot de passe ici:
string code = UserManager.GeneratePasswordResetToken(user.Id);
mais en fait essayer de valider token for email:
result = await UserManager.ConfirmEmailAsync(id, code);
ce sont 2 jetons différents.
dans votre question vous dites que vous essayez de vérifier le courriel, mais votre code est pour réinitialiser le mot de passe. Qui êtes-vous?
si vous avez besoin d'une confirmation par e-mail, puis générer un token via
var emailConfirmationCode = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
et confirmer par
var confirmResult = await UserManager.ConfirmEmailAsync(userId, code);
Si vous besoin de réinitialiser le mot de passe, générer un token comme ceci:
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
et confirmez-le comme ceci:
var resetResult = await userManager.ResetPasswordAsync(user.Id, code, newPassword);
j'ai rencontré ce problème et résolu. Il y a plusieurs raisons à cela.
1. URL problèmes de Codage (si problème survenant de façon "aléatoire")
si cela se produit au hasard, vous pourriez rencontrer des problèmes de codage d'url. Pour des raisons inconnues, le token n'est pas conçu pour des url-safe, ce qui signifie qu'il peut contenir des caractères invalides lorsqu'il est passé par une url (par exemple, s'il est envoyé via un e-mail).
Dans ce cas, HttpUtility.UrlEncode(token)
et HttpUtility.UrlDecode(token)
doit être utilisé.
comme L'a dit Oão Pereira dans ses commentaires, UrlDecode
n'est pas (ou parfois pas?) requis. Essayez les deux s'il vous plaît. Grâce.
2. Méthodes de non-correspondance (email vs mots de passe tokens)
Par exemple:
var code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);
et
var result = await userManager.ResetPasswordAsync(user.Id, code, newPassword);
le token généré par le email-token-provide ne peut pas être confirmé par le reset-password-token-provider.
mais nous verrons la cause profonde de cette arriver.
3. Différentes instances de fournisseurs de token
même si vous utilisez:
var token = await _userManager.GeneratePasswordResetTokenAsync(user.Id);
et
var result = await _userManager.ResetPasswordAsync(user.Id, HttpUtility.UrlDecode(token), newPassword);
l'erreur pouvait encore arriver.
Mon ancien code montre pourquoi:
public class AccountController : Controller
{
private readonly UserManager _userManager = UserManager.CreateUserManager();
[AllowAnonymous]
[HttpPost]
public async Task<ActionResult> 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(...);
}
et:
public class UserManager : UserManager<IdentityUser>
{
private static readonly UserStore<IdentityUser> UserStore = new UserStore<IdentityUser>();
private static readonly UserManager Instance = new UserManager();
private UserManager()
: base(UserStore)
{
}
public static UserManager CreateUserManager()
{
var dataProtectionProvider = new DpapiDataProtectionProvider();
Instance.UserTokenProvider = new DataProtectorTokenProvider<IdentityUser>(dataProtectionProvider.Create());
return Instance;
}
faites attention que dans ce code, à chaque fois qu'un UserManager
est créé (ou new
-ed), un nouveau dataProtectionProvider
est généré. Ainsi, lorsqu'un utilisateur reçoit un e-mail et clique sur le lien:
public class AccountController : Controller
{
private readonly UserManager _userManager = UserManager.CreateUserManager();
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> 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 + "\r\n"));
return RedirectToAction("Login");
}
AccountController
n'est plus l'ancienne, ni la _userManager
et son fournisseur de jeton. Donc le nouveau fournisseur de token va échouer parce qu'il n'a pas ce token dans sa mémoire.
Donc nous avons besoin d'utiliser une seule instance pour le fournisseur de jeton. Voici mon nouveau code et il fonctionne très bien:
public class UserManager : UserManager<IdentityUser>
{
private static readonly UserStore<IdentityUser> UserStore = new UserStore<IdentityUser>();
private static readonly UserManager Instance = new UserManager();
private UserManager()
: base(UserStore)
{
}
public static UserManager CreateUserManager()
{
//...
Instance.UserTokenProvider = TokenProvider.Provider;
return Instance;
}
et:
public static class TokenProvider
{
[UsedImplicitly] private static DataProtectorTokenProvider<IdentityUser> _tokenProvider;
public static DataProtectorTokenProvider<IdentityUser> Provider
{
get
{
if (_tokenProvider != null)
return _tokenProvider;
var dataProtectionProvider = new DpapiDataProtectionProvider();
_tokenProvider = new DataProtectorTokenProvider<IdentityUser>(dataProtectionProvider.Create());
return _tokenProvider;
}
}
}
Il ne pouvait pas être appelé une solution élégante, mais il a frappé la racine et résolu mon problème.
j'ai eu l'erreur "Invalid Token" même avec un code comme celui-ci:
var emailCode = UserManager.GenerateEmailConfirmationToken(id);
var result = UserManager.ConfirmEmail(id, emailCode);
Dans mon cas, le problème s'est avéré être que j'ai créé l'utilisateur manuellement et l'ai ajouté à la base de données sans utiliser le UserManager.Create(...)
méthode. L'utilisateur existait dans la base de données mais sans tampon de sécurité.
il est intéressant que le GenerateEmailConfirmationToken
renvoie un jeton sans se plaindre de l'absence de tampon de sécurité, mais ce jeton ne pourra jamais être validé.
en dehors de cela, j'ai vu le code lui-même échouer s'il n'est pas encodé.
j'ai récemment commencé à encoder le mien de la façon suivante:
string code = manager.GeneratePasswordResetToken(user.Id);
code = HttpUtility.UrlEncode(code);
Et puis, quand je suis prêt à le lire:
string code = IdentityHelper.GetCodeFromRequest(Request);
code = HttpUtility.UrlDecode(code);
pour être honnête, je suis surpris qu'il ne soit pas correctement encodé en premier lieu.
dans mon cas, notre application AngularJS a converti tous les signes plus ( + ) en espaces vides ( "" ) de sorte que le jeton était en effet invalide quand il a été renvoyé.
pour résoudre le problème, dans notre méthode ResetPassword dans le AccountController, j'ai simplement ajouté un remplacement avant de mettre à jour le mot de passe:
code = code.Replace(" ", "+");
IdentityResult result = await AppUserManager.ResetPasswordAsync(user.Id, code, newPassword);
j'espère que cela aidera n'importe qui d'autre travaillant avec L'identité dans une API Web et AngularJS.
string code = _userManager.GeneratePasswordResetToken(user.Id);
code = HttpUtility.UrlEncode(code);
//envoi de repos email
ne décodez pas le code
var result = await _userManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
peut-être que c'est un vieux fil mais, juste pour le cas, je me gratte la tête avec l'occurrence aléatoire de cette erreur. J'ai vérifié tous les threads et vérifié chaque suggestion mais-au hasard semblait-certains des codes où retourné comme "token invalide". Après quelques recherches dans la base de données des utilisateurs, j'ai finalement découvert que ces erreurs de "token invalide" étaient directement liées à des espaces ou à d'autres caractères non alphanumériques dans les noms d'utilisateurs. La Solution était facile à trouver alors. Juste configurer le UserManager pour autoriser ces caractères dans les noms d'utilisateur. Cela peut être fait juste après que le gestionnaire d'utilisateurs crée l'événement, en ajoutant un nouveau paramètre UserValidator à false la propriété correspondante de cette façon:
public static UserManager<User> Create(IdentityFactoryOptions<UserManager<User>> options, IOwinContext context)
{
var userManager = new UserManager<User>(new UserStore());
// this is the key
userManager.UserValidator = new UserValidator<User>(userManager) { AllowOnlyAlphanumericUserNames = false };
// other settings here
userManager.UserLockoutEnabledByDefault = true;
userManager.MaxFailedAccessAttemptsBeforeLockout = 5;
userManager.DefaultAccountLockoutTimeSpan = TimeSpan.FromDays(1);
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
userManager.UserTokenProvider = new DataProtectorTokenProvider<User>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromDays(5)
};
}
return userManager;
}
J'espère que cela pourrait aider les "arrivées tardives" comme moi!
assurez-vous que lorsque générer, vous utilisez:
GeneratePasswordResetTokenAsync(user.Id)
Et confirmez que vous utilisez:
ResetPasswordAsync(user.Id, model.Code, model.Password)
Si vous assurez-vous d'utiliser des méthodes d'appariement, mais cela ne fonctionne toujours pas, veuillez vérifier que user.Id
est la même dans les deux méthodes. (Parfois votre logique peut ne pas être correcte parce que vous autorisez l'utilisation du même email pour le registre, etc.)
assurez - vous que le jeton que vous générez n'expire pas rapidement-je l'avais changé à 10 secondes pour tester et il retournerait toujours l'erreur.
if (dataProtectionProvider != null) {
manager.UserTokenProvider =
new DataProtectorTokenProvider<AppUser>
(dataProtectionProvider.Create("ConfirmationToken")) {
TokenLifespan = TimeSpan.FromHours(3)
//TokenLifespan = TimeSpan.FromSeconds(10);
};
}
nous avons rencontré cette situation avec un ensemble d'utilisateurs où tout fonctionnait bien. Nous l'avons isolé jusqu'au système de protection de courriel de Symantec qui remplace les liens dans nos courriels aux utilisateurs par des liens sûrs qui vont sur leur site pour validation et redirige ensuite l'utilisateur vers le lien original que nous avons envoyé.
le problème est qu'ils introduisent un décodage... ils semblent faire un encodage D'URL sur le lien généré pour intégrer notre lien comme un paramètre de requête à leur site, mais alors lorsque l'utilisateur clique et clicksafe.symantec.com décode l'url il décode la première partie qu'ils avaient besoin de coder mais aussi le contenu de notre chaîne de requête et puis L'URL que le navigateur obtient redirigé vers a été décodé et nous sommes de retour dans l'état où les caractères spéciaux gâcher la manipulation de la chaîne de requête dans le code derrière.
voici ce que j'ai fait: décoder le Token après l'avoir encodé pour URL (en bref)
J'ai D'abord dû Encoder L'utilisateur GenerateEmailConfirmationToken qui a été généré. (Standard conseils ci-dessus)
var token = await userManager.GenerateEmailConfirmationTokenAsync(user);
var encodedToken = HttpUtility.UrlEncode(token);
et dans L'Action" Confirm " de votre contrôleur, j'ai dû décoder le Token avant de le valider.
var decodedCode = HttpUtility.UrlDecode(mViewModel.Token);
var result = await userManager.ConfirmEmailAsync(user,decodedCode);
dans mon cas, je dois juste faire HttpUtility.UrlEncode avant d'envoyer un e-mail. Pas De HttpUtility.UrlDecode pendant la réinitialisation.
ici, j'ai le même problème mais après un certain temps j'ai fondé que dans mon cas l'erreur de token invalide a été soulevée par le fait que ma classe de Compte personnalisé a la propriété Id non déclarée et a dépassé.
Comme ça:
public class Account : IdentityUser
{
[ScaffoldColumn(false)]
public override string Id { get; set; }
//Other properties ....
}
alors pour le corriger, j'ai juste enlevé cette propriété et généré à nouveau le schéma de la base de données juste pour être sûr.
supprimer ceci résout le problème.