I'm working on an ASP.NET MVC 5 project in VS2013, .NET 4.5.1, that uses Entity Framework 6 Code-First. I have a decent size database built out and somewhat working (project is about two weeks old). I want to integrate user authentication now, but I'm not sure how to approach it. After spending most of the day researching I've decided to give the new ASP.NET Identity framework a shot over having to write custom Membership or Role providers. What I'm confused about is how to make it all work with the existing database/model I have.
Currently I have an object called Employee
that holds basic employee information (for now). After having pondered the question all day, I decided to decouple authentication from it into a User
object, which is what Identity wants anyway. That being said how do I make it all work?
Here's my Employee
class:
public class Employee : Person {
public int EmployeeId { get; set; }
public byte CompanyId { get; set; }
public string Name {
get {
return String.Format("{0} {1}", this.FirstName, this.LastName);
}
}
public string Password { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<Email> Emails { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
public Employee() {
this.Addresses = new List<Address>();
this.Emails = new List<Email>();
this.Phones = new List<Phone>();
}
}
And my DbContext
derived class:
public class DatabaseContext : DbContext {
static DatabaseContext() {
Database.SetInitializer<DatabaseContext>(new DatabaseInitializer());
}
public DatabaseContext()
: base("Name=DatabaseContext") {
this.Database.Initialize(true);
}
public DatabaseContext(
string connectionString)
: base(connectionString) {
this.Database.Initialize(true);
}
/// DbSets...
public override int SaveChanges() {
try {
return base.SaveChanges();
} catch (DbEntityValidationException e) {
IEnumerable<string> errors = e.EntityValidationErrors.SelectMany(
x =>
x.ValidationErrors).Select(
x =>
String.Format("{0}: {1}", x.PropertyName, x.ErrorMessage));
throw new DbEntityValidationException(String.Join("; ", errors), e.EntityValidationErrors);
}
}
protected override void OnModelCreating(
DbModelBuilder modelBuilder) {
modelBuilder.Ignore<Coordinate>();
/// Configs...
base.OnModelCreating(modelBuilder);
}
}
No one solution fits all situations, but for my project I found that the easiest thing to do was extend the
IdentityUser
andIdentityDbContext
classes. Below is pseudocode that focuses on the bare minimum you would need to change/add to get this working.For your user class:
For your DbContext implementation:
And in Startup.Auth.cs:
Another potential option is to create a 1-1 relationship between your DomainUser class and the ApplicationUser class which inherits from IdentityUser. This would reduce the coupling between your domain model and the Identity mechanism, especially if you used WithRequiredDependent without creating a bidirectional navigation property, something like so:
So after spending about a day or so reading and reading, I ended up building my own Identity implementation. First what I did was take my existing
Employee
object and extended it to inherit fromIUser<int>
.IUser<int>
is an interface that's a part of Identity 2.0 (currently in alpha) that allows the primary key type to be configured to something other thanstring
as was default in 1.0. Because of the way I'm storing data, my implementation was really specific. For example, anEmployee
can have multipleEmail
objects related to it, and for my application I wanted to use emails as the user names. So, I simply set theUserName
property to return theEmployee
's work email:Side note, since I'm not going to be using the setter for the property, is there a cleaner way of obsoleting it other than simply leaving it empty?
Moving on, I also added the
PasswordHash
property. I added my ownRole
object, inheriting fromIRole<int>
. Lastly theEmployee
andRole
objects each have anICollection<T>
linking to each other. Another side note, the Entity Framework implementation of Identity manually creates the mapping tableUserRoles
rather than leveraging it's own configuration capabilities and I can't seem to understand the reasoning behind it. TheUserRole
it creates does get passed into the*Store
s it implements, but it doesn't really do anything special other than act as a link. In my implementation I simply used the already established link, which of course creates a mapping table in the database, but is not pointlessly exposed into the application. I just find it curious.Moving on again, with my configured objects I went ahead and implemented my own
IUserStore
andIRoleStore
classes creatively calledEmployeeStore
andRoleStore
:RoleStore
:Now, what I noticed was that the Entity Framework implementation was creating what looked like a mini-repository. Since my project was already using my own Repository implementation, I decided to leverage it instead. We'll see how that goes...
Now, all of this works and surprisingly does not crash at all, or at least hasn't yet. That being said, I have all of these wonderful Identity implementations, yet I can't seem to figure out how to leverage them inside my MVC application. Since that falls out of scope for this question, I'll go ahead and open a new one addressing that.
I'm leaving this as the answer to the question in case someone else runs into this in the future. Of course, if anyone sees an error in the code I've posted, please let me know.
Take a look at the SimpleSecurity Project source code for an example of how the database context of ASP.NET Identity was extended to include new tables. This may work for your situation. Here is the how the new context was defined by inheriting from the ASP.NET Identity context.
The SimpleSecurity Project decouples ASP.NET Identity from your MVC application and extends it.
Since your Employee class appears to be the user profile for membership I would look at tailoring it to fit with how you customize the user profile in ASP.NET Identity, which is discussed here. Basically your Employee class needs to inherit from IdentityUser and you would remove the Password property from Employee, since this is defined in IdentityUser and the framework looks for it there. Then when defining your context you would use the Employee class instead so it would look something like this