Build a New Web Application With User Accounts
One of the hallmarks of .NET development is the ease with which developers can create and manage secure user accounts. To demonstrate this, we’re going to build an application with secure individual user accounts.
Open Visual Studio and perform the following actions:
Select File > New > Project.
Select ASP.NET Core Web Application.
Name the project MySecureApp, and click OK.
From the top drop-down, ensure that ASP.NET Core 2.1 is selected, then select Web Application (Model-View-Controller), and click Change Authentication.
Select Individual User Accounts from the radio buttons on the left, and ensure the drop-down reads Store user accounts in-app, click OK, then click OK again.
That’s it. Visual Studio is now generating your base application, complete with a secure system for managing user accounts, including registration and login. User accounts by this means are built and managed using ASP.NET Core Identity, which is included in the application as a Razor class library.
Now that you have a functioning web application (yes, it works as-is!), it’s time to take it for a test drive. Right now, there is a user database, but there are no users in it, so let’s add one.
This is pretty simple, so far. It’s even simpler than you think. Run the application, and let’s get on with adding a user.
When the application comes up, you should see something similar to this:
Select Register from the right side of the menu bar. The Registration page then displays:
Enter your email, add a password for your account, then click Register. The default settings for ASP.NET Core Identity require following characteristics for passwords:
A minimum of six (6) characters in length.
At least one (1) number.
At least one (1) uppercase character.
At least one (1) lowercase character.
At least one (1) non-alphanumeric character.
At this point, you may receive an error message such as the one shown below:
If this is the case for you, click the Apply Migrations button. This error indicates that data migrations have not been applied to your newly-created database.
After you’ve applied the migrations, refresh the page. This will resubmit your registration credentials. Once the process is complete, you should see a page similar to this:
Notice this is the same screen you saw when we first ran the application, except instead of the Register and Login links in the upper-right portion of the menu bar, there is now a Hello message addressing the email address you entered, and a link for logout. Your user account is now successfully created, password-protected, and stored in the database. Go ahead and click Logout, then click Login and try logging in with your credentials.
That was pretty easy, wasn’t it? Seriously, that’s all it takes to create an application with secure individual user accounts with ASP.NET Core Identity. But is that all there is to user accounts?
On the contrary, there is a lot you can do to customize this entire process and make it your own, depending on your needs. So let’s take a deeper dive into how to configure the services provided by ASP.NET Core Identity.
Configure ASP.NET Core Identity Services
In the root folder of your ASP.NET project, you will find the startup.cs file, which contains the Startup
class and the configuration settings for your application. The part we are interested in right now is the ConfigureServices
method. This is where you set up custom configuration options for your identity services. Initially, this method will appear similar to the following:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
This is all well and good, but what if you want to increase security by setting a minimum length and complexity for user passwords? Or maybe you want to limit the number of permitted failed login attempts before locking the user’s account. You should never be satisfied with accepting default values for security when better options are available, and .NET Core does just that.
Setting Identity Configuration Options
here are many options you might wish to configure for your application’s specific needs. Modify your ConfigureServices
method to include the following additions, then let’s discuss what options you’ve added to your application’s configuration settings.
services.Configure<IdentityOptions>(options =>
{
// User settings.
options.User.RequireUniqueEmail = true;
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@";
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 9;
// Lockout settings.
options.Lockout.MaxFailedAccessAttempts = 3;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
});
In the above section of code, we have added three additional configuration categories: user settings, password settings, and lockout settings.
User Settings
Under the user settings, there are two conditions to be applied to all new usernames during user registrations:
Every email used during registration must be unique. That is, a single email may not be used to create multiple user accounts.
All alphanumeric characters, upper and lowercase are permitted in usernames, as are the characters
-
,.
,_
, and@
. Usernames are now restricted to these characters. A username with a dollar sign in it, for example, will be rejected by this configuration.
Password Settings
In the code segment above, five important conditions must be applied to the creation of all user passwords:
All passwords must contain at least one numeric digit.
All passwords must contain at least one lowercase character.
All passwords must contain at least one non-alphanumeric character (i.e. special characters such as
-
,_
,!
,+
,~
, etc).All passwords must contain at least one uppercase character.
All passwords must be at least 9 characters in length.
Lockout Settings
In the code segment above, two particular conditions must be monitored and applied to user login attempts.
The first condition sets the maximum number of failed login attempts at three. That is, after three failed attempts, the user’s account will be locked.
The second condition specifies the duration of the user’s lockout state. In this case, if the user makes three failed login attempts, the user’s account is locked for 10 minutes, after which the account is released, and the user is free to attempt to log in again.
Enabling ASP.NET Identity
ASP.NET Identity must be enabled before the specified configuration options can be put to good use. This is done through a straightforward call within the Configure
method, which, like ConfigureServices
, is also found in startup.cs. Let’s take a minute to examine the contents of this method as generated by Visual Studio with the default app configuration.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"
);
});
}
The first thing to note is that a .NET Core application lets you set up different error handling mechanisms depending upon the runtime environment. That is, you can set up specific error pages to be used during development that won't be used after the application is published. There are few occasions when a developer will make changes to this particular method, the lone exception being the specification of the default MVC route. By default, when you don't specify the controller or action in the URL, the home page that is displayed is the index page for the HomeController. There may be occasions when you, as a developer, want an alternative default page, and this is where that page is specified.
But I want to draw your attention to a single line of code:
app.UseAuthentication();
This line is the entry point for ASP.NET Identity. It tells the application that you want to activate user authentication for secure access, and you want to use the configuration options specified in the ConfigureServices method. This line adds authentication middleware to the request pipeline.
Middleware is a particular piece of software that is assembled into the application pipeline to handle requests and responses. The authentication middleware for ASP.NET Identity ensures that a user is authenticated before granting access to system resources. The user authentication cycle (or pipeline) consists of three specific events: registration, login, and logout.
Understand User Registration, Login, and Logout
You've seen that the application can register and log users in and out. However, it does not currently have the components necessary to permit you to customize this process. So let’s add these components through a process called scaffolding.
ASP.NET Core versions 2.1 and later provide ASP.NET Core Identity as a Razor class library (RCL). Applications that include Identity by calling app.UseAuthentication()
can use the scaffolder to add the source code found in the Identity Razor Class Library. In this way, it becomes possible to generate source code that can be modified to customize the behavior. Code that is generated in this manner takes precedence over the same code that already exists in the Identity RCL (the code you saw in action when you registered a new user in your application earlier in this lesson).
Although the scaffolder generates most of the code you need, it is necessary to update your project to complete the process. When you run the Identity scaffolder, a ScaffoldingReadme.txt file is created in the project directory. This file contains general instructions for completing the Identity scaffolding update.
There are many different ways to run the Identity scaffolder. For this lesson, we will run the scaffolder to generate ASP.NET Identity source code into an MVC project with authorization (the type of project created for this lesson). Allow the scaffolder to override all of the code in the RCL, and let it build a new data context class as well as a new user data class to easily explore these additions.
Run the Identity Scaffolder
From Solution Explorer, right-click on the project (MySecureApp), then select Add > NewScaffolded Item.
From the left pane of the Add Scaffold dialog, select Identity, then click the Add button.
In the ADD Identity dialog:
Select your existing layout page. Leave this blank since the layout page is already set in the _ViewStart.cshtml file.
Select all of the options you wish to include. These options give you granular control over the way user accounts are managed in your application. For simplicity’s sake, check the box labeled Override all files.
At the data context drop-down, click the plus sign on the right. You should see the following:
Click the Add button. You now have a new data context class that represents an object-based version of your application’s database.
Now click the plus sign (+) to the right of the User Class drop-down:
Click Add. You now have a new user class called MySecureAppUser. This class represents all user accounts in your application. We’ll come back to these two classes in the chapter. For now, let's finish the scaffolding process and look at a few of the files it creates.
Click Add again (at the bottom of the Add Identity window).
The scaffolding process may take a minute or two to complete. When it does, you can view the added code in the Solution Explorer. Select Areas > Identity > Pages > Account.
Now let’s examine three pieces of code: register, login, and logout.
Register, Login, Logout
Locate Register.cshtml under the Account pages folder and double-click to open it. This is the view that is used to render the user registration page. Notice in the Solution Explorer the arrow next to Register.cshtml. Click the arrow to expand Register.
You will now see Register.cshtml.cs as an option beneath the Register page. This file has the RegisterModel
class, which contains the data model and controller actions that manage the Register page. Double-click Register.cshtml.cs to open this file.
Lines 16 - 63 of this file set up the data model for the registration page. Beginning at Line 65, you see the OnPostAsync
method. This method handles the creation of a new user account from the data supplied by the user and stored in the data attributes of the RegisterModel
class. This is also where you can customize the user account creation process. For now, we won't modify this process, but you’ll want to remember this as you’ll need it for a project later in the course.
Take a few minutes and do the same with Login.cshtml and Logout.cshtml. Familiarize yourself with the code in views and the data models for each of these three pages. Later in the course, you will be modifying the look and feel of the pages as well as customizing the registration and login processes, so you’ll want to at least be familiar with the existing code.
Let’s Recap!
In this chapter:
You created a .NET MVC project with individual user accounts in Visual Studio.
You customized the Identity configuration and user settings, including password criteria and lockout parameters.
You enabled ASP.NET Identity.
You added ASP.NET Identity to an existing project through scaffolding, including the addition of Register, Login, and Logout pages and functions.
In the next chapter, you're going to learn how to authenticate users using Entity Framework. You’ve already done a little bit of this in the MVC class, but here you’ll gain a much greater appreciation for the simplicity of an otherwise complicated process.