Introduction to forms based authentication in ASP.NET MVC5 Part 5: Claims

Introduction

Claims in authorisation have received a lot of attention recently. Claims are simply key-value pairs where the key describes the type of claim, such as “first name” and the value provides the value of that claim, e.g. “Elvis”. Think of a passport which usually has a page with the photo and lots of claims: first name, last name, maiden name, expiry date etc. Those are all key-value pairs that describe the owner of the passport and provide reliable evidence that the person is really the one they are claiming to be.

I have a long series on claims on this blog. If you don’t anything about them then I recommend you at least go through the basics starting here. That series takes up claims in MVC 4. MVC 5 brings a couple of new features as far as claims are concerned.

Demo

We’ll be using the same demo application as before in this series so have it open in Visual Studio 2013.

As we said before authentication in MVC 5 is built using Katana components which are activated with extension methods on the incoming IAppBuilder object. These Katana components work independently of each other: e.g. you can turn on Google authentication at will as we saw in the previous part. At the same time you can even turn off the traditional cookie authentication, it won’t affect the call to app.UseGoogleAuthentication().

When we turned on Google authentication the consent screen stated that the application, i.e. the demo web app, will have access to the user’s email. That’s in fact a type of claim where the key is “email” or most probably some more complete namespace. Where’s that email? How can we read it?

Locate the method called ExternalLoginCallback in the AccountController. That method is fired when the user has successfully logged in using an external auth provider like Google or Facebook. The method will first need to extract the login information using…

var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();	

This login information contains the tokens, expiry dates, claims etc. As the login is taken care of by OWIN, MVC will need to tap into the corresponding Katana component to read those values. This is an example of a piece of logic outside OWIN that needs to find out something from it. The AccountController will need this information to handle other scenarios such as signing out. The key object here is the AuthenticationManager.

The AuthenticationManager which is of type IAuthenticationManager is extracted using the following private property accessor in AccountController:

private IAuthenticationManager AuthenticationManager
{
            get
            {
                return HttpContext.GetOwinContext().Authentication;
            }
}

The interface lies in the Microsoft.Owin.Security namespace. The AuthenticationManager is retrieved from the current OWIN context. The GetOwinContext extension method provides a gateway into OWIN from ASP.NET. It allows you to retrieve the request environment dictionary we saw in the series on OWIN:

HttpContext.GetOwinContext().Request.Environment;

I encourage you to type ‘HttpContext.GetOwinContext().’ in the editor and look through the available choices with IntelliSense. The Authentication property exists so that ASP.NET can read authentication related information from the OWIN context as all that is now handled by Katana components.

If you look further down in the code you’ll see the SignInAsync method. The method body shows how to sign users in and out using an external cookie-based login:

AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);

Now that we know a little bit about the authentication manager we can return to the ExternalLoginCallback method. Let’s see what the GetExternalLoginInfoAsync method returns in terms of login information. Add a breakpoint within the method and start the application. Click the Log in link and sign in with Google. Code execution stops within the method. Inspect the contents of the “loginInfo” variable. It doesn’t contain too much information: the user name, the provider name – Google – and a provider key. So there’s nothing about any provider specific related claims such as the user’s email address.

Note that the GetExternalLoginInfoAsync method only provides an object which includes properties common to all providers. The list is not too long as we’ve seen. However, the method cannot know in advance what the Google auth provider will provide in terms of claims. The list of available data will be different across providers. Some providers may provide a more generous list of claims than just an email: a URL to the user’s image, the user’s contacts, first and last names etc. Insert the following line of code above the call to GetExternalLoginInfoAsync:

AuthenticateResult authenticateResult = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);

Leave the breakpoint as it is and restart the application. Sign in with Google and inspect the authenticateResult variable. You’ll see that it provides a lot more information than the login info above. The claims are found
within the Identity property:

Claims from Google authentication

You can see that the claim types are identified in the standard URI way we saw in the series on claims. You can query the Claims collection to see if it includes the email claim. If your application explicitly requires the email address of the user then make sure to indicate it when you set it up with Google or any other auth provider.

You can save the email of the user in at least two ways:

  • Temporarily in the session using the ExternalLoginConfirmationViewModel object further down in the ExternalLoginCallback method. That view model doesn’t by default include any property for emails, you’ll need to extend it
  • In the database using the UserManager object we saw before in this series

