Asp.Net Identity 2.2 Localization of error messages

It’s interesting to see that Microsoft is trying to better in term of localization but still fail to provide a perfect solution. Asp.Net Identity is by default English oriented but at least not hardcoded. It uses inside the Identity library a resource file.
BadExample

Here is what you can see if your decompile Asp.Net Identity assembly. You will see a resource file with defined strings.

IdentityResourceFile

The problem is that you cannot just define your own resource file and setup the string you desire. You must use one of Microsoft’s package for your language. Here is the one for French.

Install-Package Microsoft.AspNet.Identity.Core.fr

AspNetIdentityCore

The problem with that solution is that you must be lucky to have your language available and you must like the string they chose for you. Some other options available is to create your own UserValidator and PasswordValidator and by inheriting the Identity ones can still have the benefit of Identity. Overall, there is some workaround but still is time consuming for nothing. Better solution like just having to drop a resource file of your own would have been way easier and easily custom for solution. Nevertheless, it starts to get in the right direction after more than a decade trying to create a better Identity framework.

CreateIdentityAsync value cannot be null when logging with User created with Migration Tool

If you create a user from your Asp.Net MVC (Identity) and logging with this one, it should work. However, if you create your users by code, for example with Entity Framework Migration seeding method, you may have for result an error page. The error is not very clear. It can say that the value cannot be null and that the parameter name is value. This does not give a lot of information about where the error is. The stack trace shows some information about security claim and the problem is trigged by the call to the UserManager CreateIdentityAsync method.

CreateUserError

In fact, the problem is that the Security Stamp was not set. This was found after seeing a difference in AspNetUsers table.

SecurityStamp

As you can see, the column was not set for user created by Entity Framework Migration Seed method.

To solve this issue, the seed method changed to generate a Security Stamp. The security stamp can be generated with a random GUID.

var adminUser = new ApplicationUser { Id = Guid.NewGuid().ToString()
	, FirstName = "Patrick"
	, LastName = "Desjardins"
	, DisplayName = "pdesjardins"
	, Email = EMAIL_ADMIN
	, CreationDateTime = runningContext.GetCurrentTime()
	, UserName = userValueGenerator.GenerateUserName(EMAIL_ADMIN), PasswordHash = ApplicationUser.HashPassword("123123")
	, ValidationDateTime = runningContext.GetCurrentTime()
	, SecurityStamp = Guid.NewGuid().ToString()
};

With the Security Stamp set, it is possible to login without having CreateIdentityAsync raising any exception.

Asp.Net MVC Login with email instead of UserName with Identity

I found very strange that it is not a simple task to login with an email in Asp.Net MVC. You cannot simply decide what property is the identifier. Asp.Net MVC decides for you that it is the UserName property.

Here is a the IdentityUser class from Microsoft.AspNet.Identity.EntityFramework.

namespace Microsoft.AspNet.Identity.EntityFramework
{
  public class IdentityUser : IUser
  {
    public virtual string Id { get; set; }
    public virtual string UserName { get; set; }
    public virtual string PasswordHash { get; set; }
    public virtual string SecurityStamp { get; set; }
    public virtual ICollection<IdentityUserRole> Roles { get; private set; }
    public virtual ICollection<IdentityUserClaim> Claims { get; private set; }
    public virtual ICollection<IdentityUserLogin> Logins { get; private set; }
    public IdentityUser()
    {
      this.Id = Guid.NewGuid().ToString();
      this.Claims = (ICollection<IdentityUserClaim>) new List<IdentityUserClaim>();
      this.Roles = (ICollection<IdentityUserRole>) new List<IdentityUserRole>();
      this.Logins = (ICollection<IdentityUserLogin>) new List<IdentityUserLogin>();
    }
    public IdentityUser(string userName): this()
    {
      this.UserName = userName;
    }
  }
}

The first step to use email is to add an email property to this class. Microsoft Identity team has not sealed the class, so it is possible to inherit from it and add you own property. This will extend what we can do with Identity.

public class ApplicationUser : IdentityUser
{
    public string Email { get; set; }
}

That’s it for the model. Entity Framework will use this class instead of the default one if you define you DbContext with a special class.

