3
votes

I'm upgrading my ASP.Net MVC5 application from Identity 1 to 2.1. After one day I made it run... but I can't verify roles for my users (by IsInRole or Authorize Attribute). I guess it is because MVC doesn't resolve the user and role manager since my application uses Unity DI and Identity 2 seems to be based on OwinContext with the following configuration :

app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

Is there a simple way, tutorial or documentation to have Identity 2 working with external dependency injection ?

[Edit] Now Unity DI seems to work a little (thanks to meep).

I modified Startup.Auth.cs, adding to ConfigureAuth(IAppBuilder app) function :

var container = UnityConfig.Container;
var dbContext = container.Resolve<ApplicationDbContext>();

container.RegisterInstance<IAppBuilder>(app, new ContainerControlledLifetimeManager());
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>();
container.RegisterType<UserManager<ApplicationUser>, ApplicationUserManager>();
container.RegisterType<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>();
container.RegisterType<RoleManager<IdentityRole>, ApplicationRoleManager>();

// [Edit 2]
app.CreatePerOwinContext<ApplicationDbContext>((options, owinContext) => container.Resolve<ApplicationDbContext>());
app.CreatePerOwinContext<UserManager<ApplicationUser>>((options, owinContext) => container.Resolve<UserManager<ApplicationUser>>());
app.CreatePerOwinContext<RoleManager<IdentityRole>>((options, owinContext) => container.Resolve<RoleManager<IdentityRole>>());

In my UnitiConfig.cs file I added the declaration for a Container singleton (used above).

In IdentityConfig.cs, I dropped the Create method and changed the ctor for

public ApplicationUserManager(IUserStore<ApplicationUser> store, ApplicationDbContext dbContext)
        : base(store)
{
    this.Initialize(dbContext);
}

private void Initialize(ApplicationDbContext dbContext)
{
    // Configurer la logique de validation pour les noms d'utilisateur
    this.UserValidator = new UserValidator<ApplicationUser>(this)
    {
        AllowOnlyAlphanumericUserNames = false,
        RequireUniqueEmail = true
    };
    // ... more code...
}

public class ApplicationRoleManager : RoleManager<IdentityRole>
{
    public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
        : base(roleStore)
    {
    }
}

I can authenticate the users... but UserManager.GetRoles() is always empty. Moreover [Authorize(Roles="myrole"] rejects every user...

1
Not much to it, really. Just register your ApplicationUserManager, ApplicationRoleManager, ApplicationSignInManager and ApplicationDbContext in your DI container. Beware that Identity still needs Owin registration for ApplicationDbContext and for ApplicationUserManager, so keep it present. - trailmax
not simple in fact. I had it half work using the links given by "meep", but my UserManager.GetRoles() is always empty... even through UserManager.IsInRole returns true. Nevertheless [Authorize(Roles="Admin")] always denies access... - boblemar
How can I register ApplicationDbContext and ApplicationUserManager into Owin using the DI resolution (I don't want to create a context for DI and another for Owin) ? - boblemar
I used app.CreatePerOwinContext<ApplicationUserManager>(DependencyResolver.GetService<ApplicationUserManager>());. Not very familiar with Unity, but this works with Autofac and SimpleInjector. - trailmax
If you not getting roles from UserManager.GetRoles(), can you check if roles are actually assigned in the database? - trailmax

1 Answers

4
votes

OK... Thank you all ! I got it !

By profiling the query send to SQL Server (using Express Profiler) I saw that EF tryed to join Roles Table On a column named IdentityUser_Id. Back to the table definition in SQL Server I saw 2 fields : - UserId containing the ids - IdentityUser_Id... always null ! So that's why my Roles were always empty !

But why theses 2 columns for the same data ?

I took a look at the EF migration file. Here were some strange lines :

RenameColumn(table: "dbo.T_Securite_AspNetUserClaims", name: "User_Id", newName: "IdentityUser_Id");
AddColumn("dbo.T_Securite_AspNetUserLogins", "IdentityUser_Id", c => c.String(maxLength: 128));
AddColumn("dbo.T_Securite_AspNetUserRoles", "IdentityUser_Id", c => c.String(maxLength: 128));

So it looks like the EF Migration generator was lost. I changed the OnModelCreating method for ApplicationDbContext to help it a little by telling where the foreign keys are actually :

modelBuilder.Entity<IdentityUser>()
            .ToTable("T_Security_AspNetUsers"); // Yes... I changed the table name : maybe the source of the problem !

modelBuilder.Entity<IdentityUser>()
            .HasMany(u => u.Roles)
            .WithOptional()
            .HasForeignKey(r => r.UserId);

modelBuilder.Entity<IdentityUser>()
            .HasMany(u => u.Logins)
            .WithOptional()
            .HasForeignKey(l => l.UserId);

modelBuilder.Entity<IdentityUser>()
            .HasMany(u => u.Claims)
            .WithOptional()
            .HasForeignKey(c => c.UserId);

Now the EF Migration file is looking better. I have roles for my users and the [Authorize(Role="xxx")] seems to work !