Let’s see how we can achieve these. Locate the ExternalLoginConfirmationViewModel object in AccountViewModels.cs and extend it as follows:

public class ExternalLoginConfirmationViewModel
{
        [Required]
        [Display(Name = "User name")]
        public string UserName { get; set; }

	public string EmailFromProvider { get; set; }
}

Add the following method to AccountController.cs to read the email claim from the claims list:

private string ExtractEmailFromClaims(AuthenticateResult authenticateResult)
{
	string email = string.Empty;
	IEnumerable<Claim> claims = authenticateResult.Identity.Claims;
	Claim emailClaim = (from c in claims where c.Type == ClaimTypes.Email select c).FirstOrDefault();
	if (emailClaim != null)
	{
		email = emailClaim.Value;
	}
	return email;
}

You can read this value in the body of ExternalLoginCallback and store it in a ExternalLoginConfirmationViewModel like this:

public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
	AuthenticateResult authenticateResult = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);

        var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();			
         if (loginInfo == null)
            {
                return RedirectToAction("Login");
            }

            // Sign in the user with this external login provider if the user already has a login
            var user = await UserManager.FindAsync(loginInfo.Login);

			string emailClaimFromAuthResult = ExtractEmailFromClaims(authenticateResult);

            if (user != null)
            {
                await SignInAsync(user, isPersistent: false);
                return RedirectToLocal(returnUrl);
            }
            else
            {
                // If the user does not have an account, then prompt the user to create an account
                ViewBag.ReturnUrl = returnUrl;
                ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
                return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel 
					{ UserName = loginInfo.DefaultUserName, EmailFromProvider = emailClaimFromAuthResult });
     }
}

This page redirects to a View called ExternalLoginConfirmation when the user first signs up in the “else” clause. Locate ExternalLoginConfirmation.cshtml in the Views/Account folder. You can use the incoming model to view the extracted email claim:

<p>
    We've found the following email from the login provider: @Model.EmailFromProvider
</p>

I’ve deleted all rows in the AspNetUsers table so that I can view this extra information. Also, clear the cookies in your browser otherwise Google will remember you. You can also run the application from a brand new browser window. The email was successfully retrieved:

Found Google email claim

We can store the email claim in the database within ExternalLoginCallback as follows:

if (user != null)
{
        await SignInAsync(user, isPersistent: false);
        IList<Claim> userClaimsInDatabase = UserManager.GetClaims<ApplicationUser>(user.Id);
	Claim emailClaim = (from c in userClaimsInDatabase where c.Type == ClaimTypes.Email select c).FirstOrDefault();
	if (emailClaim == null)
	{
        	IdentityResult identityResult = UserManager.AddClaim<ApplicationUser>(user.Id, new Claim(ClaimTypes.Email, emailClaimFromAuthResult));
	}
				
        return RedirectToLocal(returnUrl);
}

First we check the claims stored in the database using the UserManager.GetClaims method. Then we check if the email Claim is present. If not then we add it to the database. The identityResult helps you check the result of the operation through the Errors and Succeeded properties.

The claims by default end up in the AspNetUserClaims table:

Email claim saved in the aspnetuserclaims table

You can of course use the AspNetUserClaims table to store any kind of claim you can think of: standard claims found in the ClaimTypes list or your own custom ones, such as http://mycompany.com/claims/customer-type.

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

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.

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

Introduction

In the previous post we started digging into the components around Identity in MVC5. We looked at the default database and the Microsoft.AspNet.Identity.Core. We also saw how the MVC5 template with Forms based authentication creates a default UserManager class which can handle user creation, login, updates, claims etc.

We’ll continue our discussion with some other components around Identity in an MVC5 project.

We’ll build on the demo application from the previous two parts of this series.

EntityFramework

EntityFramework is the default choice for storing users in an MVC5 project. As far as identity is concerned EF is encapsulated in the Microsoft.AspNet.Identity.EntityFramework package. Do you recall the interfaces around user management in the Core assembly? IUserStore, IUserLoginStore, IUserClaimsStore etc. The EntityFramework assembly provides the out-of-the-box concrete implementation of each of those abstractions. Examples:

  • IdentityUser implements IUser
  • IdentityRole implements IRole
  • UserStore implements IUserStore, IUserLoginStore, IUserClaimsStore etc., so it contains a large amount of methods and properties which are implementations of the interfaces

IdentityUser and IdentityRole are entities that depend on EF for their persistence mechanism. Recall the database that MVC5 created for us in the local DB. Those are the DB representations of these identities. In AccountController.cs this UserStore is passed to the constructor of UserManager which in turn is passed into the constructor of AccountController. The UserManager will be responsible for all user-related operations through the UserStore implementation: insert, update, delete, add to role, read claims etc. If you navigate to AccountController.cs then you’ll see that the type of the user in the type definition is ApplicationUser. So what’s IdentityUser? If you locate the ApplicationUser class in Models/IdentityModels.cs then you’ll see that it derives from IdentityUser.

You can use the ApplicationUser class to extend the functionality of the default IdentityUser entity in EF with your custom properties.

There’s an additional important object in IdentityModels.cs: ApplicationDbContext which derives from IdentityDbContext of ApplicationUser. IdentityDbContext in turn derives from DbContext which is the standard object context in EntityFramework. Hence ApplicationDbContext is also a DbContext but it has access to the User related information that IdentityDbContext carries. We said that Users and Roles are entities so it’s just as well that ApplicationDbContext gets access to them. The constructor of the ApplicationDbContext object defines the name of the connection string, which is DefaultConnection. We saw this in the previous post but now we know where it is defined. In case you’ve renamed your connection string in web.config then you need to rename it here too.

ApplicationDbContext is also where you can add your custom DbSets so that EF can create the tables for you. We’ll go into EntityFramework in the series after this one so let’s not dive into that topic too much. It suffices to say the if you have a Customer domain then you can add a DbSet of Customer like this directly in ApplicationDbContext:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }

	public DbSet<Customer> Customers { get; set; }
}

If you go back to AccountController.cs you’ll notice that an ApplicationDbContext object is passed into the constructor of UserStore. Now we know that ApplicationDbContext derives from IdentityDbContext. IdentityDbContext is the EF object context which will carry out the actual DB operations of SELECT, UPDATE, DELETE etc. If you’re familiar with EF then this object context class will sound familiar.

You’ll also notice the TUser generic type argument. The actual type provided in all cases is ApplicationUser. You can define your own user type if you like but the following definitions force your implementation to implement IUser:

public class UserStore<TUser> : IUserLoginStore<TUser>, IUserClaimStore<TUser>, IUserRoleStore<TUser>, IUserPasswordStore<TUser>, IUserSecurityStampStore<TUser>, IUserStore<TUser>, IDisposable where TUser : global::Microsoft.AspNet.Identity.EntityFramework.IdentityUser

public class UserManager<TUser> : IDisposable where TUser : global::Microsoft.AspNet.Identity.IUser

I think it’s fine to have that constraint as IUser is an abstraction with minimal content:

string Id { get; }
string UserName { get; set; }

IdentityDbContext also provides the mapping between the entity classes and their DB representations. It also ensures that the tables are properly created when they are needed for the first time.

In summary we can say the Microsoft.AspNet.Identity.EntityFramework library provides the EntityFramework implementation of the abstractions in the Microsoft.AspNet.Identity.Core library. Let’s look at the concrete classes in some more detail.

First go at customisation

We’ve discussed the UserManager in some detail in the previous and this post. The Login action is represented by two methods in AccountController.cs:

[AllowAnonymous]
public ActionResult Login(string returnUrl)

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)

The GET action method on top is fired when the user navigates to the Login page with the login form. The upon pressing the Log in button the POST action is invoked which accepts the LoginViewModel which in turn has 3 properties:

public string UserName { get; set; }
public string Password { get; set; }
public bool RememberMe { get; set; }

You’ll see that these are populated from the form. The POST Login method is the more interesting one as far as Identity is concerned. In the body of the method the UserManager will try to find the ApplicationUser with the FindAsync method. If the user exists, i.e. it is not null, then she is signed in and redirected to the return url. Otherwise the ModelState is invalidated.

Let’s see how we can add our custom property to the ApplicationUser object. Locate the object and add the following property:

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

We’ll need to extend the corresponding view model if we want to collect this information from the user. Locate RegisterViewModel in Models/AccountViewModel.cs. It will have 3 fields: username, password and confirm password. Add a 4th one:

[Required]
[Display(Name="Favourite programming language")]
[DataType(DataType.Text)]
public string FavouriteProgrammingLanguage { get; set; }

Next open Register.cshtml in the Views/Account folder. You’ll see a form-group div where the user has to confirm the password. Just underneath add a new div:

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

Back in AccountController.cs locate the POST Register method that accepts a RegisterViewModel object. You’ll see the following bit of code after the if statement:

var user = new ApplicationUser() { UserName = model.UserName };

Extend the object as follows:

var user = new ApplicationUser() { UserName = model.UserName, FavouriteProgrammingLanguage = model.FavouriteProgrammingLanguage };

Let’s run the app and see what happens. Don’t log in with the user you created before. Instead, click on the Register link. The Register page should show the new text field. Create a new user, provide a programming language and press the Register button and.. …the yellow screen of death appears:

Yellow screen of death for code first migration

We’ve changed the entity structure but the database isn’t recreated for us on the fly of course. We’ll rectify this problem in the next post which will discuss DB migrations with EF in the User data context. We’ll revisit migrations in a greater detail in a planned series on EntityFramework which will follow this series.

The new field needs to be included elsewhere like on the Manage.cshtml view for completeness, but you can do that as an exercise.

Read the next post in this series here.

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

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.

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

Introduction

ASP.NET MVC5 comes with a number of new elements regarding user management and security. When you create a new MVC 5 web app you’ll be able to choose between 4 default authentication types:

  • No authentication, i.e. anonymous users can access your site
  • Individual user accounts: the traditional way to log onto the site using a login form. The user store is backed by SQL identity tables. You can also enable some well known auth providers: Twitter, Google, Facebook and Microsoft where you don’t need to worry about the password
  • Organisational accounts: Active Directory Federation Services (ADFS), used within organisations that manage their users in ADFS, typically coupled with Single SignOn for the applications within the organisation. You can enable Azure-based ADFS as well
  • Windows auth: Windows Active Directory authentication for intranet apps. Your Windows login credentials will be used to access the internal applications of your company. This is somewhat like a stripped down version of the organisational accounts option

In this blog series we’ll look at the new identity features of MVC5.

Forms based authentication

Fire up Visual Studio 2013 and select the ASP.NET Web Application template in the New Project window. Give the project some name and click OK. A new window will open where you can select additional templates. Pick MVC. Press the ‘Change authentication’ button and make sure that ‘Individual user accounts’ is selected:

Individual user accounts option

Visual Studio will set up a forms-enabled MVC app for you without any extra effort. Run the web app and you’ll be directed to the default home page. You’ll see that it comes with the Register and Log in links:

Register and log in links in MVC 5 web app

If you used Forms based auth in MVC 4 then this is of course no surprise to you. Click Register and create a user. If everything goes well you’ll be automatically logged in and you’ll see your username instead of “Register”:

User registered and logged in

You can click on “Hello (your name)” to manage save your password if you want. Click Log off and then log in again with your credentials to check if it works fine. It should.

The Layout view

The top menu of the MVC 5 template is controlled by _Layout.cshtml in the Views/Shared folder. Open that file. You’ll see the links for Home, About and Contact. Below those links you’ll have a partial view called _LoginPartial. _LoginPartial is located in the same folder. Open it and let’s see what it contains.

There’s an if-else statement which tweaks what the user sees based on the Request.IsAuthenticated property. This property is set by the framework depending on whether the current user has logged on or not.

The user name is extracted using the User.Identity.GetUserName() method. User is an IPrincipal object and represents the currently logged-on user. Identity is the IIdentity belonging to the user which contains a small set of information about the user such as the user name or the authentication type. User.Identity is also set by the framework just like with the IsAuthenticated property.

You can read the User object anywhere within the controllers and views, i.e. where a valid HTTP session is available. Open Controllers/HomeController.cs and add the following code to Index() just above the return statement:

string userName = User.Identity.Name;

Set a breakpoint within Index and run the application. You’ll see that the username can be easily extracted this way.

Restrict access

There’s little point in authenticating users if you don’t limit the access to certain parts of your website to authenticated users only.

Right-click the Controllers folder and select Add, Controller. In the Add Scaffold window select the topmost option, i.e. Empty. Call it CustomersController. Add the following two methods to the controller:

public string SecretName()
{
	return "This is the secret customer name.";
}

public string PublicName()
{
	return "This is the public customer name.";
}

The goal is to protect access to the SecretName action and only let anonymous users read the public name.

Run the application and log off if you’re still logged in. Then navigate to the above actions:

  • localhost:xxxx/Customers/publicname
  • localhost:xxxx/Customers/secretname

Not surprisingly you can access both methods without having to log in first.

If your only requirement is to restrict access to a controller action to authenticated users only then you can use the Authorize attribute like this:

[Authorize]
public string SecretName()
{
	return "This is the secret customer name.";
}

Re-run the app and navigate to the secretname action. You should see that you’re redirected to the login page. Log in and you’ll see the secret. Note the URL of the Login page: Account/Login. It is defined in a Katana component in Startup.Auth.cs in the App_Start folder:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
          AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
          LoginPath = new PathString("/Account/Login")
});

If you don’t know what OWIN and Katana mean then you can start here.

The ReturnUrl query string in the URL will store which controller and action you’ve tried to access. It will be fed to the POST Login action of AccountController:

public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)

Now log off and navigate to /Customers/publicname. It should still be available to anonymous users.

What if you want to restrict access to all actions within the controller? You can apply the Authorize attribute on the controller level:

[Authorize]
public class CustomersController : Controller

Run the app again and navigate to /Customers/publicname. It is now also a restricted site so you’ll be redirected to the login page.

You can override the controller level Authorize attribute by decorated the individual action(s) with the AllowAnonymous attribute:

[AllowAnonymous]
public string PublicName()
{
	return "This is the public customer name.";
}

Run the app and verify that /Customers/publicname is publicly available again.

The Authorize attribute accepts a couple of parameters to further refine the access filter. Examples:

[Authorize(Users="andras,admin")]
[Authorize(Roles="admin,poweruser")]

You can probably guess what they mean: allow users with specific user names – andras and admin – or only allow users who have either admin and power user role to access an action.

You can test this as follows. Add the following attribute to SecretName:

[Authorize(Users = "elvis,bob")]
public string SecretName()
{
	return "This is the secret customer name.";
}

Run the app and navigate to the secret name action. Log in with your user. You should see that you’re immediately redirected back to the Login page – unless you selected ‘elvis’ or ‘bob’ as the user name in the sign up process. In that case the great secret will be revealed to you.

We’ve now seen the basics of forms based authentication in MVC5. We’ll dig much deeper in the coming blog posts.

Read the next post in this series here.

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

Claims-based authentication in .NET4.5 MVC4 with C#: External authentication with WS-Federation Part 1

Our model MVC4 internet applications in this series had one important feature in common: they all provided the authentication logic internally.

This is the traditional approach to logging in on a web page: there is a login page within the application which provides the gateway to the protected parts of the website. Upon providing the login credentials and pressing the ‘Log in’ button the web application will check the the validity of those credentials against some data store and accept or reject the request.

We will build on the demo application from the previous post on claims in MVC4. If you are new to Claims in .NET4.5 then I’d recommend that you start from the beginning.

External authentication: introduction

There are several reasons why the internal auth approach might not be the most suitable one:

  • This is not a trivial exercise: logging in and out must happen in a secure way
  • Your attention therefore may be diverted from the ‘true’ purpose of your application, i.e. the very reason for its existence
  • You may not like programming in Security-related topics which holds you back from writing the ‘real’ application logic of your app
  • Multiple authentication types are often problematic to implement: you can typically only provide one specific type of authentication on your site and it’s usually a Forms-based one
  • As the auth logic is internal to your app it is difficult to re-use in other apps that need the same type of login: the result is a copy-paste type of horror

Thus it would be nice to somehow factor out the authentication logic in a separate project/application which can perform the authentication for your web app and for any other apps that also need authentication against the same user store. The benefits of such a scenario are the following:

  • Multiple applications can share the login logic
  • Keep the authentication logic in one place and avoid the copy-paste scenario: if the logic changes it will be automatically propagated in all consuming applications, also called the RELYING PARTIES
  • It’s possible to re-use the auth session across several applications so that the user does not need to log in on multiple sites: this is called Single SignOn
  • The external apps, i.e. the relying parties can get rid of their internal auth data allowing developers to concentrate on the ‘real stuff’
  • The responsibilities are more clearly divided: the relying party carries out the business logic and the auth app takes care of the authentication
  • The relying parties can establish a trust relationship with the auth app using Federation: this is important as the external apps should not blindly accept a authentication result as it may come from an unreliable source
  • The team of developers can be divided up more efficiently: domain experts who work on the real business logic and security experts that work on the authentication and user store part
  • You can put the external auth app anywhere: on a different physical server, in the cloud, behind some web service, etc.
  • Your web app can be set up to accept claims from multiple authentication services: as long as the claims are coming from a trusted source your web app will not care which one they are coming from

