In the last tutorial on ASP.NET Identity Getting Started, we discussed how to set up ASP.NET identity in ASP.NET MVC Application. We also created Register User View and looked how to register users in the application.
In this tutorial, we are going to build the login page and look at how to Authenticate the user using the OWIN Middleware authentication component.
Here is the link to all the tutorials
- Introduction to ASP.NET Identity
- ASP.NET Identity Tutorial – Getting Started
- ASP.NET Identity Tutorial – OWIN Authentication Middleware
- ASP.NET Core Identity
Table of Contents
OWIN Authentication middleware
The OWIN authentication middleware is used for authenticating users.
In older ASP.NET Applications, we used Forms authentication module to authenticate the users into our application. When a user logs in his credentials are verified by querying the information from the data store. A cookie is issued to the user, which contained the user credentials embedded into it.
The every subsequent request coming to the application must contain the same cookie. The Forms authentication validates every request and if the valid cookie is found, then the request is granted else the user is presented with a login page and asked to enter valid credentials.
The OWIN authentication middleware replaces the Forms Authentication module.
The OWIN authentication middleware can issue authentication cookies on its own or it can use the external logins like facebook, google, twitter etc to do that.
The OWIN authentication middleware is platform agnostic. It can be used in ASP.NET MVC, webforms or ASP.NET core
OWIN authentication middleware resides in the namespace Microsoft.AspNet.Identity.OWIN. We did install it our previous tutorial using NuGet package manager
1 2 3 | Install-Package Microsoft.aspnet.identity.owin |
It has dependencies on the following packages
- Microsoft.Owin
The Microsoft.Owin namespace contains classes that support OWIN collection. The class Provides a set of helper types and abstractions for simplifying the creation of OWIN Components - Microsoft.Owin.Security
- Microsoft.Owin.Security.Cookies
This Middleware that adds the cookie-based authentication to the application. This is similar to ASP.NET’s forms authentication - Microsoft.Owin.Security.OAuth
Middleware that enables an application to support any standard OAuth 2.0 authentication workflow
What is OWIN
OWIN (The Open Web Interface for .NET) Interface defines the standard interface between web server and Web application.
OWIN is not a framework. It is a set of rules or specifications on how the web applications and web servers should interact with each other. The goal of OWIN is to decouple Web server and application.
You can read OWIN Tutorial from here
To use Owin authentication middleware we need to install one more NuGet packages
Microsoft.Owin.Host.systemWeb
This namespace contains the types related to handling OWIN requests
1 2 3 | Install-Package Microsoft.Owin.Host.systemWeb |
The latest version available at the time of writing this article is 3.1.0
OWIN Startup Class
Every OWIN enabled Application must have one startup class where you need to register the OWIN Middleware used by the application pipeline. When an OWIN application starts first thing it does is to look for the startup up class. The Startup class must have Configuration(IAppBuilder) method. This method is then executed by the OWIN runtime.
OWIN Startup class can be added by selecting the root folder of the Project and clicking on Add -> Add New Item. Select the OWIN Startup class from the list of Options. Enter the Name as Startup and click on Add. This will add the Owin startup class to the project
Configuring OWIN Middleware
Open the startup class created in the above step and you will see that the Configuration method is automatically added to the class. The Configuration method accepts parameters of type IAppBuilder. This Parameter is supplied by the Host at the runtime. Configuration method is where all the configuration related information of all our OWIN middleware must be added.
Change the class definition to partial. Call the method ConfigureAuth(app) inside the Configuration in the startup.cs.
We will add the configuration method related OWIN authentication middleware here shortly
The complete startup.cs is as follows
1 2 3 4 5 6 | using System; using System.Threading.Tasks; using Microsoft.Owin; using Owin; |
[assembly: OwinStartup(typeof(ASPNetIdentity.startup))]
namespace ASPNetIdentity { public partial class startup { public void Configuration(IAppBuilder app) { ConfigureAuth(app); } } }
CreatePerOwinContext
In the previous tutorial, we created the instance of the user manager and user store in the register method itself. We also created an instance of DBContext in the same method.
This approach could result in more than one instance of user manager (or DBContext) in the request.
The solution is to create a single instance of UserManager and DbContext per request and reuse them throughout the application.
We can use the OWIN context objects to store the user Manager object and retrieve it whenever it is needed. This is done in the CreatePerOwinContext method of the AppBuilder class. The Instance of the AppBuilder is supplied by the Host at the runtime.
The CreatePerOwinContext creates a single instance of the object for each request handled by the application. The Instance is tied to the request lifecycle.
The app.CreatePerOwinContext<T>() method registers a callback method, which will return the new instance of the specified type. This instance is then stored in the OwinContext and can be used throughout the application. You can access the instance from the Context.Get() method.
Now, goto app_start folder and create a Startup.Auth.cs class. Remove App_Start from the namespace. Change the class definition to Partial as shown below. Import the required namespaces. Add the method ConfigureAuth
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Owin; using ASPNetIdentity.Models; namespace ASPNetIdentity { public partial class Startup { public void ConfigureAuth(IAppBuilder app) { } } } |
Configuring DBContext
To make use of the CreatePerOwinContext, we need to have a static method create, which returns the new instance of the DbContext
Open the IdentityModels.cs and locate the ApplicationDbContext class
Now, add the static method “create” to the class, which returns the instance of the class as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false) { } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } } |
Now, open the Startup.Auth.cs and add ApplicationDbContext to the Owin Context using the CreatePerOwinContext as shown below
1 2 3 | app.CreatePerOwinContext(ApplicationDbContext.Create); |
Configuring the User Manager
Now, we will create the static “Create” method in the User Manager class
Open the IdentityModels.cs and import the following namespaces
1 2 3 4 | using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; |
Now, go to the ApplicationUserManager class and the static “create” method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class ApplicationUserManager : UserManager<ApplicationUser> { public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store) { } public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) { var store = new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()); var manager = new ApplicationUserManager(store); return manager; } } |
Here the Create method receives two arguments. IdentityFactoryOptions and Owin Context.
Next, we create an instance of UserStore. The User Store requires an instance of ApplicationDbContext.
We have already stored the ApplicationDbContext in the Owin context. We can get the instance of the ApplicationDbContext using the Get method of the OwinContext as shown
1 2 3 | context.Get<ApplicationDbContext>() |
Next, we will get the instance of the User Manager and return it
Finally, we add following code in ConfigureAuth method of the Startup.Auth.cs to store instance of ApplicationUserManager in the Owin Context
1 2 3 | app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); |
Sign in Manager
SignInManager is a concrete class which handles the user sign in from the application. This class was added in the version 2.1.0 of the identity framework. It is defined in the Microsoft.AspNet.Identity.Owin namespace.
The Prior versions of Identity used AuthenticationManager. The SignInManager uses the AuthenticationManager to Sign in users to the system. SignInManager takes ApplicationUser and authenticationManager in its constructor.
In the code below we create ApplicationSignInManager inheriting from the SignInManager <TUser,TKey>. Note that we also created a static Create method which creates and returns the instance of ApplicationSignInManager.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using Microsoft.Owin.Security; public class ApplicationSignInManager : SignInManager<ApplicationUser, string> { public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager) : base(userManager, authenticationManager) { } public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context) { return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication); } } |
We are retrieving the instance of the User Manager from the owin Context
1 2 3 | context.GetUserManager<ApplicationUserManager>() |
The AuthenticationManager is available in the Owin context as Authentication property
Finally, we can then add the sign in manager in the Owin context
1 2 3 | app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); |
In older version of ASP.NET applications used the FormsAuthentication module to issue Cookies to identify the current logged in user. The cookie authentication is now handled by the Microsoft.owin.Security.Cookies middleware. We use extension method UseCookieAuthentication to configure this middleware.
The UseCookieAuthentication method takes the AuthenticationType parameter where we need to specify the Authentication Type we are going to use. We also need to specify the loginpath which is the path where unauthenticated users are redirected.
1 2 3 4 5 6 7 8 9 | using Microsoft.AspNet.Identity; app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login") }); |
Copy the above code in the owin startup class (startup.cs) under Configuration Method.
Now, we have built the necessary plumbing to make use of OWIN Authentication middleware. Let us now build the login & logout pages
Building the Login Page
Login View Model
Open the AccountViewModels.cs and add the loginViewModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class LoginViewModel { [Required] [Display(Name = "Email")] [EmailAddress] public string Email { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [Display(Name = "Remember me?")] public bool RememberMe { get; set; } } |
Login and logout Action methods in Account Controller
Open the AccountController.cs
Import the namespace
1 2 3 4 5 | using Microsoft.Owin.Security; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin.Security; |
Next, get the reference to the instance of User Manager, Stores Manager & Authentication Manager from the Owin Context.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | private ApplicationSignInManager _signInManager; private ApplicationUserManager _userManager; public ApplicationSignInManager SignInManager { get { return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); } private set { _signInManager = value; } } public ApplicationUserManager UserManager { get { return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); } private set { _userManager = value; } } private IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } } |
Login Action method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } // // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (!ModelState.IsValid) { return View(model); } // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, change to shouldLockout: true var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false); switch (result) { case SignInStatus.Success: return RedirectToLocal(returnUrl); case SignInStatus.LockedOut: return View("Lockout"); case SignInStatus.RequiresVerification: return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); case SignInStatus.Failure: default: ModelState.AddModelError("", "Invalid login attempt."); return View(model); } } |
We make a call to PasswordSignInAsync method of the SignInManager. This method validates the user and issues authentication cookie. It Returns SignInStatus which indicates whether the user was able to log in or not
1 2 3 | var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false); |
A Helper class
1 2 3 4 5 6 7 8 9 10 | private ActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } return RedirectToAction("Index", "Home"); } |
Login View
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | @using ASPNetIdentity.Models @model LoginViewModel @{ ViewBag.Title = "Log in"; } <h2>@ViewBag.Title.</h2> <div class="row"> <div class="col-md-8"> <section id="loginForm"> @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() <h4>Use a local account to log in.</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <div class="checkbox"> @Html.CheckBoxFor(m => m.RememberMe) @Html.LabelFor(m => m.RememberMe) </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Log in" class="btn btn-default" /> </div> </div> <p> @Html.ActionLink("Register as a new user", "Register") </p> @* Enable this once you have account confirmation enabled for password reset functionality <p> @Html.ActionLink("Forgot your password?", "ForgotPassword") </p>*@ } </section> </div> </div> |
Register method
Update the Register Action method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // // POST: /Account/Register [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser { UserName = model.Email, Email = model.Email }; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771 // Send an email with this link // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme); // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>"); return RedirectToAction("Index", "Home"); } AddErrors(result); } // If we got this far, something failed, redisplay form return View(model); } |
We are using SignInAsyc method the, which Creates a user identity and then signs the identity using the AuthenticationManager
1 2 3 | SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); |
LogOff method
1 2 3 4 5 6 7 8 9 10 11 | // // POST: /Account/LogOff [HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); return RedirectToAction("Index", "Home"); } |
Now, start the web application check the log in/log off and register user functionality.
Conclusion
In this Tutorial, we looked at how to use Sign in manager provided by the Microsoft.AspNet.Identity.Owin middleware to authenticate the user. We also looked how to add an instance of the User Manager, Sign in Manager and DBContext to the Owin Context and retrieve it later.
Here is the link to all the tutorials
- Introduction to ASP.NET Identity
- ASP.NET Identity Tutorial – Getting Started
- ASP.NET Identity Tutorial – OWIN Authentication Middleware
- ASP.NET Core Identity
getOwinContext is not available in dotnet 6, can you please assist how to implement in dotnet 6?
HI, the source is restricted? i am not able to see it, i recive a 401
SignInManager.PasswordSignInAsync method not attached
Please share
creating instance of the user manager in the register method
This approach could result in more than one instance of user manager (or DBContext) in the request.
how explain more plz
“OWIN authentication middleware is the new framework…”
“OWIN is not a framework”
“…provided by the Microsoft.AspNet.Identity.Owin framework…”
Think I’m going to have to digest this!
Thanks for the heads up
OWIN is specification
Microsoft.AspNet.Identity.Owin is middleware which implements OWIN Specification
*Removed the word Framework
Ah, cool. Thanks.
You are the best instructor
your explain is very simple and your English is pretty and simple
but i want more details about
Two Factor Authentication and mail confirmation and SMS
Sir Please Provide DB Details Too which we have to made during this .