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...

38
demandé sur Jeroen 2014-08-20 16:55:01

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);
47
répondu trailmax 2016-03-29 08:31:56

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.

50
répondu cheny 2018-01-17 11:01:59

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é.

21
répondu mendel 2018-01-17 12:09:28

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.

17
répondu LTMOD 2014-12-30 23:26:57

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.

12
répondu user3812699 2017-12-06 14:17:18
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); 
6
répondu Tebogo Johannes 2015-04-29 17:24:24

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!

1
répondu Josep Alacid 2016-01-13 23:11:00

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.)

1
répondu Grey Wolf 2016-08-19 19:47:04

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);
           };
    }
1
répondu Mathemats 2017-10-26 01:48:18

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.

1
répondu user9296906 2018-01-31 21:55:34

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);
0
répondu Damion 2018-03-07 13:27:39

dans mon cas, je dois juste faire HttpUtility.UrlEncode avant d'envoyer un e-mail. Pas De HttpUtility.UrlDecode pendant la réinitialisation.

0
répondu sailen 2018-03-25 03:18:56

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.

0
répondu Diego Garcia 2018-07-06 16:27:20