What would such a scenario look like? First I’ll try to describe the scenario in words.

The external authentication app we have been talking about is called a Security Token Service, or an STS in short. It is also called an Identity Provider. The STS is a normal website with its own login page sitting on some web server.

Imagine the following:

  • You have a web page that relies on external authentication
  • Thus it will be void of all types of auth logic and it will have no Login page either
  • A client wishes to reach a protected page within your web app
  • The client will then be redirected to the LOGIN PAGE OF THE STS
  • The STS performs the authentication and issues a security token to the client upon successful login
  • This token, which we’ll talk more about later, probably does not include too many claims: user ID, user name, email
  • This token will also include an identifier that identifies the issuer of the token in a reliable way
  • The token is sent back to the client which is then redirected to the external application where the user originally wanted to log in
  • The relying party inspects the token, checks the issuer, maybe transforms the claims and can reject or accept the user depending on the validity of the token and the claims within the token
  • Example: if the issuer of the token is not coming from a trusted auth service, the signature in the token has been tampered with or an important claim is missing or is malformed then you can still reject the request in your web app very early on
  • If everything is fine with the token then the relying web app will establish a ClaimsPrincipal the same way as we saw before in related blog posts

The flow can be shown graphically as follows:

An STS model

The security token is meaningless for the client. As mentioned above, it will be used by your web app to check its validity, transform the claims etc. Also, just to stress the point, it is not important any more where the STS is located.

Security Assertion Markup Language: SAML

You may be wondering what the security token issued by the STS looks like. There are some standard and certainly lots of company-specific formats out there. The default in .NET4.5 follows the SAML format, which is sort of a specialised XML. Here comes a portion of such a token from Wikipedia:

SAML example

You’ll see the Issuer, the X509 cert data, i.e. the digital signature and the NameID in the picture. The signature will be used to see if the token has been tampered with after it left the STS and if the issuer is a trusted one. There’s typically not much else shown in a SAML token. It is up to the STS what kind of data it will include in the SAML token. The STS may provide a different set of initial claims depending on the type of application wishing to be authenticated. The good news is that you will not have to work with SAML directly; .NET will translate the XML into Claims automatically. It is also important to note that if you have complete control over the STS then it is up you what you include in the SAML: anything from UserId to EyeColour and FavouriteBand can be sent along.

WS-Federation

The protocol that makes this trust relationship and token communication possible is called WS-Federation. It is a standard and is now available in .NET4.5. The flow of communication in words is as follows:

  • The client tries to access a protected page on your Claims-enabled site by sending a HTTP GET request
  • .NET will see that the request is void of any security token so it will be redirected to the Login page of the STS by another HTTP 302 request
  • The URL of the redirect will include a special query string that may look something like this: wsfed?wa=wsignin1.0&wtrealm=[ID of relying party]
  • The query string says that we want to sign in to a certain Realm, which is the identifier of the relying party, usually its URL
  • Upon successful login the STS somehow needs to send the SAML token to the relying party, so let’s stop here for a second…

The STS will send back a form with method = “POST” which will be redirected from the client to the relying party. This form might look like the following:

<form method="post" action="address of relying party">
    <input name="wresult" value="<saml:assertion..." />
    <script>
        window.setTimeout('document.forms[0].submit()', 0);
    </script>
</form>

The STS attaches the SAML to the value attribute of the input field within the form. The form is then submitted using a very simple piece of embedded JavaScript. Let’s continue with the flow:

  • The form is POSTed back to the relying party from the client
  • The relying party will validate the token and its contents and turn it into an Identity

It’s important to stress that this is not some Microsoft specific framework targeting .NET applications only. WS-Federation is part of the larger WS* family of web service specifications. It can happen that you have an STS built with .NET and a Ruby on Rails web app that you would like to connect to the STS. The fact that the STS was implemented using .NET is an unimportant detail in the bigger picture as the communication is based on a widely accepted standard. If you are in this situation then you need to check if Ruby has built-in support for WS-Federation, which I’m pretty sure it does although I know precious little about that framework.

