Introduction to ASP.NET Identity

By Pranav Rastogi, Rick Anderson, Tom Dykstra, Jon Galloway, and Erik Reitan

ASP.NET Identity is a membership system which allows you to add login functionality to your application. Users can create an account and login with a user name and password or they can use an external login providers such as Facebook, Google, Microsoft Account, Twitter and more.

You can configure ASP.NET Identity to use a SQL Server database to store user names, passwords, and profile data. Alternatively, you can use your own persistent store to store data in another other persistent storage, such as Azure Table Storage.

Overview of ASP.NET Identity in ASP.NET Web App

ASP.NET Identity is used in the Visual Studio project templates for ASP.NET 5. In this topic, you’ll learn how the ASP.NET 5 project templates use ASP.NET Identity to add functionality to register, log in, and log out a user.

The purpose of this article is to give you a high level overview of ASP.NET Identity. You can follow it step by step or just read the details. For more detailed instructions about creating apps using ASP.NET Identity, see the Next Steps section at the end of this article.

  1. Create an ASP.NET Web Application with Individual User Accounts.

    In Visual Studio, select File -> New -> Project. Then, select the ASP.NET Web Application from the New Project dialog box. Continue by selecting a Web Application with Individual User Accounts, as the authentication method.

    ../../_images/01-mvc.png

    The created project contains the following ASP.NET Identity package.

    • Microsoft.AspNet.Identity.EntityFramework

      The Microsoft.AspNet.Identity.EntityFramework package has the Entity Framework implementation of ASP.NET Identity which will persist the ASP.NET Identity data and schema to SQL Server.

    Note

    In Visual Studio, you can view NuGet packages details by selecting Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution. You also see a list of packages in the dependencies section of the project.json file within your project.

    When the application is started, the Startup class is instantiated. Within this class, the runtime calls the ConfigureServices method which adds a number of services to a services container. Included in this services container is the Identity services:

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
    
        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();
    
        services.AddMvc();
    
        // Add application services.
        services.AddTransient<IEmailSender, AuthMessageSender>();
        services.AddTransient<ISmsSender, AuthMessageSender>();
    }
    

    After the ConfigureServices method is called, the Configure method is called. In this method, ASP.NET Identity is enabled for the application when the UseIdentity method is called. This adds cookie-based authentication to the request pipeline.

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();
    
        if (env.IsDevelopment())
        {
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }
    
        app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());
    
        app.UseStaticFiles();
    
        app.UseIdentity();
    
        // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715
    
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
    

    For more information about the request pipeline, see Understanding ASP.NET 5 Web Apps - Application Startup. For more information about the application start up process, see Application Startup.

  2. Creating a user.

    Launch the application from Visual Studio (Debug -> Start Debugging) and then click on the Register link in the browser to create a user. The following image shows the Register page which collects the user name and password.

    ../../_images/02-reg.png

    When the user clicks the Register link, the UserManager and SignInManager services are injected into the Controller:

        public class AccountController : Controller
        {
            private readonly UserManager<ApplicationUser> _userManager;
            private readonly SignInManager<ApplicationUser> _signInManager;
            private readonly IEmailSender _emailSender;
            private readonly ISmsSender _smsSender;
            private readonly ApplicationDbContext _applicationDbContext;
            private static bool _databaseChecked;
            private readonly ILogger _logger;
    
            public AccountController(
                UserManager<ApplicationUser> userManager,
                SignInManager<ApplicationUser> signInManager,
                IEmailSender emailSender,
                ISmsSender smsSender,
                ILoggerFactory loggerFactory,
                ApplicationDbContext applicationDbContext)
            {
                _userManager = userManager;
                _signInManager = signInManager;
                _emailSender = emailSender;
                _smsSender = smsSender;
                _applicationDbContext = applicationDbContext;
                _logger = loggerFactory.CreateLogger<AccountController>();
            }
    

    Then, the Register action creates the user by calling CreateAsync function of the UserManager object, as shown below:

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Register(RegisterViewModel model)
    {
        EnsureDatabaseCreated(_applicationDbContext);
        if (ModelState.IsValid)
        {
            var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
            var result = await _userManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
                // Send an email with this link
                //var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
                //await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
                //    "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
                await _signInManager.SignInAsync(user, isPersistent: false);
                _logger.LogInformation(3, "User created a new account with password.");
                return RedirectToAction(nameof(HomeController.Index), "Home");
            }
            AddErrors(result);
        }
    
        // If we got this far, something failed, redisplay form
        return View(model);
    }
    
  3. Log in.

    If the user was successfully created, the user is logged in by the SignInAsync method, also contained in the Register action. By signing in, the SignInAsync method stores a cookie with the user’s claims.

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Register(RegisterViewModel model)
    {
        EnsureDatabaseCreated(_applicationDbContext);
        if (ModelState.IsValid)
        {
            var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
            var result = await _userManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=532713
                // Send an email with this link
                //var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
                //await _emailSender.SendEmailAsync(model.Email, "Confirm your account",
                //    "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
                await _signInManager.SignInAsync(user, isPersistent: false);
                _logger.LogInformation(3, "User created a new account with password.");
                return RedirectToAction(nameof(HomeController.Index), "Home");
            }
            AddErrors(result);
        }
    
        // If we got this far, something failed, redisplay form
        return View(model);
    }
    

    The above SignInAsync method calls the below SignInAsync task, which is contained in the SignInManager class.

    If needed, you can access the user’s identity details inside a controller action. For instance, by setting a breakpoint inside the HomeController.Index action method, you can view the User.claims details. By having the user signed-in, you can make authorization decisions. For more information, see Authorization.

    As a registered user, you can log in to the web app by clicking the Log in link. When a registered user logs in, the Login action of the AccountController is called. Then, the Login action signs in the user using the PasswordSignInAsync method contained in the Login action.

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
    {
        EnsureDatabaseCreated(_applicationDbContext);
        ViewData["ReturnUrl"] = returnUrl;
        if (ModelState.IsValid)
        {
            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, set lockoutOnFailure: true
            var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
            if (result.Succeeded)
            {
                _logger.LogInformation(1, "User logged in.");
                return RedirectToLocal(returnUrl);
            }
            if (result.RequiresTwoFactor)
            {
                return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            }
            if (result.IsLockedOut)
            {
                _logger.LogWarning(2, "User account locked out.");
                return View("Lockout");
            }
            else
            {
                ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                return View(model);
            }
        }
    
        // If we got this far, something failed, redisplay form
        return View(model);
    }
    
  4. Log off.

    Clicking the Log off link calls the LogOff action in the account controller.

    public async Task<IActionResult> LogOff()
    {
        await _signInManager.SignOutAsync();
        _logger.LogInformation(4, "User logged out.");
        return RedirectToAction(nameof(HomeController.Index), "Home");
    }
    

    The code above shows the SignInManager.SignOutAsync method. The SignOutAsync method clears the users claims stored in a cookie.

  5. View the database.

    After stopping the application, view the user database from Visual Studio by selecting View -> SQL Server Object Explorer. Then, expand the following within the SQL Server Object Explorer:

    • (localdb)MSSQLLocalDB
    • Databases
    • aspnet5-<the name of your application>
    • Tables

    Next, right-click the dbo.AspNetUsers table and select View Data to see the properties of the user you created.

    ../../_images/04-db.png

Components of ASP.NET Identity

The primary reference assembly for the ASP.NET Identity system is Microsoft.AspNet.Identity. This assembly contains the core set of interfaces for ASP.NET Identity.

../../_images/05-dependencies.png

These dependencies are needed to use the ASP.NET Identity system in ASP.NET applications:

  • EntityFramework.SqlServer - Entity Framework is Microsoft’s recommended data access technology for relational databases.
  • Microsoft.AspNet.Authentication.Cookies - Middleware that enables an application to use cookie based authentication, similar to ASP.NET’s Forms Authentication.
  • Microsoft.AspNet.Cryptography.KeyDerivation - ASP.NET 5 utilities for key derivation.
  • Microsoft.AspNet.Hosting.Abstractions - ASP.NET 5 Hosting abstractions.

Migrating to ASP.NET Identity 3.x

For additional information and guidance on migrating your existing apps to the ASP.NET Identity 3.x system, see Migrating from ASP.NET Identity 2.x to 3.x.