In the previous article, we learned about filters in MVC. We have also seen that all filters are used as attributes over a controller, an action, or can sometimes be registered globally.
In this article, we will uncover every detail of the Authorize action filter. The Authorize action filter is used as an attribute over a controller or action. The purpose of using the Authorize attribute is to authenticate or authorize a user.
Authentication
Authentication refers to checking whether a user is a valid user, meaning a registered user on your site or not. Usually, in real-time projects, we use the Authorize attribute to restrict access to users who are not authenticated. This means that if a user is not logged in and tries to access an action marked as 'Authorize', we redirect them to the login page again. To make it work properly, we implement Forms Authentication in parallel.
Let's implement Forms Authentication and check how the Authorize attribute helps to achieve the desired outcome.
- Step 1. First, add the following lines to your webconfig file under System.Web tag to enable Forms Authentication.
<authentication mode="Forms"> <forms loginurl="~/Home/Login"> </forms> </authentication>
Note: loginurl is the path to your login action method, meaning if anyone tries to access an Action which is marked as Authorize, he will be redirected to the Login Page if he is not already logged in.
- Step 2: Create two action methods: one that returns a Login view and the second that validates the user by comparing the username and password provided by the user with the corresponding values from the database. For simplicity, I have used a hard-coded value.
[HttpGet] public ActionResult Login(string ReturnUrl) { ViewBag.ReturnUrl = ReturnUrl; return View(); } [HttpPost] public ActionResult Login(string UserName, string Password, bool RememberMe, string ReturnUrl) { if (UserName == "sachin" & Password == "sachin@7777") { FormsAuthentication.SetAuthCookie(UserName, RememberMe); return Redirect(ReturnUrl); } return View("Login"); }
Note 1: Here, the Return URL is the URL of the resource that the user tried to access, but since they were not logged in, they were redirected to the login page. So after logging in, he will again redirect to the page he wanted to access before logging in.
Note 2: SetAuthCookie is a static method of the FormsAuthentication class, which creates a cookie on the user's browser with the name specified as the first parameter. RememberMe is a Boolean value that signifies whether the user wants it to persist in their browser or not.
@{ ViewBag.Title = "Login"; } <h2>Login</h2> <form action="/Home/Login" method="post"> User Name:@Html.TextBox("UserName") Password :@Html.Password("Password") Remember Me:@Html.CheckBox("RememberMe") @Html.Hidden("ReturnUrl", new { @value = ViewBag.ReturnUrl}) <button type="submit">Login</button> </form>
- Step 3: Mark any method that you think needs validation with the Authorize attribute.
[Authorize] public ActionResult Post() { return View(); }
Now, run the application and try to access the above method; you will be redirected to the Login page. After login, you will be automatically redirected to the Post Action method.
Authorization
Authorization means whether a user has the right to access the resource or not. Sometimes, a user might be a registered user and currently logged in, but it doesn't make sense to give them admin rights. So, if any logged-in user tries to access the admin page, you will restrict them from accessing any such resource.
- Step 1: For authorization, we use the second overloaded version of the Authorize attribute to specify the role. We can even specify multiple roles as comma-separated values. If a user is authenticated and has the necessary role to access the resource, then they can access the resource.
[Authorize(Roles="Admin,Editor")] public ActionResult Post() { return View(); }
Here, only users who are in the role of Admin or Editor can access the Post Action Method.
- Step 2: Previously, we have created a cookie having two parameters, i.e., Name and RememberMe. The first parameter signifies the name of the cookie, and the second parameter signifies whether it is a persistent cookie or not.
However, the requirement has now changed; we also have to check the role, so first we need to insert the role into the cookie. But the cookie doesn't accept multiple parameters, so we use the FormsAuthentication ticket, add the role, then insert this ticket into the cookie. So, modify your Login method like below:
[HttpPost] public ActionResult Login(string UserName, string Password, bool RememberMe, string ReturnUrl) { if (UserName == "sachin" & Password == "sachin@7777") { string roles="Admin,Editor"; //must be comma separated. FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, UserName, System.DateTime.Now, DateTime.Now.AddHours(24), RememberMe, roles); var EncryptCookie = FormsAuthentication.Encrypt(ticket); HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, EncryptCookie); HttpContext.Response.Cookies.Add(cookie); return Redirect(ReturnUrl); } return View("Login"); }
Let's understand the above code.
- First, we have created roles as a string with a comma-separated value. I have used the hardcoded value; you can retrieve user roles from the database.
- Now, we created a FormsAuthentication ticket by specifying version as 1, name as the username, created on as the current date, expiry as 24 hours, Is persistent as RememberMe, and Userdata as roles.
- Then we have encrypted the ticket.
- Created a cookie with the name as the ticket name and inserted the encrypted ticket into it.
- Now, in the response object, we have added the cookie.
- Step 3: We have now added the cookie that contains the ticket and the ticket actually contains the role. But our work is not yet completed. The Authorize attribute fetches roles from the HttpContext object. Therefore, we will need to create a generic principal with roles assigned to it in the Application_AuthenticateRequest event, which occurs in the global scope.asax.
protected void Application_AuthenticateRequest(object sender,EventArgs e) { var authcookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName]; if(authcookie!=null){ FormsAuthenticationTicket authticket = FormsAuthentication.Decrypt(authcookie.Value); if(authticket!=null&!authticket.Expired){ var roles = authticket.UserData.Split(); HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(authticket), roles); } } }
Now run the application, and it will work as expected.
HttpContext
It becomes important to understand what HttpContext is. HttpContext can be considered a container, which contains the request object and the response object. Whenever an HTTP request is initiated, an HttpContext object is created by IIS .