Security Token Service

What does an actual STS look like then? There are several commercial products out there. Examples:

.NET4.5 includes base classes that allow you to build your own STS. Beware though that this is not a trivial exercise. You must be very knowledgeable and experienced in programming Security.

There’s an open source STS available on GitHub: Thinktecture IdentityServer which we’ll take a closer look at in the next blog post.

For now you won’t need any of the real STS solutions out there while developing your solution. You can download an extension to Visual Studio which enables you to use a Development STS with pre-set claims. We will use this in the demo.

Demo

You will need to download and install the Identity and Access Tool extension from here for the demo.

This is a great tool for development purposes; you won’t need a real STS but you can still write your code that accepts the security token as if it comes from a real STS. Then when you’re done you simply replace the tool with the STS of your choice.

Open the MVC4 application from the previous post. As it currently stands this application still uses Forms-based authentication and we’ll try to convert it to a Claims-based one.

Before we change anything let’s note some important identity-related aspects of web.config:

1. We have our system.identityModel section where we registered the custom authentication and custom authorisation managers:

<system.identityModel>
    <identityConfiguration>
      <claimsAuthenticationManager type="ClaimsInMvc4.CustomClaimsTransformer,ClaimsInMvc4" />
      <claimsAuthorizationManager type="ClaimsInMvc4.CustomAuthorisationManager,ClaimsInMvc4" />
    </identityConfiguration>
  </system.identityModel>

2. We let users log in by their usernames and passwords on our login page:

<authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>

3. We registered a session authentication module under the modules node:

<modules>
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"></add>
</modules>

4. There is no authorization element, meaning we let anonymous users view the unprotected pages of the website.

Upon successful installation of the Identity and Access Tool you should see a new menu point when you right-click the project:

Identity tool menu point

Click on the underlined menu point which will open up the Identity and Access window:

Identity and access window

You have here a number of options to add an STS to your project:

  • Local Development STS is the one you’ll want to use for development purposes if you don’t have a real STS available
  • A business identity provider, like the ones listed above, e.g. Oracle
  • An Azure cloud based STS

Select the first option. You can then select the ‘Local Development STS’ tab:

Local development STS tab

You will see a list of test claims that the web application will receive, such as the name ‘Terry’. Again, keep in mind that there’s no way to directly log on to a fully claims-based web app; here we pretend that an external STS is sending these claims to your application after a user has successfully signed in on the login page of the STS. You can configure this list according to the needs of your token validation and authorisation logic.

Change the value of the name claim, i.e. the very first one to the name of the user you created in the previous blog posts, so I’ve changed mine to ‘Andras’.

You can select the SAML version: either 1.1 or 2.0. This depends on the available versions of the STS of your choice. In our case it doesn’t make any difference, so leave option 1.1 selected.

Click OK and let’s see what happens. At first you won’t see any changes. Let’s inspect web.config though:

1. The system.identityModel has been extended to include claims-related elements:

<system.identityModel>
    <identityConfiguration>
      <claimsAuthenticationManager type="ClaimsMvc.CustomClaimsTransformer,ClaimsMvc" />
      <claimsAuthorizationManager type="ClaimsMvc.CustomAuthorisationManager,ClaimsMvc" />
      <audienceUris>
        <add value="http://localhost:2533/" />
      </audienceUris>
      <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
        <authority name="LocalSTS">
          <keys>
            <add thumbprint="9B74CB2F320F7AAFC156E1252270B1DC01EF40D0" />
          </keys>
          <validIssuers>
            <add name="LocalSTS" />
          </validIssuers>
        </authority>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None" />
    </identityConfiguration>
  </system.identityModel>

We will discuss these elements in more detail in the next blog post. Note the following: the Identity and Access Tool is periodically updated and can be downloaded from within Visual Studio. Select Extensions and Updates… in the Tools menu. Make sure you check if there are any updates available under the Updates menu point:

Tools updates in Visual Studio

When I published the first version of this post – some time in March 2013 – the above XML was slightly different. I updated the Identity and Access Tool on 12 May 2013 which yielded the above system.identityModel node. It is possible that when you read this post the Access Tool will again yield something different. Let me know in the comments section if you notice a change and I’ll update this post accordingly.

