Visual Studio 2013 has several templates for creating new web projects. A brand new web project with authentication will have over 1500 lines of C# code. This saves a lot of time for new applications.
1500 lines for authentication may be too much if you’re learning ASP.NET. Fortunately, ASP.NET Identity is not too complicated when you start “from scratch”.
What follows is a short tutorial on setting up basic ASP.NET Identity in an empty web project.
Authentication refers to the question “Who is the user?” Authorization refers to the question “What can the user do?”
HTTP is a stateless protocol, so authentication involves two things:
ASP.NET Identity solves both authentication and authorization.
The following sections will cover these parts in the following order:
Cookie authentication is used in most websites. When a user logs in, the server generates a secure Cookie. That Cookie is used by the browser to authenticate each subsequent request.
Cookie authentication requires two dependencies:
Microsoft.AspNet.Identity.Owin
Microsoft.Owin.Host.SystemWeb
They can be added to a project using the NuGet package manager (access it by right-clicking on the project in Visual Studio).
With the dependencies added, Cookie authentication is configured using an OWIN Startup Class. The Visual Studio new file dialog has an option to create a new “OWIN Startup Class”.
The following namespaces are required in the OWIN Startup Class:
using Microsoft.Owin.Security.Cookies;
using Microsoft.AspNet.Identity;
Then the following code, inside the Configuration
method, sets up cookie authentication:
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login.aspx")
}
);
With Cookie authentication set up, the current user needs to be signed in. Authentication comes later, but for the moment, an identity can be directly created programmatically:
The following code would sign in current user as Joe, belonging to the Admin
and User
groups/roles:
var identity = new ClaimsIdentity(
new[] {
new Claim(ClaimTypes.Name, "Joe"),
new Claim(ClaimTypes.Role, "Admin"),
new Claim(ClaimTypes.Role, "User")
},
DefaultAuthenticationTypes.ApplicationCookie,
ClaimTypes.Name,
ClaimTypes.Role
);
var authentication = HttpContext.Current.GetOwinContext().Authentication;
authentication.SignIn(new AuthenticationProperties { IsPersistent = true }, identity);
Such code could be executed at a login page (e.g., in a LoginButton_Click
event handler).
The following code is used to log out the current user:
var authentication = HttpContext.Current.GetOwinContext().Authentication;
authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
The Web.config can be used to secure access to a particular page, a directory or the entire application:
<location path="SecurePage.aspx">
<system.web>
<authorization>
<allow users="Joe"/>
<deny users="*" />
</authorization>
</system.web>
</location>
The above code blocks access to SecurePage.aspx
for all users except for “Joe”
The code in the previous section is sufficient for securing resources on a website. However, the identity was hard-coded.
The identity could be managed entirely by a custom framework. A better approach is to use the ASP.NET Identity framework. The framework is secure and well-designed. It securely hashes passwords and can deal with users and roles (as well as other more sophisticated features).
Authentication in ASP.NET Identity requires the implementation of a class for users and a storage provider.
They can be created with two classes:
public class ApplicationUser : IUser<string>
{
public string Id { get; set; }
public string UserName { get; set; }
}
and
public class ApplicationStore : IUserPasswordStore<ApplicationUser>, IUserRoleStore<ApplicationUser>
The ApplicationStore implements several interfaces. They can be automatically implemented using Visual Studio’s “Implement Interfaces” function (right click on the interface name in the C# text editor).
For a read-only Identity framework, the following methods require a functioning body:
FindByIdAsync
FindByNameAsync
GetPasswordHashAsync
HasPasswordAsync
GetRolesAsync
IsInRoleAsync
Dispose
The default password hasher is Microsoft.AspNet.Identity.PasswordHasher. A compatible hash can be computed as as follows:
string hashed = new PasswordHasher().HashPassword("password");
Once the required methods have been implemented (e.g., by reading from a database or text file), the login can be performed as follows:
string username = UserNameTextBox.Text;
string password = PasswordTextBox.Text;
var authentication = HttpContext.Current.GetOwinContext().Authentication;
var store = new ApplicationStore();
var userManager = new UserManager<ApplicationUser, string>(store);
var user = userManager.Find(username, password);
if (user != null)
{
var identity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
authentication.SignIn(new AuthenticationProperties { IsPersistent = true }, identity);
}
Log out is unchanged:
var authentication = HttpContext.Current.GetOwinContext().Authentication;
authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
ASP.NET Identity can be used with fewer than 1500 lines of code.
The complexity of the templates in Visual Studio 2013 come from several sources:
However, these extra features translate into additional branches, conditions, options and modules. They do not change the fundamental behaviour of authentication and authorization.
Understanding a basic ASP.NET Identity implementation makes it much easier to see the core logic and behaviour.
Once you’ve mastered the basics of ASP.NET Identity, I recommend using the more complete Visual Studio templates.
Published 28 March 2015 by Benjamin Johnston.