Oleg Sh
Oleg Sh

Reputation: 9013

When claims are available

I add a claim in GenerateUserIdentityAsync method:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);

            userIdentity.AddClaim(new Claim(ClaimsStaticStrings.Inactivity, company.Inactivity.ToString()));

        return userIdentity;
    }
}

Then I try to get it in Account/Login method:

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        var result = await SignInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                int inactivity = Utils.GetInactivityFromIdentity(User.Identity);
                Response.Cookies.Add(new HttpCookie("inactivity", inactivity.ToString()));

                return RedirectToAction("Index", "Home");
        }
    }


    public static int GetInactivityFromIdentity(IIdentity identity)
    {
        System.Security.Claims.ClaimsIdentity claims = (System.Security.Claims.ClaimsIdentity)identity;

        var claim = claims.FindFirst(Models.ClaimsStaticStrings.Inactivity);

        if (claim != null)
        {
            return int.Parse(claim.Value);
        }
        else
            throw new Exception("Inactivity is not set");

    }

it throws exception "Inactivity is not set". variable 'claims' has only one claim - name

But when I call GetInactivityFromIdentity method from any other page (after redirect) - it works fine (and claims are filled with all set claims). Why so?

Upvotes: 0

Views: 1033

Answers (2)

Gelu Vac
Gelu Vac

Reputation: 1

The reason for not being able to access your SignedIn User's claims is the events order designed in the SignIn process - .Net issue. When you perform the call SignInManager.SignIn(), the events order takes you through your project's extended class called ApplicationSignInManager which derives from SingInManager - and inside it you will find an override method called CreateUserIdentityAsync which gets triggered by the SignIn call. Inside this method, the User Identity Claims get created and returned (by calling GenerateUserIdentityAsync), only they become accessible much later in the SignIn process. The work around here is to create a public property called AppUserClaims of type ClaimsIdentity inside the ApplicationSignInManager class and use it to store the User Identity Claims values before retreiving them to the SignIn flow.

public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
  var iClaims = user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
  AppUserClaims = iClaims.Result; 
  return iClaims; 
}

As soon as you store the claims to this property, going back inside the Login method, you can access it like below - remember the Claims values are always of type string:

string YourX = SignInManager.AppUserClaims.FindFirst("YourX").Value;

Upvotes: 0

trailmax
trailmax

Reputation: 35106

Claims are serialised into auth-cookie. Cookie is not set until yo go through page reload on authentication. At the point where you try to access the claims from the cookie, there is no cookie in HTTP Request - SignInManager will be setting the cookie only when the request is complete, but not immediately after. You indeed need a redirect/page reload cycle to get the cookie and claim available.

You'll have to somehow get inactivity value not through the claim, but from your data storage when you sign-in users.

Upvotes: 1

Related Questions