Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anti-forgery token issues

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?

like image 490
r3plica Avatar asked Nov 14 '13 12:11

r3plica


People also ask

What is an anti-forgery token?

AntiForgeryToken is a security token generated by the . Net Core web application, which is used to validate a post request to guard against Cross-Site Request.

How do you validate anti-forgery tokens?

The feature doesn't prevent any other type of data forgery or tampering based attacks. To use it, decorate the action method or controller with the ValidateAntiForgeryToken attribute and place a call to @Html. AntiForgeryToken() in the forms posting to the method.

What is anti-forgery cookie?

Cross-site request forgery (also known as XSRF or CSRF) is an common attack against web apps that store authentication tokens in the cookies. Browser will automatically attach these authentication cookies with every request to the website.

Where are anti-forgery tokens stored?

The token is stored as a cookie that's sent with every request the client makes. Generating and validating this cookie is performed by the Cookie Authentication Middleware.


7 Answers

Try setting (in global.cs):

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
like image 174
Alex Filipovici Avatar answered Oct 19 '22 08:10

Alex Filipovici


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
like image 42
Mike Goodwin Avatar answered Oct 19 '22 09:10

Mike Goodwin


Just put this in global.asax.cs

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
like image 35
mzk Avatar answered Oct 19 '22 08:10

mzk


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

like image 33
Gurgen Sargsyan Avatar answered Oct 19 '22 09:10

Gurgen Sargsyan


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.

like image 41
cederlof Avatar answered Oct 19 '22 08:10

cederlof


In Global.asax.cs,

1.Add these namespaces

using System.Web.Helpers;
using System.Security.Claims;

2.Add this line in method Application_Start:

 protected void Application_Start()
 {
       .......
       AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
 } 
like image 35
KiranC Avatar answered Oct 19 '22 08:10

KiranC


AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

Works for my case I am using ADFS Authentication.

like image 39
Ashu Avatar answered Oct 19 '22 08:10

Ashu