Introduction to forms based authentication in ASP.NET MVC5 Part 4

Introduction

In the previous part of this series we looked at some key objects around the new Identity packages in .NET. We also identified the EntityFramework database context object which can be extended with your own entities. By default it only contains User-related entities. Then we tried to extend the default implementation of IUser, i.e. ApplicationUser which derives from the EF entity IdentityUser. We failed at first as there’s no mechanism that will update our database on the fly if we make a change to the underlying User model.

We’ll continue working on the same demo project as before so have it ready in Visual Studio 2013.

Database migrations and seeding

As hinted at in the previous post we’ll look at EntityFramework 6 and DB migration in the series after this one but we’ll need to solve the current issue somehow.

Open the Package Manager Console in Visual Studio: View, Other Windows, Package Manager Console. Run the following command:

Enable-Migrations

This is an EntityFramework specific command. After some console output you should see the following success message:

Code First Migrations enabled for project [your project name]

Also, a couple of new items will be created for you in VS. You’ll see a migration script in a new folder called Migrations. The migration script will have a name with a date stamp followed by _InitialCreate.cs. Also in the same folder you’ll see a file called Configuration.cs. An interesting property is called AutomaticMigrationsEnabled. It is set to false by default. If it’s set to true then whenever you change the structure of your entities then EF will reflect those in the database automatically. In an alpha testing environment this is probably a good idea but for the production environment you’ll want to set it to false. Set it to true for this exercise.

Another interesting element in Configuration.cs is the Seed method. The code is commented out but the purpose of the method is to make sure that there’s some data in the database when the database is updated. E.g. if want to run integration tests with real data in the database then you can use this method to populate the DB tables with some real data.

There are at least two strategies you can follow to populate the User database within the Seed method. The traditional EF context approach looks like this:

PasswordHasher passwordHasher = new PasswordHasher();
context.Users.AddOrUpdate(user => user.UserName
	, new ApplicationUser() { UserName = "andras", PasswordHash = passwordHasher.HashPassword("hello") });
context.SaveChanges();

We use PasswordHasher class built into the identity library to hash a password. The AddOrUpdate method takes a property selector where we define which property should be used for equality. If there’s already a user with the username “andras” then do an update. Otherwise insert the new user.

Another approach is to use the UserManager object in the Identity.EntityFramework assembly. Insert the following code into the Seed method:

if (!context.Users.Any(user => user.UserName == "andras"))
{
	UserStore<ApplicationUser> userStore = new UserStore<ApplicationUser>(context);
	UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(userStore);
	ApplicationUser applicationUser = new ApplicationUser() { UserName = "andras" };
	userManager.Create<ApplicationUser>(applicationUser, "password");
}

We first check for the presence of any user with the username “andras” with the help of the Any extension method. If there’s none then we build up the UserManager object in a way that’s now familiar from the AccountController constructor. We then call the Create method of the UserManager and let it take care of the user creation behind the scenes.

Go back to the Package Manager Console and issue the following command:

Update-Database

The console output should say – among other things – the following:

Running Seed method.

Open the database file in the App_Data folder and then check the contents of the AspNetUsers table. The new user should be available and the table also includes the FavouriteProgrammingLanguage column:

User data migration success

Run the application and try to log in with the user you’ve just created in the Seed method. It should go fine. Then log off and register a new user and provide some programming language in the appropriate field. Then refresh the database in the Server Explorer and check the contents of AspNetUsers. You’ll see the new user with there with their favourite language.

If you’d like to create roles and add users to roles in the seed method then it’s a similar process:

string roleName = "IT";
RoleStore<IdentityRole> roleStore = new RoleStore<IdentityRole>(context);
RoleManager<IdentityRole> roleManager = new RoleManager<IdentityRole>(roleStore);
roleManager.Create(new IdentityRole() { Name = roleName });
userManager.AddToRole(applicationUser.Id, roleName);

Third party identity providers

