Anti-forgery token issues

79,253

Solution 1

Try setting (in global.cs):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

Solution 2

Do you know what claims you do get in your ClaimsIdentity? If not:

  1. Remove the [ValidateAntiForgeryToken] attribute
  2. Put a breakpoint somewhere in your controller and break at it
  3. Then look at the current ClaimsIdentity and examine the claims
  4. Find one that you think will uniquely identify your user
  5. Set the AntiForgeryConfig.UniqueClaimTypeIdentifier to that claim type
  6. Put back the [ValidateAntiForgeryToken] attribute

Solution 3

Just put this in global.asax.cs

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;

Solution 4

Try open link in incognito window or clear cookie from that domain(i.e. localhost).

Solution 5

Edit: Having a greater understanding of this problem at this moment, you can disregard my answer below.

Setting AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier; in Application_Start() of Global.asax.cs fixed it for me. Even though I have the claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier set, I get the same error as in the original question. But pointing it out as above somehow works.



Starting with MVC4 the anti-forgery-token doesn't use User.Identity.Name as the unique identifier. Instead it looks for the two claims given in the error message.

Update NOTE: This should not be needed You can add the missing claims to your ClaimsIdentity when the user is being logged in, like so:

string userId = TODO;
var identity = System.Web.HttpContext.Current.User.Identity as ClaimsIdentity;
identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", userId));
identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", userId));

Note that one of the claims might already be there from before, and you will get an error with duplicate claims if you add both. If so, just add the one missing.

Share:
79,253
r3plica
Author by

r3plica

SOreadytohelp

Updated on August 26, 2021

