DEV Community

Carl Layton
Carl Layton

Posted on • Originally published at carlserver.com

Create Custom Password Hasher for ASP .NET Core Identity

In this post, I will look at how to extend the ASP .NET Core Identity membership system to use a custom class for hashing passwords. ASP .NET Core Identity is very extendable and extending it is a good way to get a better understanding of how the system works. We can customize it by providing new implementations to its interfaces. This code is not production ready and any custom security implementation should be done with care and be backed by good reasons. That being said, lets have some fun. This tutorial requires Visual Studio 2017, dotnet core 2.0, and LocalDb, which should be installed with Visual Studio depending on which components were picked at the time of install. The entire sample project is available on Github.

Start by opening Visual Studio 2017 and create a new ASP .NET Core Web Application project. I named it DotNetCoreAuthExamples but you can name it anything you like. Click OK.

create-project

Choose .NET Core and ASP.NET Core 2.0 from the dropdowns at the top. Select Web Application (Model-View-Controller) for the template and select Change Authentication and pick Individual User Accounts.

create-project-dotnet-core

After the project is created, debug it from Visual Studio. Once the website loads, click the Register link from the top menu bar. Fill in an email and password and click the Register button.

register-form

The first time an error will appear. This is because we havenโ€™t created the database yet. Click on Apply Migrations. This will create the database using Entity Framework. All the required database stuff is defined by the template and the defaults will work for this demonstration.

database-migration-errors

Once that is done, the button text changes to Migrations Applied. Refresh the page and click yes at any browser prompts about resubmitting the form. The home page will reload and the top menu bar will show the test user logged in.

If you get an error starting with SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server... check the server in the app settings connection string. This can be found in the appsettings.json file. The Visual Studio Sql Server Object Explorer will tell you the server path to LocalDb. Make sure the connection string matches this path. If not, change it and then stop and restart debugging.

After the page reloads with the user logged in, a record is also created in the database for this user. To see that, open the SQL Server Object Explorder in Visual Studio. It should auto-connect to LocalDb. The database name will contain the project name. Once you find it, query the AspNetUsers table like this SELECT * FROM dbo.AspNetUsers. There is 1 row with the registered user.

As this point, we have a working default MVC template with Individual User Accounts. We will now supplement this with a class to do our own password hashing. This is to demonstrate how different components of the Identity membership system can be customized. Create a new class named CustomPasswordHasher and have it implement IPasswordHasher<ApplicationUser>. There are 2 methods we need to implement, string HashPassword(TUser user, string password) and PasswordVerificationResult VerifyHashedPassword(TUser user, string hashedPassword, string providedPassword). Our custom class will reverse the original password string. This is to demonstrate how the methods will be used. In a real application, this would contain a secure, well planned, and well tested implementation. The full class is below. This should not be used in a real application.



    public class CustomPasswordHasher : IPasswordHasher<ApplicationUser>
    {
        public string HashPassword(ApplicationUser user, string password)
        {
            return ReversePassword(password);
        }

        public PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
        {
            if (hashedPassword == ReversePassword(providedPassword))
            {
                return PasswordVerificationResult.Success;
            }

            return PasswordVerificationResult.Failed;
        }

        private string ReversePassword(string value)
        {
            // This is not a secure way to store a password!
            char[] charArray = value.ToCharArray();
            Array.Reverse(charArray);
            return new string(charArray);
        }
    }


Enter fullscreen mode Exit fullscreen mode

The final thing to do is register our new implementation with the dependency injection system. This happens in the ConfigureServices method of the Startup class. Copy/Paste the following line as the first line of the method: services.AddTransient<IPasswordHasher<ApplicationUser>, CustomPasswordHasher>();. To learn more about Dependency injection in ASP .NET Core, check out my post "ASP .NET Core MVC Dependency Injection Overview". The full ConfigureServices method is below.



    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<IPasswordHasher<ApplicationUser>, CustomPasswordHasher>();

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        // Add application services.
        services.AddTransient<IEmailSender, EmailSender>();

        services.AddMvc();  
    }


Enter fullscreen mode Exit fullscreen mode

The only thing left to do is test it. Place a breakpoint in both the HashPassword and VerifyHashedPassword methods. Start debugging and register a new user, like we did above. On registration, the HashPassword breakpoint will be hit and on login the VerifyHashedPassword method will be hit. In both cases, the user will be logged in. If you got this far, it means you have successfully created a custom password hasher for ASP .NET Core Identity!

In conclusion, this post shows 1 example of overriding a feature of ASP .NET Core Identity. The complete code can be found in this Github repository. This code is for educational purposes only and is not meant for production environments. Thought, planning, and good reasons need to be involved for authentication code meant for a production environment. That being said, it is possible to customize many parts of the ASP .NET Core Identity system. In my next post, I will expand on this with an example of customizing the user store component to allow user accounts to be stored in memory instead of a database.

Top comments (0)