We looked at Start.Auth.cs briefly in a previous part of this series. By default most of the code is commented out in that file and only the traditional login form is activated. However, if you look at the inactive code bits then you’ll see that you can quite quickly enable Microsoft, Twitter, Facebook and Google authentication.

You can take advantage of these external providers so that you don’t have to take care of storing your users’ passwords and all actions that come with it such as updating passwords. Instead, a proven and reliable service will tell your application that the person trying to log in is indeed an authenticated one. Also, your users won’t have to remember another set of username of password.

In order to use these platforms you’ll need to register your application with them with one exception: Google. E.g. for Facebook you’ll need to go to developers.facebook.com, sign in and register an application with a return URL. In return you’ll get an application ID and a secret:

Facebook application ID and secret

The Azure mobile URL is the callback URL for an application I registered before on each of those 4 providers. I hid the application ID and application secret values.

For Twitter you’ll need to go to dev.twitter.com and register your application in a similar fashion. You’ll get an API key and an API secret:

Twitter application keys

These providers will use OAuth2 and OpenId Connect to perform the login but the complex details are hidden behind the Katana extensions, like app.UseMicrosoftAccountAuthentication.

As you activate the external providers there will be new buttons on the Log in page for them:

External providers login buttons

Upon successful login your web app will receive an authentication token from the provider and that token will be used in all subsequent communication with your website. The token will tell your website that the user has authenticated herself along with details such as the expiry date of the token and maybe some user details depending on what your web app has requested.

As mentioned before there’s one exception to the client ID / client secret data requirement: Google. So comment out…

app.UseGoogleAuthentication();

…and run the application. Navigate to the Log in page and press the Google button. You’ll be directed to the standard Google login page. If you’re already logged on with Google then this step is skipped. Next you’ll be shown a consent page where you can read what data “localhost” will be able to read from you:

Google consent screen

If you implement the other providers at a later point then they’ll follow the same process, i.e. show the consent screen. Normally you can configure the kind of data your application will require from the user on the individual developer websites mentioned above.

Click Accept and then you can complete the registration in the last step:

Confirm registration with Google

This is where you create a local account which will be stored in the AspNetUserLogins table. Press Register and there you are, you’ve registered using Google.

Check the contents of AspNetUserLogins:

Google login data in DB

Also, the user is stored in the AspNetUsers table:

Google user

You’ll see that the password is not stored anywhere which is expected. The credentials are stored in the provider’s database.

Read the last part in this series here.

You can view the list of posts on Security and Cryptography here.

Advertisements

Introduction to forms based authentication in ASP.NET MVC5 Part 2

Introduction

In the previous part of this series we looked at the absolute basics of Forms Based Authentication in MVC5. Most of what we’ve seen is familiar from MVC4.

It’s time to dive into what’s behind the scenes so that we gain a more in-depth understanding of the topic.

We’ll start with the database part: where are users stored by default and in what form? We created a user in the previous post so let’s see where it had ended up.

Demo

Open the project we started building previously.

By default if you have nothing else specified then MVC will create a database for you in the project when you created your user – we’ll see how in the next series devoted to EntityFramework. The database is not visible at first within the project. Click on the Show All Files icon in the solution explorer…:

Show all files icon in solution explorer

…and you’ll see an .mdf file appear in the App_Data folder:

Database file in App_Data folder

The exact name will differ in your case of course. Double-click that file. The contents will open in the Server Explorer:

Membership tables in MVC 5

Some of these tables might look at least vaguely familiar from the default ASP.NET Membership tables in MVC4. However, you’ll see that there are a lot fewer tables now so that data is stored in a more compact format. A short summary of each table – we’ll look at some of them in more detail later:

  • _MigrationHistory: used by EntityFramework when migrating users – migrations to be discussed in the next series
  • AspNetRoles: where the roles are stored. We have no roles defined yet so it’s empty
  • AspNetUserClaims: where the user’s claims are stored with claim type and claim value. New to claims? Start here.
  • AspNetUserLogins: used by external authentication providers, such as Twitter or Google
  • AspNetUserRoles: the many-to-many mapping table to connect users and roles
  • AspNetUsers: this is where all site users are stored with their usernames and hashed passwords