Comments

  • r3plica
    r3plica over 2 years

    I am having an issue with the anti-forgery token :( I have created my own User class which worked fine but now I am getting an error whenever I go to the /Account/Register page. The error is:

    A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.

    I found this article:

    http://stack247.wordpress.com/2013/02/22/antiforgerytoken-a-claim-of-type-nameidentifier-or-identityprovider-was-not-present-on-provided-claimsidentity/

    so I changed my Application_Start method to this:

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
    
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    
        AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;
    }
    

    but when I do that, I get this error:

    A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress' was not present on the provided ClaimsIdentity.

    Has anyone come across this before? If so, do you know how to solve it?


    Here is my custom user class:

    public class Profile : User, IProfile
    {
        public Profile()
            : base()
        {
            this.LastLoginDate = DateTime.UtcNow;
            this.DateCreated = DateTime.UtcNow;
        }
    
        public Profile(string userName)
            : base(userName)
        {
            this.CreatedBy = this.Id;
    
            this.LastLoginDate = DateTime.UtcNow;
            this.DateCreated = DateTime.UtcNow;
    
            this.IsApproved = true;
        }
        
        [NotMapped]
        public HttpPostedFileBase File { get; set; }
    
        [Required]
        public string CompanyId { get; set; }
    
        [Required]
        public string CreatedBy { get; set; }
        public string ModifiedBy { get; set; }
    
        public DateTime DateCreated { get; set; }
        public DateTime? DateModified { get; set; }
        public DateTime LastLoginDate { get; set; }
    
        [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredTitle")]
        public string Title { get; set; }
        [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredFirstName")]
        public string Forename { get; set; }
        [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredLastName")]
        public string Surname { get; set; }
    
        [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredEmail")]
        public string Email { get; set; }
        public string JobTitle { get; set; }
        public string Telephone { get; set; }
        public string Mobile { get; set; }
        public string Photo { get; set; }
        public string LinkedIn { get; set; }
        public string Twitter { get; set; }
        public string Facebook { get; set; }
        public string Google { get; set; }
        public string Bio { get; set; }
    
        public string CompanyName { get; set; }
    
        [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredCredentialId")]
        public string CredentialId { get; set; }
        [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredSecurityCode")]
        public bool IsLockedOut { get; set; }
        public bool IsApproved { get; set; }
    
        [Display(Name = "Can only edit own assets")]
        public bool CanEditOwn { get; set; }
        [Display(Name = "Can edit assets")]
        public bool CanEdit { get; set; }
        [Display(Name = "Can download assets")]
        public bool CanDownload { get; set; }
        [Display(Name = "Require approval to upload assets")]
        public bool RequiresApproval { get; set; }
        [Display(Name = "Can approve assets")]
        public bool CanApprove { get; set; }
        [Display(Name = "Can synchronise assets")]
        public bool CanSync { get; set; }
    
        public bool AgreedTerms { get; set; }
        public bool Deleted { get; set; }
    }
    
    public class ProfileContext : IdentityStoreContext
    {
        public ProfileContext(DbContext db)
            : base(db)
        {
            this.Users = new UserStore<Profile>(this.DbContext);
        }
    }
    
    public class ProfileDbContext : IdentityDbContext<Profile, UserClaim, UserSecret, UserLogin, Role, UserRole>
    {
    }
    

    I profile is just simple for my repositories, looks like this:

    public interface IProfile
    {
        string Id { get; set; }
        string CompanyId { get; set; }
        
        string UserName { get; set; }
        string Email { get; set; }
    
        string CredentialId { get; set; }
    }
    

    and the User class is the Microsoft.AspNet.Identity.EntityFramework.User class. My AccountController looks like this:

    [Authorize]
    public class AccountController : Controller
    {
        public IdentityStoreManager IdentityStore { get; private set; }
        public IdentityAuthenticationManager AuthenticationManager { get; private set; }
        
        public AccountController() 
        {
            this.IdentityStore = new IdentityStoreManager(new ProfileContext(new ProfileDbContext()));
            this.AuthenticationManager = new IdentityAuthenticationManager(this.IdentityStore);
        }
    
        //
        // GET: /Account/Register
        [AllowAnonymous]
        public ActionResult Register()
        {
            return View();
        }
    
        //
        // POST: /Account/Register
        [HttpPost]
        [AllowAnonymous]
        public async Task<ActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    // Create a profile, password, and link the local login before signing in the user
                    var companyId = Guid.NewGuid().ToString();
                    var user = new Profile(model.UserName)
                    {
                        CompanyId = companyId,
                        Title = model.Title,
                        Forename = model.Forename,
                        Surname = model.Surname,
                        Email = model.Email,
                        CompanyName = model.CompanyName,
                        CredentialId = model.CredentialId
                    };
    
                    if (await IdentityStore.CreateLocalUser(user, model.Password))
                    {
                        //Create our company
                        var company = new Skipstone.Web.Models.Company()
                        {
                            Id = companyId,
                            CreatedBy = user.Id,
                            ModifiedBy = user.Id,
                            Name = model.CompanyName
                        };
    
                        using (var service = new CompanyService())
                        {
                            service.Save(company);
                        }
    
                        await AuthenticationManager.SignIn(HttpContext, user.Id, isPersistent: false);
                        return RedirectToAction("Setup", new { id = companyId });
                    }
                    else
                    {
                        ModelState.AddModelError("", "Failed to register user name: " + model.UserName);
                    }
                }
                catch (IdentityException e)
                {
                    ModelState.AddModelError("", e.Message);
                }
            }
    
            // If we got this far, something failed, redisplay form
            return View(model);
        }
    
        //
        // POST: /Account/Setup
        public ActionResult Setup(string id)
        {
            var userId = User.Identity.GetUserId();
            using (var service = new CompanyService())
            {
                var company = service.Get(id);
                var profile = new Profile()
                {
                    Id = userId,
                    CompanyId = id
                };
    
                service.Setup(profile);
    
                return View(company);
            }
        }
    }
    

    It used to be decorated with the [ValidateAntiForgeryToken] attribute, but that is where it stopped working.

    Why?

  • Matt DeKrey
    Matt DeKrey almost 10 years
    I think it's important to note why this works: This tells the AntiForgery class to use the NameIdentifier (which is the user id string found by GetUserId). Thanks to Mike Goodwin's answer in helping me learn this!
  • Dhanuka777
    Dhanuka777 almost 9 years
    I tried "AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;" and got this error "Sequence contains more than one matching element", in my case there are several claims (name, role and emailaddress). How can I sort this out?
  • Mike Taverne
    Mike Taverne almost 9 years
    I set this in Global.asax.cs
  • AaronLS
    AaronLS about 8 years
    I understand why you are using userId as the "/nameidentifier", but why are you putting in the userId as the "/identityprovider"?
  • guysherman
    guysherman over 7 years
    This is also the solution if you're using OpenId (ie Azure ActiveDirectory) as your authenticaiton.
  • Mohsen Kamrani
    Mohsen Kamrani over 7 years
    Why does this work and what is the cause of the problem?
  • rawel
    rawel about 7 years
    This works because when you have a session cookie with invalid nameidentifier, the server tries to use the invalid identifier without redirecting the user to the log-in page and get the proper nameidentifier.
  • Mark Rowe
    Mark Rowe about 7 years
    Full namespaces.. I had to do some digging to find out where ClaimTypes was held. System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifi‌​er = System.Security.Claims.ClaimTypes.NameIdentifier;
  • NitinSingh
    NitinSingh almost 6 years
    More than providing the direct spoon feed answer, this one tells the background and enables self discovery. :) Thanks a lot
  • NitinSingh
    NitinSingh almost 6 years
    How does it adds any more value than the ones answered above
  • Keisha W
    Keisha W over 5 years
    Thanks for adding the usings. @NitinSingh I think that adds more value because I didn't know which of three potential namespaces in my project to use.
  • NitinSingh
    NitinSingh over 5 years
    Whenever you add a new functionality, it will ask for correct references. Once it compiles, you should remove unused ones via Refactor menu on right click
  • Artorias2718
    Artorias2718 over 5 years
    Thanks. What I don't get is why I had to make this change, I fixed a few different issues I was having with my code last night and everything worked just fine. Without changing anything, I tested this on another machine and it all worked until a few minutes ago.
  • niz_sh
    niz_sh almost 5 years
    This is in System.Web.WebPages.dll, v2.0.0.0, so don't forgot include this reference to project
  • Michel
    Michel over 4 years
    this really helped me. It turned out I got a claim fro another application running on my localhost, in my application where there are no claims used (which is why the claims thing sounded weird to me). So when I logged out of the other application, the claims were gone and so was the error. On the live-test- environment these sites are more separated. So I think I need the above mentioned solution, but only for local development.
  • Marc Levesque
    Marc Levesque over 3 years
    I find this answer much more helpful. In my case, I'm converting an existing application to use IdentityServer, and this error started showing up. Using Mike's method I was able to check the claims and use an appropriate claim, such as the subject id (sub claim).