Claims Identity MVC

Asked

Viewed 2,552 times

4

I’m having difficulty implementing Claims to make user authorizations in my project, I’ve read a lot but I can’t execute it. I’m using in the project the Nhibernate.AspNet.Identity may be that for this reason I can not run as in the forums and tutorials read, but I’m not sure.

Example of how I tried to create:

public async Task Login(LoginViewModel model, string returnUrl)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }
            ApplicationUser signedUser = _userManager.FindByEmail(model.Email);
            var result = await _signInManager.PasswordSignInAsync(signedUser.UserName, model.Password, model.RememberMe, shouldLockout: false);

            var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.Email), }, DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);
            identity.AddClaim(new Claim(ClaimTypes.Role, "Adm"));
            identity.AddClaim(new Claim(ClaimTypes.GivenName, "Teste"));
            identity.AddClaim(new Claim(ClaimTypes.Sid, signedUser.userID));

            switch (result)
            {
                case SignInStatus.Success:
                    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("", "Login ou Senha incorretos.");
                    return View(model);
            }

        }

Example of how I’m trying to verify: inserir a descrição da imagem aqui

  1. I created a table to save the Claims and Userclaims, now I would like to know how to log in and load these Claims without the need to search in bank?
  2. How to validate Claims in my Methods?
  3. Is there any more effective way to authenticate the user without using the Claims or Roles (which by what I researched is already outdated)?

Thank you for your attention!

2 answers

5


Claims need to be added before you can Signin user, and should be added to the object ClaimsIdentity that is created to effect that Signin.

You must have a class you inherit from IdentityUser and is used with Identity classes, probably something like ApplicationUser, containing the following method:

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

    // aqui você adiciona tuas claims
    userIdentity.AddClaim(new Claim(ClaimTypes.Role, "Adm"));
    userIdentity.AddClaim(new Claim(ClaimTypes.GivenName, "Teste"));
    userIdentity.AddClaim(new Claim(ClaimTypes.Sid, this.userID));

    return userIdentity;
}

This is the ideal place for you to add your Claims.


Explanation

I honestly find these tutorials out there complicated and incomplete, I will try to detail without complicating so much (I hope). Identity works with a number of classes, such as:

  1. Signinmanager
  2. Usermanager
  3. Userstore
  4. Identityuser
  5. Identityrole
  6. etc....

If you haven’t changed much of the default template, there in the folder App_start must have a file IdentityConfig.cs where you will find a class ApplicationSignInManager (who inherits from SignInManager<ApplicationUser, string>). This is the class that is used to perform the Signin users, and has different ways of login: PasswordSignInAsync() (which is the one you use in your example), SignInAsync(), ExternalSignInAsync(), TwoFactorSignInAsync().

Anyway, all of them at some point will need to create the identity of the user, which is an object of the type ClaimsIdentity. For this, during the process of Signin (regardless of which method has been used), the method will be called CreateUserIdentityAsync of this class. You should notice that in this class ApplicationSignInManager override this method, with the following implementation:

public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
    return user.GenerateUserIdentityAsync((UserManager)UserManager);
}

That is, he calls this method GenerateUserIdentityAsync of your user, which is some class that inherits from IdentityUser. This method must create and return an object of the type ClaimsIdentity which will be used in the Signin.

Therefore, this method is the ideal location for creating your custom Claims, in my view. Also, you should notice that in the file Startup.Auth.cs in that same folder, you have the following code:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider
    {
        // Enables the application to validate the security stamp when the user logs in.
        // This is a security feature which is used when you change a password or add an external login to your account.  
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<UserManager, ApplicationUser>(
            validateInterval: TimeSpan.FromMinutes(30),
            regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    }
});

Basically this setting causes Identity every "x" minute (in this case 30) to revalidate the identity of its user by executing the method contained in the expression lambda, in that case the same GenerateUserIdentityAsync of your user. By creating your Claims within this method, you guarantee that they will remain even after this period when Identity revalidates the user.

  • He explained very well.. To be able to follow his explanation well with my project. As he spoke here this as Applicationuser the class with the method Generateuseridentityasync(). Now when I look for the Claims as well as the image I passed before whose nothing carried, now returns like this: Imagery Would you also tell me how I remove a Claim from that list?

  • @Eluanderj.F.Lopes in this case, normally you assemble your Aims within the method I indicated, and should no longer add or remove, but there are situations that this is inevitable. Can you share in what situation you intend to remove a Claim from the user? Ideally you would do this type of checking there in that same method, and then decide whether you will add a particular Target or not.

  • Like if I remove the access of a particular user from the database. But I managed to get around the situation.. Thanks for the help!

  • @Eluanderj.F.Lopes understood, but good that you managed to get around. I usually create a filter to be executed at each request, where I check if a user must be "revalidated", if so, I do the user’s Signout. For that, I create a Dictionary<string, bool> where the key is the user id and the value is a bool whether or not to revalidate. In the methods that change user roles, or in the method to remove access, I change this value to true for that user. If he is logged in, at his next request, the filter will perform Signout, that’s how I do.

  • @Eluanderj.F.Lopes this dictionary is static and I usually leave it in some Helper class, being static I guarantee that it will be available between a request and another.

2

Responding in parts.

1 - At some point you will have to search your database for Claims to add it to the context of the logged-in user. Partially you are doing this, missing some things I will demonstrate:

In this session you are searching the user, validating user and password and creating some Claims.

    ApplicationUser signedUser = _userManager.FindByEmail(model.Email);
    var result = await _signInManager.PasswordSignInAsync(signedUser.UserName, model.Password, model.RememberMe, shouldLockout: false);

    var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.Email), }, DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);
    identity.AddClaim(new Claim(ClaimTypes.Role, "Adm"));
    identity.AddClaim(new Claim(ClaimTypes.GivenName, "Teste"));
    identity.AddClaim(new Claim(ClaimTypes.Sid, signedUser.userID));

All right so far, but you need to add Claims to the user context by doing this:

    public IAuthenticationManager AuthenticationManager
    {
        get { return HttpContext.Current.GetOwinContext.Authentication; } 
    }

    public async Task Login(LoginViewModel model, string returnUrl) {
         //... seu código de busca e validação de usuário

         ApplicationUser signedUser = _userManager.FindByEmail(model.Email);
         var result = await _signInManager.PasswordSignInAsync(signedUser.UserName, model.Password, model.RememberMe, shouldLockout: false);

         var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.Email), }, DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);
         identity.AddClaim(new Claim(ClaimTypes.Role, "Adm"));
         identity.AddClaim(new Claim(ClaimTypes.GivenName, "Teste"));
         identity.AddClaim(new Claim(ClaimTypes.Sid, signedUser.userID));

         AuthenticationManager.SignIn(New AuthenticationProperties {
            AllowRefresh = true,
            IsPersistent = true,
            ExpiresUtc = DateTime.UtcNow.AddDays(1)
         });
    }

To fetch the value in a Claim, use:

    public string BuscaValorClaim(string strNomeClaim) {
     ClaimsPrincipal listaClaim = ((ClaimsPrincipal), Threading.Thread.CurrentPrincipal)
     return listaClaim.FindFirst((x) => x.Type == strNomeClaim).Value.ToString()
    }
  • Have you tested this code? Looking at it the way it is, I do not believe that will keep the Claims in the next request, I believe that something is missing there...I could even make it work, but still is being made a second unnecessary authentication.

  • Wesley, when you said you were going to answer for parts, I waited for a second item, but only found the one indicated by 1 -

  • You can send the class code of your _signInManager ?

Browser other questions tagged

You are not signed in. Login or sign up in order to post.