Using Stefan Cebulak's answer and a great Ben Foster blog article ASP.NET Bare Authentication I came up with the solution below that I applied to ASP.NET Identity 2.0 generated by Visual Studio 2013 AccountController .
The solution uses an integer as the primary key for users, and also allows you to get the identifier of the current user without making a trip to the database.
Here are the steps you need to follow:
1. Creating user-defined custom classes
By default, AccountController uses classes that use string as the type of primary key. We need to create classes below that will be used instead of int . I defined all the classes below in one file: AppUser.cs
public class AppUser : IdentityUser<int, AppUserLogin, AppUserRole, AppUserClaim>, IUser<int> { } public class AppUserLogin : IdentityUserLogin<int> { } public class AppUserRole : IdentityUserRole<int> { } public class AppUserClaim : IdentityUserClaim<int> { } public class AppRole : IdentityRole<int, AppUserRole> { }
It will also be useful to create a custom ClaimsPrincipal that easily displays the user id
public class AppClaimsPrincipal : ClaimsPrincipal { public AppClaimsPrincipal( ClaimsPrincipal principal ) : base( principal ) { } public int UserId { get { return int.Parse(this.FindFirst( ClaimTypes.Sid ).Value); } } }
2. Create a custom IdentityDbContext
In our application database context, IdentityDbContext will expand by default, which implements all authentication related DbSets. Even if DbContext.OnModelCreating is an empty method, I'm not sure about IdentityDbContext.OnModelCreating , so when overriding, remember to call base.OnModelCreating( modelBuilder ) AppDbContext.cs
public class AppDbContext : IdentityDbContext<AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim> { public AppDbContext() : base("DefaultConnection") {
3. Create custom UserStore and UserManager to be used above
AppUserStore.cs
public interface IAppUserStore : IUserStore<AppUser, int> { } public class AppUserStore : UserStore<AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim>, IAppUserStore { public AppUserStore() : base( new AppDbContext() ) { } public AppUserStore(AppDbContext context) : base(context) { } }
AppUserManager.cs
public class AppUserManager : UserManager<AppUser, int> { public AppUserManager( IAppUserStore store ) : base( store ) { } }
4. Modify AccountController to use your custom classes
Change all UserManager to AppUserManager , UserStore to AppUserStore , etc. Take an example of these constructors:
public AccountController() : this( new AppUserManager( new AppUserStore( new AppDbContext() ) ) ) { } public AccountController(AppUserManager userManager) { UserManager = userManager; }
5. Add the user ID as a claim to the ClaimIdentity stored in the cookie
In step 1, we created an AppClaimsPrincipal that returns a UserId from ClaimType.Sid . However, in order to have this claim, we need to add it when registering with the user. In AccountController the SingInAsync method SingInAsync responsible for logging in. We need to add a line to this method in order to add a ticket.
private async Task SignInAsync(AppUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
6. Create a BaseController with the CurrentUser property
To have easy access to the already registered user ID in your controllers, create an abstract BaseController from which your controllers will be displayed. In BaseController create CurrentUser as follows:
public abstract class BaseController : Controller { public AppClaimsPrincipal CurrentUser { get { return new AppClaimsPrincipal( ( ClaimsPrincipal )this.User ); } } public BaseController() { } }
7. Inherit your BaseController controllers and enjoy
From now on, you can use CurrentUser.UserId in your controllers to access the current user ID in the system without disconnecting from the database. You can use it to query only objects belonging to the user.
You donβt have to worry about automatically generating the primary keys of the user - it is not surprising that the Entity Framework by default uses Identity for whole primary keys when creating tables.
Warning! Keep in mind that if you implement it in an already released project, for already registered users ClaimsType.Sid will not exist, and FindFirst will return null in AppClaimsPrincipal . You need to either force all users to log out or run this script in AppClaimsPrincipal