As you can see the membership tables have been streamlined a lot compared to what they looked like in previous versions. They have a lot fewer columns and as we’ll see later they are very much customisable with EntityFramework.

Right-click the AspNetUsers table and select Show Table Data. You’ll see the user you created before along with the hashed password.

Database details

Go back to the Solution explorer and open up web.config. Locate the connectionStrings section. That’s where the default database connection string is stored with that incredibly sexy and easy-to-remember name. So the identity components of MVC5 will use DefaultConnection to begin with. We can see from the connection string that a local DB will be used with no extra login and password.

You can in fact change the connection string to match the real requirements of your app of course. The SQL file name is defined by the AttachDbFilename parameter. The Initial Catalog parameter denotes the database name as it appears in the SQL management studio. Change both to AwesomeDatabase:

AttachDbFilename=|DataDirectory|\AwesomeDatabase.mdf;Initial Catalog=AwesomeDatabase;

Run the application. Now two things can happen:

  • If you continued straight from the previous post of the series then you may still be logged on – you might wonder if the app is still using the old database, but it’s not the case. The application has picked up the auth cookie available in the HTTP request. In this case press Log Off to remove that cookie.
  • Otherwise you’re not logged in and you’ll see the Register and Log in links

Try to log in, it should fail. This is because the new database doesn’t have any users in it yet and the existing users in the old database haven’t been magically transported. Stop the application, press Refresh in Solution Explorer and you’ll see the new database file:

New database created automatically

Communication with the database

We’ve seen where the database is created and how to control the connection string. Let’s go a layer up and see which components communicate with the database. Identity data is primarily managed by the Microsoft.AspNet.Identity.Core NuGet library. It is referenced by the any MVC5 app where you run forms based authentication. It contains – among others – abstractions for the following identity-related elements:

  • IUser with ID and username
  • IRole with ID and role name
  • IUserStore: interface to abstract away various basic actions around a user, such as creating, deleting, finding and updating a user
  • IUserPasswordStore which implements IUserStore: interface to abstract away password related actions such as getting and setting the hashed password of the user and determining – on top all functions of an IUserStore

There’s also a corresponding abstraction for storing the user roles and claims and they all derive from IUserStore. IUserStore is the mother interface for a lot of elements around user management in the new Identity library.

There are some concrete classes in the Identity.Core library, such as UserManager and RoleManager. You can see the UserManager in action in various methods of AccountController.cs:

var result = await UserManager.CreateAsync(user, model.Password);
IdentityResult result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
IdentityResult result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);

The default UserManager is set in the constructor of the AccountController object:

public AccountController()
           : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}

public AccountController(UserManager<ApplicationUser> userManager)
{
     UserManager = userManager;
}

public UserManager<ApplicationUser> UserManager { get; private set; }

You see that we supply a UserStore object which implements a whole range of interfaces:

  • IUserLoginStore
  • IUserClaimStore
  • IUserRoleStore
  • IUserPasswordStore
  • IUserSecurityStampStore
  • IUserStore

So the default built-in UserManager object will be able to handle a lot of aspects around user management: passwords, claims, logins etc. As a starting point the UserManager will provide all domain logic around user management, such as validation, password hashing etc.

In case you want to have your custom solution to any of these components then define your solution so that it implements the appropriate interface and then you can plug it into the UserManager class. E.g. if you want to store your users in MongoDb then implement IUserStore, define your logic there and pass it in as the IUserStore parameter to the UserManager object. It’s a good idea to implement as many sub-interfaces such as IUserClaimsStore and IUserRoleStore as possible so that your custom UserStore that you pass into UserManager will be very “clever”: it will be able to handle a lot of aspects around user management. And then when you call upon e.g. UserManager.CreateAsync then UserManager will pick up your custom solution to create a user.

However, if you’re happy with an SQL server solution governed by EntityFramework then you may consider the default setup and implementations inserted by the MVC5 template. We’ll investigate those in the next post.

You can view the list of posts on Security and Cryptography here.

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: