In this chapter, we will discuss how to implement security features in the application. We will also look at the new membership features included with ASP.NET and available for use from ASP.NET MVC. In the latest release of ASP.NET, we can manage user identities with the following −
In this chapter, we will also take a look at the new identity components that is a part of ASP.NET and see how to customize membership for our users and roles.
Authentication of user means verifying the identity of the user. This is really important. You might need to present your application only to the authenticated users for obvious reasons.
Let’s create a new ASP.Net MVC application.
Click OK to continue.
When you start a new ASP.NET application, one of the steps in the process is configuring the authentication services for application needs.
Select MVC template and you will see that the Change Authentication button is now enabled.
This is done with the Change Authentication button that appears in the New Project dialog. The default authentication is, Individual User Accounts.
When you click the Change button, you will see a dialog with four options, which are as follows.
The first option is No Authentication and this option is used when you want to build a website that doesn't care who the visitors are.
It is open to anyone and every person connects as every single page. You can always change that later, but the No Authentication option means there will not be any features to identify users coming to the website.
The second option is Individual User Accounts and this is the traditional forms-based authentication where users can visit a website. They can register, create a login, and by default their username is stored in a SQL Server database using some new ASP.NET identity features, which we'll look at.
The password is also stored in the database, but it is hashed first. Since the password is hashed, you don't have to worry about plain-text passwords sitting in a database.
This option is typically used for internet sites where you want to establish the identity of a user. In addition to letting a user create a local login with a password for your site, you can also enable logins from third parties like Microsoft, Google, Facebook, and Twitter.
This allows a user to log into your site using their Live account or their Twitter account and they can select a local username, but you don't need to store any passwords.
This is the option that we'll spend some time with in this module; the individual user accounts option.
The third option is to use organizational accounts and this is typically used for business applications where you will be using active directory federation services.
You will either set up Office 365 or use Azure Active Directory Services, and you have a single sign-on for internal apps and Cloud apps.
You will also need to provide an app ID so your app will need to be registered with the Windows Azure management portal if this is Azure based, and the app ID will uniquely identify this application amongst all the applications that might be registered.
The fourth option is Windows authentication, which works well for intranet applications.
A user logs into Windows desktop and can launch a browser to the application that sits inside the same firewall. ASP.NET can automatically pick up the user's identity, the one that was established by active directory. This option does not allow any anonymous access to the site, but again that is a configuration setting that can be changed.
Let's take a look into the forms-based authentication, the one that goes by the name, Individual User Accounts. This application will store usernames and passwords, old passwords in a local SQL Server database, and when this project is created, Visual Studio will also add NuGet packages.
Now run this application and when you first come to this application you will be an anonymous user.
You won't have an account that you can log into yet so you will need to register on this site.
Click on the Register link and you will see the following view.
Enter your email id and password.
Click Register. Now, the application will recognize you.
It will be able to display your name. In the following screenshot, you can see Hello, muhammad.waqas@outlook.com! is displayed. You can click on that and it's a link to a page where you can change the password.
You can also log off, shut down, reboot, come back a week later, and you should be able to log in with the credentials that you used earlier. Now click on the log off button and it will display the following page.
Click again on the Log in link and you will go to the following page.
You can login again with the same credentials.
A lot of work goes on behind the scene to get to this point. However, what we want to do is examine each of the features and see how this UI is built. What is managing the logoff and the login process? Where is this information sorted in the database?
Let's just start with a couple of simple basics. First we will see how is this username displayed. Open the _Layout.cshtml from the View/Shared folder in the Solution explorer.
<!DOCTYPE html> <html> <head> <meta charset = "utf-8" /> <meta name = "viewport" content = "width = device-width, initial-scale = 1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> <div class = "navbar navbar-inverse navbar-fixed-top"> <div class = "container"> <div class = "navbar-header"> <button type = "button" class = "navbar-toggle" datatoggle = "collapse" data-target = ".navbar-collapse"> <span class = "icon-bar"></span> <span class = "icon-bar"></span> <span class = "icon-bar"></span> </button> @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" }) </div> <div class = "navbar-collapse collapse"> <ul class = "nav navbar-nav"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> </ul> @Html.Partial("_LoginPartial") </div> </div> </div> <div class = "container body-content"> @RenderBody() <hr /> <footer> <p>© @DateTime.Now.Year - My ASP.NET Application</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") @RenderSection("scripts", required: false) </body> </html>
There is a common navigation bar, the application name, the menu, and there is a partial view that's being rendered called _loginpartial. That's actually the view that displays the username or the register and login name. So _loginpartial.cshtml is also in the shared folder.
@using Microsoft.AspNet.Identity @if (Request.IsAuthenticated) { using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" })){ @Html.AntiForgeryToken() <ul class = "nav navbar-nav navbar-right"> <li> @Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" }) </li> <li> <a href = "javascript:document.getElementById('logoutForm').submit()">Logoff</a> </li> </ul> } }else{ <ul class = "nav navbar-nav navbar-right"> <li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li> <li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li> </ul> }
As you can see above, there are if/else statements. If we do not know who the user is, because the request is not authenticated, this view will display register and login links. The user can click on the link to log in or register. All this is done by the account controller.
For now, we want to see how to get the username, and that's inside Request.IsAuthenticated. You can see a call to User.Identity.GetUserName. That will retrieve the username, which in this case is ‘muhammad.waqas@outlook.com’
Let's suppose that we have some sort of information which we want to protect from unauthenticated users. So let’s create a new controller to display that information, but only when a user is logged in.
Right-click on the controller folder and select Add → Controller.
Select an MVC 5 controller - Empty controller and click ‘Add’.
Enter the name SecretController and click ‘Add’ button.
It will have two actions inside as shown in the following code.
using System.Web.Mvc; namespace MVCSecurityDemo.Controllers{ public class SecretController : Controller{ // GET: Secret public ContentResult Secret(){ return Content("Secret informations here"); } public ContentResult PublicInfo(){ return Content("Public informations here"); } } }
When you run this application, you can access this information without any authentication as shown in the following screenshot.
So only authenticated users should be able to get to Secret action method and the PublicInfo can be used by anyone without any authentication.
To protect this particular action and keep unauthenticated users from arriving here, you can use the Authorize attribute. The Authorize attribute without any other parameters will make sure that the identity of the user is known and they're not an anonymous user.
// GET: Secret [Authorize] public ContentResult Secret(){ return Content("Secret informations here"); }
Now run this application again and specify the same URL http://localhost:54232/Secret/Secret. The MVC application will detect that you do not have access to that particular area of the application and it will redirect you automatically to the login page, where it will give you a chance to log in and try to get back to that area of the application where you were denied.
You can see that it is specified in the return URL, which essentially tells this page that if the user logs in successfully, redirect them back to /secret/secret.
Enter your credentials and click ‘Log in’ button. You will see that it goes directly to that page.
If you come back to the home page and log off, you cannot get to the secret page. You will be asked again to log in, but if go to /Secret/PublicInfo, you can see that page, even when you are not authenticated.
So, when you don't want to be placing authorization on every action when you're inside a controller where pretty much everything requires authorization. In that case you can always apply this filter to the controller itself and now every action inside of this controller will require the user to be authenticated.
using System.Web.Mvc; namespace MVCSecurityDemo.Controllers{ [Authorize] public class SecretController : Controller{ // GET: Secret public ContentResult Secret(){ return Content("Secret informations here"); } public ContentResult PublicInfo(){ return Content("Public informations here"); } } }
But if you really want any action to be open, you can come override this authorization rule with another attribute, which is, AllowAnonymous.
using System.Web.Mvc; namespace MVCSecurityDemo.Controllers{ [Authorize] public class SecretController : Controller{ // GET: Secret public ContentResult Secret(){ return Content("Secret informations here"); } [AllowAnonymous] public ContentResult PublicInfo(){ return Content("Public informations here"); } } }
Run this application and you can access the /Secret/PublicInfo with logging in but other action will require authentication.
It will allow anonymous users into this one action only.
With the Authorize attribute, you can also specify some parameters, like allow some specific users into this action.
using System.Web.Mvc; namespace MVCSecurityDemo.Controllers{ [Authorize(Users = "ali.khan@outlook.com")] public class SecretController : Controller{ // GET: Secret public ContentResult Secret(){ return Content("Secret informations here"); } [AllowAnonymous] public ContentResult PublicInfo(){ return Content("Public informations here"); } } }
When you run this application and go to /secret/secret, it will ask you to log in because it is not the proper user for this controller.