Can the Database Be Customized?
You’ve just built a simple .NET Core web application and scaffolded Identity into it. As it stands, that application can authenticate users with Entity Framework. It doesn’t need anything else at all to complete that process. However, let’s look more closely at the database that has been created to store your user data and how you can personalize it and make it applicable to a specific application.
Run your application and register a new user. Then do the following:
In Visual Studio, click the View menu and select SQL Server Object Explorer (SSOX).
Under SQL Server in the SSOX, locate the local database, noted as (localdb)\MSSQLLocalDB.
Expand the local database and the Databases folder within it.
Find your project, then expand the Tables folder.
Here you’ll see several tables that have been created for you by Identity and Entity Framework to drive your application. Each of these tables begins with the prefix AspNet. We’ll talk about what these are later, but for now, look at AspNetUsers. This table contains the individual user accounts you’ve registered. Right-click on AspNetUsers, and choose View Data from the pop-up menu.
When you select View Data from the pop-up menu, you see a window similar to the one shown here:
This shows the contents of the AspNetUsers table. The user I registered using the email myclass@openclassrooms.com is displayed. This table, and all entries in it, are constructed by Entity Framework in conjunction with Identity to create secure user account management.
Go back to the SQL Server Object Explorer again. Expand the AspNetUsers table, and then expand the Columns folder beneath it.
Here you see a list of all the column definitions in the AspNetUsers table. These are the standard properties for every user account created in this manner. But we mentioned something about personalizing these accounts, didn’t we?
Personalize User Data
Remember those two classes we scaffolded at the end of the last chapter? We said we’d get back to those later. Let's take a look at them now.
Add Personal Data to the User Class
Locate and click on Areas > Identity > Data, as shown below:
You’ll see those two classes here: MySecureAppDataContext
and MySecureAppUser
.
There’s nothing special about the data context class yet. It’s completely generic. We want to look at the MySecureAppUser
class. Double-click on it to open it in the editor. Your file should look like this:
Below is an accessible version of the code you should see in your file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace MySecureApp.Areas.Identity.Data
{
// Add profile data for application users by adding properties to the MySecureAppUser class
// 66 references
public class MySecureAppUser : IdentityUser
{
}
}
Not much here, right? Just an empty class. The vital characteristic of this class is that it extends the IdentityUser class, which is the base class Identity and Entity Framework used to build that secure user accounts. So now, it’s time to start personalizing those accounts. You do that in this class, so let’s get started!
By default, your users don’t have any names. You don’t want them to be entirely anonymous, so go ahead and add properties for first and last names.
When you’re finished, your modified class should look something like this:
Below is an accessible version of the code you should see in your file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace MySecureApp.Areas.Identity.Data
{
// Add profile data for application users by adding properties to the MySecureAppUser class
// 66 references
public class MySecureAppUser : IdentityUser
{
// 0 references | 0 exceptions
public string FirstName { get; set; }
// 0 references | 0 exceptions
public string LastName { get; set; }
|
}
}
Now go back and add the attribute [PersonalData]
above each of these properties.
What exactly does adding the [PersonalData]
attribute accomplish? Properties that you decorate with this attribute receive some special treatment. They are:
Deleted when the Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml Razor Page calls
UserManager.Delete
.Included in the downloaded data by the Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml Razor Page.
Modify Pages and Actions to Accommodate the New Data
First you need to update the InputModel
class found in Areas/Identity/Pages/Account/Manage/Index.cshtml.cs.
Locate and open the file.
Scroll down and find the
InputModel
class (about Line 41).Add properties to the model to match the first and last name properties you added to the user class.
Below is an accessible version of the code you should see in your file:
public class Inputmodel { [Required] [EmailAddres] // 3 references | 0 exceptions public string Email { get; set; } [Phone] [Display(Name = "Phone number")] // 3 references | 0 exceptions public string PhoneNumber { get; set; } [Display(Name = "First name")] // 0 references | 0 exceptions public string FirstName { get; set; } [Display(Name= "Last name")] // 0 references | 0 exceptions public string LastName { get; set; } }
Scroll down and locate where the
InputModel
class is populated with user data in theOnGetAsync
method. Add the code to assign the values from the user class to the properties in theInputModel
class.Below is an accessible version of the code you should see in your file:
var userName = await _userManager.GetUserNameAsync(user); var email = await _userManager.GetEmailAsync(user); var phoneNumber = await _userManager.GetPhoneNumberAsync(user); Username = userName; Input = new InputModel { Email = email, PhoneNumber = phoneNumber FirstName = user.FirstName, LastName = user.LastName }; IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); return Page(); }
Now scroll down to the
OnPostAsync
method and find where the user object is retrieved from theUserManager
(about Line 92).Add code here to assign the data values from the posted model to the corresponding properties in the user object.
Below is an accessible version of the code you should see in your file:
if (!ModelState.IsValid) { return Page(); } var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } if (Input.FirstName != user.FirstName) { user.FirstName = Input.FirstName; } if (Input.LastName != user.LastName) { user.LastName = Input.LastName; }
You need to make one more change. At the bottom of the method, right before the user’s login is refreshed, you need to update the user object. This is done with a call to the UserManager:
await _userManager.UpdateAsync(user);
. Add this line as shown below:Below is an accessible version of the code you should see in your file:
var phoneNumber = await _userManager.GetPhoneNumberAsync(user); if (Input.PhoneNumber != phoneNumber) { var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); if (!setPhoneResult.Succeeded) { var userId = await _userManager.GetUserIdAsync(user); throw new InvalidOperationException($"Unexpected error occurred setting phone number."); } } await _userManager.UpdateAsync(user); await _signInManager.RefreshSignInAsync(user); StatusMessage = "Your profile has been updated"; return RedirectToPage();
Next you need to add form elements to the pages that use the InputModel. Open the Areas/Identity/Pages/Account/Manage/Index.cshtml page and scroll down to the form group element that receives the user’s phone number (about Line 34). Insert the form elements into the form group as shown below:
Below is an accessible version of the code you should see in your file:
<div class="form-group">
<label asp-for="Input.FirstName"></label>
<input asp-for="Input.FirstName" class="form-control" />
<label asp-for="Input.LastName"></label>
<input asp-for="Input.LastName" class="form-control" />
<label asp-for="Input.PhoneNumber"></label>
<input asp-for="Input.PhoneNumber" class="form-control" />
<span asp-validation-for="Input.PhoneNumber" class="text-danger"></span>
</div>
Code Exercise: Update the Account/Register.cshtml page
You’ve just made the necessary modifications to the InputModel class and to the page at Areas/Identity/Pages/Account/Manage/Index.cshtml. Using what you’ve just learned, update the Register page and its input model to accept the two name fields you added to the user object.
Code Exercise: Update the Database and Test Your New Code
When you’ve completed the first exercise, open the Package Manager Console, and add a new migration by entering the following command in the console window:
Add-Migration CustomUserData
Then update the database:
Update-Database
When the update is complete, test your application.
Register a new user.
View your newly created user data on the /Identity/Account/Manage page.
View the user’s personal data on the /Identity/Account/Manage/PersonalData page.
In the next section, we’ll explore how to use third-party external accounts to register and authenticate users.
Let’s Recap!
At this point, you have learned how to:
Customize a user class to include additional personal data.
Modify views and controllers to accommodate changes to user data models.
Register and authenticate users in an MVC application with Entity Framework.
Now that you have a solid understanding of authentication using Entity Framework in an MVC application, let’s discuss how you can authenticate users using external accounts.