2. Forms-based login is gone:

<authentication mode="None" />

3. The modules element has been extended with WS-Federation:

<modules>
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      </add>
      <remove name="FormsAuthentication" />
      <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
    </modules>

4. By default we’ll deny access to anonymous users:

<authorization>
      <deny users="?" />
    </authorization>

Run the application and you may be greeted with the following error message:

Must have admin rights to local STS

If you started VS in admin mode then you shouldn’t see this, I’ll just restart mine.

Watch the browser bar carefully while the page is loading. At some point there should be a URL similar to this:

http://localhost:12175/wsFederationSTS/Issue/?wa=wsignin1.0&wtrealm=http%3a%2f%2flocalhost%3a2533%2f&wctx=rm%3d0%26id%3dpassive%26ru%3d%252f&wct=2013-05-12T12%3a22%3a58Z

This is the external ‘login page’, but there’s of course no external login page of the model STS. This is what’s happening:

  • Web.config has been changed by the identity tool to deny access to all anonymous users
  • When you run the application you will initially be an anonymous user
  • Your request is redirected to the model STS page on localhost:12175. Remember that this was the port number that we selected in the Identity and Access window. Don’t worry if yours has a different port number, it doesn’t make any difference
  • You will probably recognise the format of the URL with ‘?wa=wsignin1.0&wtrealm=’ followed by the URL of the MVC4 website
  • The local STS returns the list of claims we specified in the Identity and Access window
  • The request is redirected to our web page and the user is logged in
  • The request is redirected by the forms-based mechanism we discussed above where the form containing the SAML value of the authentication token was submitted by JavaScript

Recall that we protected the About page with the ClaimsAuthorize attribute:

[ClaimsAuthorize("Show", "Code")]
        public ActionResult About()

…which will activate our custom authorisation logic in CustomAuthorisationManager.cs:

public class CustomAuthorisationManager : ClaimsAuthorizationManager
    {
        public override bool CheckAccess(AuthorizationContext context)
        {
            string resource = context.Resource.First().Value;
            string action = context.Action.First().Value;

            if (action == "Show" && resource == "Code")
            {
                bool livesInSweden = context.Principal.HasClaim(ClaimTypes.Country, "Sweden");
                bool isAndras = context.Principal.HasClaim(ClaimTypes.GivenName, "Andras");
                return isAndras && livesInSweden;
            }

            return false;
        }
    }

Add two breakpoints to the application: one within CustomClaimsTransformer.Authenticate and one within CustomAuthorisationManager.CheckAccess. Re-run the application. If the code execution hasn’t stopped then click the Log off link to force a new ‘login’ via the local STS. Code execution should stop at CustomClaimsTransformer.Authenticate. This is good news as our custom auth manager still kicks in and dresses up the Principal with our custom claims…:

private ClaimsPrincipal DressUpPrincipal(String userName)
        {
            List<Claim> claims = new List<Claim>();

            //simulate database lookup
            if (userName.IndexOf("andras", StringComparison.InvariantCultureIgnoreCase) > -1)
            {
                claims.Add(new Claim(ClaimTypes.Country, "Sweden"));
                claims.Add(new Claim(ClaimTypes.GivenName, "Andras"));
                claims.Add(new Claim(ClaimTypes.Name, "Andras"));
                claims.Add(new Claim(ClaimTypes.NameIdentifier, "Andras"));
                claims.Add(new Claim(ClaimTypes.Role, "IT"));
            }
            else
            {
                claims.Add(new Claim(ClaimTypes.GivenName, userName));
                claims.Add(new Claim(ClaimTypes.Name, userName));
                claims.Add(new Claim(ClaimTypes.NameIdentifier, userName));
            }

            return new ClaimsPrincipal(new ClaimsIdentity(claims, "Custom"));
        }

…and also establishes the authentication session as per the CreateSession method. Now click the About link on the front page. As this is a protected page code execution will stop within CustomAuthorisationManager.CheckAccess which shows that even this custom manager class works as it should. Upon successful authorisation the About page should load as expected.

So our previous investments are still worth the effort. The external login doesn’t invalidate our claims authentication and claims transformation logic.

In the next post we’ll look at the changes in web.config in more details and hook up our MVC4 with a real STS.

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

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: