'Add Claim On Successful Login

I need to add a claim to the user's identity after a successful user login. This is where I think it needs to happen:

public async Task<ActionResult> Login(LoginViewModel model, string returnUrl, string myClaimValue)
{
   if (!ModelState.IsValid)
   {
      return View(model);
   }

   var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
   switch (result)
   {
      case SignInStatus.Success:
         UserManager.AddClaim(User.Identity.GetUserId(), new Claim("MyClaim", myClaimValue));
         return RedirectToLocal(returnUrl);
      case SignInStatus.LockedOut:
         return View("Lockout");
      case SignInStatus.RequiresVerification:
         return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
      case SignInStatus.Failure:
      default:
         ModelState.AddModelError("", "Invalid login attempt.");
         return View(model);
   }
}

I think this is the right approach, but the call to User.Identity.GetUserId() throws an exception. It looks like User.Identity is not updated by the successful signin. In lieu of this reality, what is the best way for me to get the newly signed in user's id so that I can add a claim?

Or am I doing this all wrong?



Solution 1:[1]

Additional claims must be set before SignInManager.PasswordSignInAsync is triggered. This can be done by a custom ClaimsIdentityFactory:

public class ApplicationClaimsIdentityFactory : ClaimsIdentityFactory<ApplicationUser>
{
    // This claim value is taken from Login View
    public static readonly string MyClaimKey = "app:MyClaimKey";
    public string MyClaimValue { get; set; }

    public async override Task<ClaimsIdentity> CreateAsync(UserManager<ApplicationUser, string> manager, ApplicationUser user, string authenticationType)
    {
        var identity = await base.CreateAsync(manager, user, authenticationType);
        identity.AddClaim(new Claim(MyClaimKey, MyClaimValue));
        return identity;
    }
}

Apply this factory before sign in step:

UserManager.ClaimsIdentityFactory = new ApplicationClaimsIdentityFactory() 
        { 
            MyClaimValue = loginModel.MyClaimValue
        };
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);

Solution 2:[2]

This will store claims to database UserManager.AddClaim(User.Identity.GetUserId(), new Claim("MyClaim", myClaimValue));

If you want to associated claims with the logged-in user when he logs in, You have to overwrite the SignInAsync method of SignInManager

public override async Task SignInAsync(ApplicationUser user, bool isPersistent, bool rememberBrowser) { var userIdentity = await CreateUserIdentityAsync(user); // your code here userIdentity.AddClaim(new Claim(ClaimTypes.Gender, "male")); // AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.TwoFactorCookie); if (rememberBrowser) { var rememberBrowserIdentpublic override async Task SignInAsync(ApplicationUser user, bool isPersistent, bool rememberBrowser)
{
    var userIdentity = await CreateUserIdentityAsync(user);

    // add your claims here
    userIdentity.AddClaim(new Claim(ClaimTypes.Gender, "male"));
    //

    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.TwoFactorCookie);
    if (rememberBrowser)
    {
        var rememberBrowserIdentity = AuthenticationManager.CreateTwoFactorRememberBrowserIdentity(ConvertIdToString(user.Id));
        AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, userIdentity, rememberBrowserIdentity);
    }
    else
    {
        AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, userIdentity);
    }
}

Solution 3:[3]

I did this and it worked:

  1. fetch the user using UserName
  2. check password using SignInManager.CheckPasswordSignInAsync()
  3. add your custom Claims and Login using SignInManager.SignInWithClaimsAsync()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Arsen Khachaturyan
Solution 3 Nader Vaghari