Search code examples
c#authenticationasp.net-web-apiasp.net-identityasp.net-identity-2

ASP Identity 2 + Web API Token Auth - Persistent claims not Loading


Im having some trouble with claims in ASP.NET Web API Token Auth.

Essentially I have created a user with some claims (values are being stored in the AspNetUserClaim table) but when the users identity is created, those claims are not being pulled from the database.

A breakdown of my setup is as follows.

  1. User Class: Has a GenerateUserIdentityAsync method (pretty standard) and a couple of custom properties:

    public class LibraryUser : IdentityUser{
        //Add Custom Properties Here
        public string Company { get; set; }
    
        public string DisplayName { get; set; }
    
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<LibraryUser> manager, string authenticationType)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
    
            // Add custom user claims here
            return userIdentity;
        }
    }
    
  2. My DBContext declares some simple name changes to make the DB look nicer

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    
        // Modify the Model creation properties..
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    
        //Rename Identity 2.0 Tables to something nicer..
        modelBuilder.Entity<LibraryUser>().ToTable("LibraryUser");
        modelBuilder.Entity<IdentityUser>().ToTable("LibraryUser");
        modelBuilder.Entity<IdentityRole>().ToTable("Role");
        modelBuilder.Entity<IdentityUserRole>().ToTable("UserRole");
        modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaim");
        modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogin");
    }
    
  3. I have a Simple UserManager class called LibraryUserManager which just extends UserManager for my user Type.

    public class LibraryUserManager : UserManager<LibraryUser>
    
  4. When the database is seeded (when calling Update-Database) the following user is created:

    // -- Create Admin User, put in admin role..
    LibraryUserManager userManager = new LibraryUserManager(new UserStore<LibraryUser>(context));
    
    var user = new LibraryUser()
    {
        UserName = "admin@admin.com",
        Email = "admin@admin.com",
        DisplayName = "Administrator",
        Company = "Test"
    };
    
    userManager.Create(user, "Password1.");
    
    userManager.AddClaim(user.Id, new Claim(ClaimTypes.Role, "user"));
    userManager.AddClaim(user.Id, new Claim(ClaimTypes.Role, "author"));
    userManager.AddClaim(user.Id, new Claim(ClaimTypes.Role, "reviewer"));
    userManager.AddClaim(user.Id, new Claim(ClaimTypes.Role, "admin"));
    
  5. Once this is run.. the database has the user (in the LibraryUser table) and the Claims (in the UserClaim table)

enter image description here

enter image description here

  1. When the user is being authenticated by my custom Auth Provider they are found (via the user manager) and the GenerateUserIdentityAsync is called:

EDIT : showing rest of that method...

    var userManager = context.OwinContext.GetUserManager<LibraryUserManager>();

    LibraryUser user = await userManager.FindAsync(context.UserName, context.Password);

    //check if a user exists
    if (user == null)
    {
        context.SetError("invalid_grant", "The user name or password is incorrect.");
        return;
    }

    ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);
    AuthenticationProperties properties = CreateProperties(user.UserName, user.DisplayName, oAuthIdentity);           
    AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);

    context.Validated(ticket);
  1. Contents of create properties (called above):

    public static AuthenticationProperties CreateProperties(string userName, string displayName, ClaimsIdentity oAuthIdentity)
    {
        IDictionary<string, string> data = new Dictionary<string, string>
        {
            { "userName", userName },
            { "displayName", displayName },
            { "roles", string.Join(",", oAuthIdentity.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray())}
        };
    
        return new AuthenticationProperties(data);
    }
    
  2. When the user is authed.. I have put a breakpoint in LibraryUser.GenerateUserIdentityAsync (code under point 1 above) and the only claims in the ClaimsIdentity.Claims collection returned by CreateIdentityAsync are the default ones (name, identity_provider, security_stamp and one other..) .. the claims I have manually added are not returned from the DB..

Can anyone see what I am missing??

I have tried to give all the info I can if you require more please comment and I will amend my question.

Thanks in advance :D

_L


Solution

  • There conflicting line is located inside the OnModelCreating method:

    modelBuilder.Entity<LibraryUser>().ToTable("LibraryUser");
    modelBuilder.Entity<IdentityUser>().ToTable("LibraryUser"); // <-- this one
    

    Because you are using the LibraryUser as a derived class from IdentityUser, you don't need to explicitly map IdentityUser to a table. Doing it messes up with how the primary and foreign keys are generated in the database.