public class MainDbContext : IdentityDbContext<ApplicationUser>{//...}

That is what is required. This create in the background a DbSet of your class that inherit from IdentityUser. I have called mine ApplicationUser, but you can use whatever you prefer. IdentityDbContext override the OnModelCreating. This is important that if you inherit from it that you call the base class to still have all the configuration provided by the IdentityDbContext. To be more detailed, the OnModelCreating associate the custom class to AspNetUsers table. It does plenty of others thing, but from what it concerns us right now, this is it. From here, if you instruct Entity Framework to build your database, you should see in the database your field in Identity table.
AspNetUserEmailProperty

The next step is to change the View Model and the View. The View Model for the registration use an UserName property. We can remove this and add one for Email.

public class RegisterViewModel
{
    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }

    [Required]
    [Display(Name="Email")]
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }
}

This is the place where you can add additional data annotation to have more validation. Then, we need to change the view that use the Register View Model. We need to remove the username form group to add a new one for email.

 <div class="form-group">
        @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
        </div>
    </div>

The last step is to change the controller to do something with the email and username. Since Identity still use username and password combination than we have to trick the system.

First of all, we need to generate a user name from the email. It is not possible to use directly the email because it has some invalid characters like @. You can have a small method that transform the user name.

public string GenerateUserName(string email)
{
    return email.Replace("@", "").Replace(".", "").Replace("-", "");
}

From here, you can create a new ApplicationUser and assign your generated user name into the property. The next code remains the same by calling the UserManager.

Second, we need to modify the login because user will enter an email and you need to transform it to user name. The login ViewModel needs to be changed.

public class LoginViewModel
{

    [Required]
    [DataType(DataType.EmailAddress)]
    [Display(Name="Email")]
    public string Email { get; set; }
    
    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [Display(Name = "Remember me?")]
    public bool RememberMe { get; set; }
}

The view also change by not having input for an username but for an email. This is the same way we have done for the registration page. I will not put the code here to keep it simple.

Finally, the controller must be changed too. Not a lot of thing need to be changed but since we ask for an email and that the system use a username than we must convert it.

var user = await userService.FindAsync(GenerateUserName(model.Email), model.Password);

In conclusion, it requires some manipulation but it is not difficult. It would be cleaner not to manipulate the UserName property but it is still not a nightmare to proceed.

Concerning the user name

The algorithm that generate the user name is not strong. Collision can occur if you have the concatenation this way. This is not a huge deal since it will fail to save in the database but still a better way is to have something unique. To save character like @ as user name, the framework allows you to configure the UserManager to have not only alphanumeric characters.

public UserManager(IMainDbContext dbContext)
{
	var dbMainContext = dbContext.GetDbContext();
	this.userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(dbMainContext));

	//Allow to have email in the username
	this.userManager.UserValidator = new UserValidator<ApplicationUser>(this.userManager)
	{
		AllowOnlyAlphanumericUserNames = false
	};
}

As you can see, you can set a new UserValidator and set the AllowOnlyAlphanumericUserNames to false. This is way better!

Migrating Asp.Net MemberShip to Asp.Net MVC Identity for authentification

I was using WebMatrix with the Gym Workout project. But, it wasn’t a clean way to handle authentification. It was in the middle of the old membership with a new flavor. However, since Microsoft is going with the new One Asp.Net Owin Identity framework which allow to use claims but also to be dissociate from Microsoft Sql Server make it more appealing. It’s also on the frame of OWIN, so we have almost all positive features without the negative parts. Finally, Identity does have the clean way to have custom field to extend default username/password attribute. It’s clean because it creates specific columns for each field. So, if you have an existing Asp.Net MVC, how do you upgrade to the Identity framework for authentification? This is what I will explain in this Identity tutorial.

First, some libraries are required.

  • Microsoft.AspNet.Identity.EntityFramework
  • Microsoft.AspNet.Identity.Core
  • Microsoft.AspNet.Identity.OWIN

This can be found by using Nuget with the following command:

PM> Install-Package Microsoft.AspNet.Identity.Core
PM> Install-Package Microsoft.AspNet.Identity.Owin
PM> Install-Package Microsoft.AspNet.Identity.EntityFramework
PM> Install-Package Microsoft.Owin.Host.SystemWeb

The next step was to get rid of everything concerning the user which was in its own class called for the project “UserProfile”. Instead, I created a new class called “ApplicationUser” which inherit from the default one of Identity framework. I removed the WebUserProvider because now, it will use Entity Framework to get user information. Providers are not required and everything will go with the service layer, to the data access layer to the database with Entity Framework (EF). The AccountModel (is in fact the view model) still exist which allow to have our custom fields like the Role, Language and Email in the view. Everything is bound to the model class ApplicationUser.

public class ApplicationUser : IdentityUser, ICurrentUser
{
    public ApplicationUser()
    {
        Email = "";
        Language = "";
    }
    public string UserId {
        get { return base.Id; }
        set{} 
    }
    public string Email { get; set; } 
    public string Language { get; set; } 
        
}

Even if the ApplicationUser inherit from IdentityUser which as a Id property, the ICurrentUser that we had previously use the UserId property. This is why this one is defined in ApplicationUser and link to the IdentityUser one. The only thing that we will do later is to ignore this property to not have the data inside the database. We also need to create a ApplicationUserService that contact the Data Access Layer.

A major change concerns the ServiceFactory class which has every entities service classes. Since the Account come from the database and that we do not want to go to the database every time one of the entity require to have something related to the user logged, they need to share the same account. This require a change in the constructor that is going to take in its constructor the IUserProvider which has a property Account. This one is passed to every service constructor.

public class ServiceFactory : IServiceFactory
{
    private IUserProvider _userProvider;
    #region Implementation of IServiceFactory

    public IAccountService Account { get; private set; }
    public IMuscleService Muscle { get; private set; }
    public IWorkoutService Workout { get; private set; }
    public IWorkoutSessionService WorkoutSession { get; private set; }
    public IWorkoutSessionExerciseService WorkoutSessionExercise { get; private set; }

    public IExerciseService Exercise { get; private set; }

    #endregion

    public ServiceFactory(IRepositoryFactory repositoryFactory, IMapperFactory mapperFactory, IUserProvider userProvider)
    {
        _userProvider = userProvider;
        var account = _userProvider.Account;
        Account = new ApplicationUserService(repositoryFactory, mapperFactory);
        Muscle = new MuscleService(repositoryFactory, mapperFactory, account);
        Workout = new WorkoutService(repositoryFactory, mapperFactory, account);
        WorkoutSession = new WorkoutSessionService(repositoryFactory, mapperFactory, account);
        WorkoutSessionExercise = new WorkoutSessionExerciseService(repositoryFactory, mapperFactory, account);
        Exercise = new ExerciseService(repositoryFactory, mapperFactory, Muscle, account);
    }
}

Entities inherit of BaseService.

public abstract class BaseService
{
        public BaseService(IRepositoryFactory repositoryFactory, IMapperFactory mapperFactory, ICurrentUser user)
        {
            Repository = repositoryFactory;
            Mapper = mapperFactory;
            Repository.SetUser(user);
        }

        protected IRepositoryFactory Repository { get; private set; }
        protected IMapperFactory Mapper { get; private set; }
}

When the service is initialized, it uses the injected Repository to set the user that is also injected. Since the ServiceFactory calls the Account once, one call is done to the database. After, it only set the object to the repository which could be used later.

The concept of IUserProvider remains. This is because we will use a WebUserProvider for the Asp.Net MVC and a WebServiceUserProvider.

The Global.asax.cs file changes because it does not use anymore the AuthConfig.RegisterAuth() because the Identity with OWIN does have a Startup page in the App folder.

public partial class Startup
{

        // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
    public void ConfigureAuth(IAppBuilder app)
    {
        // Enable the application to use a cookie to store information for the signed in user
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login")
        });
        // Use a cookie to temporarily store information about a user logging in with a third party login provider
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
    }

     
}

This also require to register the Startup class with another Startup class that is used as a bootstrapper for the OWIN container.

using Owin;

[assembly: OwinStartup(typeof(WorkoutPlanner.Startup))]
namespace WorkoutPlanner
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
    }
}

Notice the assembly instruction that tell which class is used for OwinStartup. It uses two classes but could use one. the only benefit of splitting the class in two is to have one statup entry and the code to configure somewhere else. This could later uncluttered the booting class.

The migration changes also. The Seed method does not use the WebSecurity to initialize default user. It uses the ApplicationUser class with the UserStore and UserManager class that use Entity Framework to work with the database.

To create the database with all Identity classes and all the business classes, the migration command in the package console is required. The

update-database -ConfigurationTypeName "Configuration"

This works because the Configuration class inherit of DbMigrationsConfigurations which allow the migration tool to be executed. It takes the database context to be used. In our case, it’s the one that contains every entities of the application.

public class Configuration : DbMigrationsConfiguration<DatabaseContext>//<DatabaseContext>
{
	public Configuration()
	{
              base.AutomaticMigrationsEnabled = true;
	}

	protected override void Seed(DatabaseContext context)
	{
		var userStore = new UserStore<ApplicationUser>(context);
		var manager = new UserManager<ApplicationUser>(userStore);

		var role = new IdentityUserRole { Role = new IdentityRole(Model.Roles.ADMINISTRATOR) };
		var user = new ApplicationUser() { UserName = "123123", Email = "123123@123.com", Language = "en-US" };
		user.Roles.Add(role);
		IdentityResult result = manager.Create(user, "123123");

		var role2 = new IdentityUserRole { Role = new IdentityRole(Model.Roles.NORMAL) };
		var user2 = new ApplicationUser() { UserName = "qweqwe", Email = "qweqwe@qweqwe.com", Language = "fr-CA" };
		user.Roles.Add(role2);
		IdentityResult result2 = manager.Create(user2, "qweqwe");

		var muscles = new[]{new Muscle { Id = 1, Name = new LocalizedString { French = "Cou", English = "Neck" } },
							new Muscle { Id = 2, Name = new LocalizedString { French = "Épaule", English = "Shoulder" } }
							};


		context.Set<Muscle>().AddOrUpdateRange(muscles);
		//... and so on...
		base.Seed(context);
	}
}

When working with Asp.Net Identity one key is to have your database context class inherit from IdentityDbContext and not directly from DbContext. Otherwise, you will get that keys are not defined for IdentityUserLogin, IdentityUserRole, IdentityUserLogins and IdentityUserRoles.

DataAccessLayer.Database.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
DataAccessLayer.Database.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.
public class DatabaseContext :IdentityDbContext<ApplicationUser>, IDatabaseContext

Migrating from Asp.Net Membership to Identity is not something that can be done for the first time under few hours. In fact, it took me around 12 hours to figure out that I had to inherit from IdentityDbContext and to solve several seeding problem. Also, having to use Entity Framework changed a little but how to use the user information. Nevertheless, the time is not exponential since it concerns only users and roles entities. I believe that it should take less than a day for someone who have all the information.

Asp.Net Identity error while seeding with custom field

If you are using the new Asp.Net Identity framework which is working with OWIN, you may fall with a SQL exception. This exception look like this :

System.Data.SqlClient.SqlException: Invalid column name ‘UserId’

SeedingError

The UserId is a column that is ignored in the configuration of the entity. Why does it try to read from it?

Here is how the DbContext looks like:

public class Configuration : DbMigrationsConfiguration<DatabaseContext>
{
	public Configuration()
	{
	}

	protected override void Seed(DatabaseContext context)
	{
		var userStore = new UserStore<ApplicationUser>();
		var manager = new UserManager<ApplicationUser>(userStore);

		var role = new IdentityUserRole { Role = new IdentityRole(Model.Roles.ADMINISTRATOR) };
		var user = new ApplicationUser() { UserName = "123123", Email = "123123@123.com", Language = "en-US" };
		user.Roles.Add(role);
		IdentityResult result = manager.Create(user, "123123");

		var role2 = new IdentityUserRole { Role = new IdentityRole(Model.Roles.NORMAL) };
		var user2 = new ApplicationUser() { UserName = "qweqwe", Email = "qweqwe@qweqwe.com", Language = "fr-CA" };
		user.Roles.Add(role2);
		IdentityResult result2 = manager.Create(user2, "qweqwe");
		
		//...
	}
}

The problem reside on the first line of the seeding method. The UserStore takes in its first parameter the database context. Since I am not explicitly telling the UserStore to take the one passed by parameter, it does not know about the configuration defined in the DatabaseContext.

To solve the problem just change the first line with this line:

	var userStore = new UserStore<ApplicationUser>(context);