Click here to Skip to main content
15,886,052 members
Articles / Web Development / ASP.NET / ASP.NET Core
Article

ASP.NET CORE Token Authentication and Authorization using JWT (No Cookies) – Part 2

Rate me:
Please Sign up or sign in to vote.
4.80/5 (23 votes)
10 Oct 2019CPOL14 min read 73.3K   2.4K   44   31
This article explains how to implement Token Authentication and Authorization using JWT in ASP.NET CORE.

Contents

Introduction

Part 1 covered how to Authenticate users with their login credentials. In Part 2, we are going see how to implement Authorization for users. In other words, grant permission for users to use certain part of the application or all of it. User Permission (Authorization) is handled in 3 levels, Controller, Action method and View page. For this, we will be writing custom attributes and few extension methods. Part 2 includes separate LoginDemo.sln project implemented with all the topics covered below for Authorization.

User Roles

Let’s have three different roles:

  1. Director
  2. Supervisor
  3. Analyst

Create a static class Roles.cs to define user roles. These are parameter values which would be passed into custom attribute.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace LoginDemo.CustomAttributes
{
    public static class Roles
    {
        public const string DIRECTOR = "DIRECTOR";
        public const string SUPERVISOR = "SUPERVISOR";
        public const string ANALYST = "ANALYST";
    }
}

Let’s add “WRITE” permission to User collection.

C#
//Using hard coded collection list as Data Store for demo purpose. 
//In reality, User data comes from Database or some other Data Source - JRozario
private List UserList = new List
{
    new User { USERID = "jsmith@email.com", PASSWORD = "test", 
    EMAILID = "jsmith@email.com", FIRST_NAME = "John", 
    LAST_NAME = "Smith", PHONE = "356-735-2748", 
    ACCESS_LEVEL = Roles.DIRECTOR.ToString(), WRITE_ACCESS = "WRITE_ACCESS" },
    new User { USERID = "srob@email.com", PASSWORD = "test", 
    FIRST_NAME = "Steve", LAST_NAME = "Rob", 
    EMAILID = "srob@email.com", PHONE = "567-479-8537", 
    ACCESS_LEVEL = Roles.SUPERVISOR.ToString(), WRITE_ACCESS = "WRITE_ACCESS" },
    new User { USERID = "dwill@email.com", PASSWORD = "test", 
    FIRST_NAME = "DJ", LAST_NAME = "Will", 
    EMAILID = "dwill@email.com", PHONE = "599-306-6010", 
    ACCESS_LEVEL = Roles.ANALYST.ToString(), WRITE_ACCESS = "WRITE_ACCESS" },
    new User { USERID = "JBlack@email.com", PASSWORD = "test", 
    FIRST_NAME = "Joe", LAST_NAME = "Black", 
    EMAILID = "JBlack@email.com", PHONE = "764-460-8610", 
    ACCESS_LEVEL = Roles.ANALYST.ToString(), WRITE_ACCESS = "" }
};   

How to Create Custom Authorize Attribute?

User permissions are handled using attributes which can be used to decorate controller and action methods. When an attribute is decorated at top of the controller, then it applies to all the action methods in that controller. Likewise, each action method can also be decorated.

To create a customized attribute for authorization, we will be using IAuthorizationFilter interface and TypeFilterAttribute. IAuthorizationFilter implements the actual filter for our authorization using OnAuthorization method. TypeFilterAttribute is used for dependency injection. For more information, please refer to Microsoft docs.

Let’s write custom attribute for authorization:

C#
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace LoginDemo.CustomAttributes
{
    public class AuthorizeAttribute : TypeFilterAttribute
    {
        public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
        {
            Arguments = new object[] { claim };
        }
    }

    public class AuthorizeFilter : IAuthorizationFilter
    {
        readonly string[] _claim;

        public AuthorizeFilter(params string[] claim)
        {
            _claim = claim;
        }

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
            var claimsIndentity = context.HttpContext.User.Identity as ClaimsIdentity;

            if (IsAuthenticated)
            {
                bool flagClaim = false;
                foreach (var item in _claim)
                {
                    if (context.HttpContext.User.HasClaim(item, item))
                        flagClaim = true;
                }
                if (!flagClaim)
                    context.Result = new RedirectResult("~/Dashboard/NoPermission");
            }
            else
            {
                context.Result = new RedirectResult("~/Home/Index");
            }
            return;
        }
    }
}

As you can see, there are two classes, AuthorizeAttribute and AuthorizeFilter in the above code.

AuthorizeAttribute

C#
public class AuthorizeAttribute : TypeFilterAttribute
{
    public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
    {
        Arguments = new object[] { claim };
    }
}

This class implements TypeFilterAttribute which is used by dependency container to create object of AuthorizeFilter. AuthorizeAttribute constructor takes array of string as parameter. Since it is an array, we can pass roles with comma (,) separated values [E.g.: “Director”, “Supervisor”, “Analyst”].

AuthorizeFilter

C#
public class AuthorizeFilter : IAuthorizationFilter

AuthorizeFilter class implements IAuthroizationFilter interface with OnAuthorization method.

C#
public AuthorizeFilter(params string[] claim)
{
    _claim = claim;
}

AuthorizeFilter constructor takes array of strings as parameter, which is passed from AuthorizeAttribute.

C#
public void OnAuthorization(AuthorizationFilterContext context)

OnAuthorization method comes with AuthorizationFilterContext. From this “context”, we can get the HttpContext and User Identity which carries the claims.

C#
var IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
var claimsIndentity = context.HttpContext.User.Identity as ClaimsIdentity;

From the “context” object, we get to know if the user is authenticated or not. With same “context” object, we get the user claims identity collection.

C#
if (IsAuthenticated)
    {
        bool flagClaim = false;
        foreach (var item in _claim)
        {
            if (context.HttpContext.User.HasClaim(item, item))
                flagClaim = true;
        }
        if (!flagClaim)
            context.Result = new RedirectResult("~/Dashboard/NoPermission");
    }
    else
    {
        context.Result = new RedirectResult("~/Home/Index");
    }
    return;

In this block, we check if user is authenticated or not. If not, we redirect the user to Login page. Then we loop through ‘claim’ array collection (This array collection would be user roles which we would be passing as parameter from controller, e.g.: Authorize[new string[] “Director”, “Supervisor”]).

Here, context.HttpContext.User.Identity object carries user permissions in claims object collection. In Part 1, we created token with user claims and loaded into context.HttpContext.User identity object. Please refer to Part 1 under “Middleware app.UseAuthentication()”.

Inside the loop, we check if the parameter value(s) is in the user identity claims collection using “HasClaim” method. In Part 1, it's explained how User Claims Identity object collection is created and assigned to HttpContext. It’s the same HttpContext from which we are getting the User Claims and checking against the parameter value(s) passed.

If the parameter value is found in the claims collection, then the user is authorized and let pass through the Http Request. If not, we redirect the user to a page which says, you do not have permission. In this article, it's redirected to “No Permission” page.

How to Set Permission in Controller and Action Method Level?

Now that we have created a custom attribute to authorize, it’s time to use it in the controller.

C#
public class DashboardController : Controller
{
    [Authorize(Roles.DIRECTOR)]
    public IActionResult DirectorPage()
    {
        return View("DirectorPage");
    }

    [Authorize(Roles.SUPERVISOR)]
    public IActionResult SupervisorPage()
    {
        ViewBag.Message = "Permission controlled through action Attribute";
        return View("SupervisorPage");
    }

    [Authorize(Roles.ANALYST)]
    public IActionResult AnalystPage()
    {
        return View("AnalystPage");
    }
}

Action methods are decorated with [Authorize] attribute. We are passing user roles as parameters. Director, Supervisor and Analyst each have separate action method.

C#
[Authorize(Roles.DIRECTOR)]
public IActionResult DirectorPage()
{
    return View("DirectorPage");
}

In this ‘DirectorPage’ action method, we have set the Authorize attribute with Roles.DIRECTOR. Which means, only users with Director role can view the page. If Supervisor or Analyst try to access the page, then they will be redirected to “No Permission” page from the AuthorizeFilter class.

Roles.DIRECTOR is the parameter which we are passing to AuthorizeFilter class. Authorize filter class takes the “Roles.DIRECTOR” parameter value and checks if the value is found in User Claims Identity collection. If it’s found, then the user is allowed to pass through the action method and the user is allowed to see “DirectorPage”. If the logged in user is a Supervisor or Analyst, then they cannot see the ‘DirectorPage’.

If we want to allow multiple users to have access to action method, then we can pass the parameter values with comma(,) separated values like below:

C#
[Authorize(Roles.DIRECTOR, Roles.SUPERVISOR, Roles.ANALYST)]
public IActionResult AllRoles()
{
    return View();
}

To set permission on controller level, we can set it like below. When Authorize attribute is set on controller level, then the permission is set for all action methods in that controller.

C#
        namespace LoginDemo.Controllers
        {
	        [Authorize(Roles.DIRECTOR, Roles.SUPERVISOR, Roles.ANALYST)]    
            public class YourController : Controller
            {
                public IActionResult Action1()
                {
                    return View();
                }

                public IActionResult Action2()
                {
                    return View();
                }

                public IActionResult Action3()
                {
                    return View();
                }
            }
        }    

Extension Methods to Check User Permission

Filter attributes can only be used in controller. But there are places where we need to check user permission and we can’t rely only on filter attributes. What if we want to check user roles inside action method in an “If” condition? What if we want to check user roles in View Page? In this case, we can’t use filter attributes. For this reason, we are going to have extension methods. One for controller action method and one for View Page.

Let’s create a static class PermissionExtension.cs to have extension method for controller.

C#
namespace LoginDemo.CustomAttributes
{
    public static class PermissionExtension
    {
        public static bool HavePermission(this Controller c, string claimValue)
        {
            var user = c.HttpContext.User as ClaimsPrincipal;
            bool havePer = user.HasClaim(claimValue, claimValue);
            return havePer;
        }
        public static bool HavePermission(this IIdentity claims, string claimValue)
        {
            var userClaims = claims as ClaimsIdentity;
            bool havePer = userClaims.HasClaim(claimValue, claimValue);
            return havePer;
        }
    }
}

Extension method is implemented for use in controller and in view page. These methods take claims value as parameter and checks if that claim exists in the HttpContext.User claims object collection. To invoke the method in controller, we are using “this Controller” as first parameter which makes it an extension method. For use in view page, it's “this IIdentity”.

These extension methods are for convenience and to keep things in one place. You can also directly use “User.HasClaims()” in your action method and in view page.

How to Check Permission Within Action Method (In-Line Code)?

Say suppose you have an action method and want to use it for more than one role. But you want to do different logic or get data from different source or something specific based on role. In this case, we need to know the logged in user role inside the action method.

In the below action method, all logged in users have permission. We check user roles inside action method. Inside the action method, I want to return a different view based on role. For “Supervisor”, it returns “SupervisorPage” view, for “Analyst” it returns “AnalystPage” view. This is only for example, it can be used to implement different logic, get data from different source, etc.

It’s a simple if condition with extension method HavePermission() which controls the permission.

C#
public IActionResult SupervisorAnalystPage()
{
    ViewBag.Message = "Permission controlled inside action method";
    if (this.HavePermission(Roles.SUPERVISOR))
        return View("SupervisorPage");

    if (this.HavePermission(Roles.ANALYST))
        return View("AnalystPage");

    return new RedirectResult("~/Dashboard/NoPermission");
}

Above, we have created HavePermission() extension method. Using the extension method, we control the permission with simple “if” condition. Extension method checks for user role in HttpContext user claims object collection.

C#
this.HavePermission(Roles.SUPERVISOR)
this.HavePermission(Roles.ANALYST))

How to Control User Permission Inside View Page?

What if we want to control something in View Page based on user role/permission? How to show and hide different top "Menu" items each for Director, Surpervisor and Analyst? For this we need to know user permission in view page level. We want somethis like, if the user is Director, then show/hide a menu, button etc,. 

Let's say we want to disable and enable “Save” button based on user role/permission. This can be done with the above HavePermission() extension method. In this example, button is disabled for user who do not have WRITE access. In this view page, there is a form which takes input from user. This form can only be saved by users who have WRITE access. For others, “Save” button is disabled and they cannot save the form data.

HTML
@if (User.Identity.HavePermission("WRITE_ACCESS") == true)
{
    <button type="button"</button<>
}
else
{
    <button type="button" disabled="disabled"<</button>
}

Above, we have created two HavePermission() extension methods, one for Controller and one for View Page. Here HavePermission() checks for “WRITE_ACCESS” claim in HttpContext user claims object collection. If true, we enable save button, otherwise it's disabled. Like this, we can control “What to show?” and “How to show?” view page content for users based on their role/permission.

C#
@if (User.Identity.HavePermission("WRITE_ACCESS") == true)

Login to demo project as Analyst JBlack@email.com who does not have WRITE permission. In the landing page, click on “See how page level permission works”.

Image 1

You can see “Save” button is disabled and user cannot save the form data.

Image 2

For other users, they have WRITE permission and “Save” button is enabled to save form data.

Image 3

How to Handle Unauthorized Request?

How to restrict users who are not signed-in to application, but directly tries to access a page by giving the URL of that page? Say application URL is http://www.yourapplication.com which takes users to login page, But there is a page for “Supervisor” and the user knows the URL of that page, if “Supervisor” page URL is http://www.yourapplication.com/supervisor and user directly enters the URL in browser without login, then it is an unauthorized access. Let’s see how to handle this.

We are going to implement a filter attribute (similar to AuthorizeFilter). But this we will be using it in controller level. Create a class UnAuthorized.cs. Implement IAuthorizationFilter interface and handle the request in OnAuthroization() method. We don’t have to pass any parameter values for this filter. This filter only checks for Authentication.

C#
namespace LoginDemo.CustomAttributes
{
    public class UnAuthorizedAttribute : TypeFilterAttribute
    {
        public UnAuthorizedAttribute() : base(typeof(UnauthorizedFilter))
        {
            //Empty constructor
        }
    }
    public class UnauthorizedFilter : IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            bool IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
            if (!IsAuthenticated)
            {
                context.Result = new RedirectResult("~/Home/Index");
            }
        }
    }
}

Get user identity object from AuthorizationFilterContext, from which we can find out if the user is authenticated.

C#
bool IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;

If the user is not authenticated or login user, redirect the user to login page.

C#
if (!IsAuthenticated)
{
    context.Result = new RedirectResult("~/Home/Index");
}

This you can use it in controller level by simply adding the [UnAuthorized] attribute on the top, which applies for all the action methods in that controller. Authorize attribute also checks for IsAuthenticated, so one can ask what is the use of [UnAuthorized] attribute. There could be a case where permission is not controlled, which means all the user can have permission for an action method and there is no [Authorize] attribute for that action method. In the below controller, SupervisorAnalystPage() action method does not have [Authorize] attribute. Any user who knows the URL can try to access this. Another reason to have [UnAuthorized] attribute is to handle Ajax calls, which we will see in the next topic.

C#
namespace LoginDemo.Controllers
{
    [UnAuthorized]
    public class DashboardController : Controller
    {
        [Authorize(Roles.DIRECTOR)]
        public IActionResult DirectorPage()
        {
            return View("DirectorPage");
        }

        [Authorize(Roles.SUPERVISOR)]
        public IActionResult SupervisorPage()
        {
            ViewBag.Message = "Permission controlled through action Attribute";
            return View("SupervisorPage");
        }

        [Authorize(Roles.ANALYST)]
        public IActionResult AnalystPage()
        {
            return View("AnalystPage");
        }

        public IActionResult SupervisorAnalystPage()
        {
            ViewBag.Message = "Permission controlled inside action method";
            if (this.HavePermission(Roles.SUPERVISOR))
                return View("SupervisorPage");

            if (this.HavePermission(Roles.ANALYST))
                return View("AnalystPage");

            return new RedirectResult("~/Dashboard/NoPermission");
        }
    }
}

How to Handle and Authenticate/Authorize Ajax Calls?

Before we implement the code, let’s see “Why?” “Where?” and “How?” to do this.

Users can leave the application idle till the session expires. When session expires, token is not available and user is logged off from application. But the page is still open in the browser. Not knowing that the session expired and user is logged off, users can click anything in the page. If that click is a regular http call (where page reload happens or something), then the user would be automatically redirected to login page. If that click happens to be an Ajax call, then the page would remain as it is and redirection to login page will not happen.

P.S.: Ajax calls are also an HTTP call. Regular HTTP call and Ajax call differ the way it is called. Mostly regular HTTP calls are directly executed by browser. Ajax calls are made from code(javascript/jquery) and then the browser executes the call. For regular HTTP call, the return result can be View Page or Json result, etc. But with Ajax call, it's only Json result.

  • Extension method to identify Ajax calls
  • Handle Ajax call Authentication in [UnAuthorized] filter attribute
  • Handle Ajax call Authorization in [Authorize] filter attribute
  • Catch Http Status code for Ajax call in jquery and redirect to login page

Extension Method to Identify Ajax Calls

First, we need to know if a request is an Ajax call or a regular HTTP call. This we can find through Http Headers. Second, we are going to create an extension method so that we can use it with the Http Request object.

Create a static class AjaxExtension.cs and a static method “IsAjaxRequest”. Add the below code:

C#
        using Microsoft.AspNetCore.Http;
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Threading.Tasks;

        namespace LoginDemo.CustomAttributes
        {
            public static class AjaxExtension
            {
                //HttpRequest Extension method to 
                //check if the incoming request is an AJAX call - JRozario 
                public static bool IsAjaxRequest(this HttpRequest request)
                {
                    if (request == null)
                        throw new ArgumentNullException("request");

                    if (request.Headers != null)
                        return request.Headers["X-Requested-With"] == "XMLHttpRequest";
                    return false;
                }
            }
        }    

It’s a simple extension method implemented with HttpRequest object. We can find if a request is an Ajax call with one of the Http Headers "X-Requested-With". If this header value is “XMLHttpRequest”, then it's an Ajax call.

C#
return request.Headers["X-Requested-With"] == "XMLHttpRequest";

Handle Ajax Call Authentication in [UnAuthorized] Filter Attribute

Now we have an extension method IsAjaxRequest() to find if a request is Ajax call. Let’s add a condition to [UnAuthorized] filter attribute using the extension method.

C#
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;

namespace LoginDemo.CustomAttributes
{
    public class UnAuthorizedAttribute : TypeFilterAttribute
    {
        public UnAuthorizedAttribute() : base(typeof(UnauthorizedFilter))
        {
            //Empty constructor
        }
    }
    public class UnauthorizedFilter : IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            bool IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
            if (!IsAuthenticated)
            {
                if (context.HttpContext.Request.IsAjaxRequest())
                {
                    context.HttpContext.Response.StatusCode =
                    (int)HttpStatusCode.Forbidden; //Set HTTP 403 Forbidden - JRozario
                }
                else
                {
                    context.Result = new RedirectResult("~/Home/Index");
                }
            }
        }
    }
}

As you can see the condition context.HttpContext.Request.IsAjaxRequest() which checks if it’s an Ajax call. If it’s an Ajax call, we do not redirect the call. Redirection will not work with Ajax calls. Return result for Ajax call can only be a data. Here, we set the Http Response Status Code to 403 – Forbidden. This says Ajax call ended up in Error and the call was unsuccessful. All the Ajax calls in this demo are written in jquery. Jquery Ajax calls can get back Http Status code from HTTP Response object(header). If it’s a regular Http call, then redirect to Login page. We are done with authenticating Ajax request. Next, we need to redirect the user to login page.

Handle Ajax Call Authorization in [Authorize] Filter Attribute

For Authentication, we set the Http Status code as 403 Forbidden. For Authorization, we are going to set the Http Status Code as 401 Unauthorized. It’s the same logic where we check if it’s an Ajax call using extension method IsAjaxRequest(). If it’s an Ajax call, set Http Status Code as 401 Unauthorized. If it’s a regular Http call, then redirect to “No Permission” page.

C#
        using Microsoft.AspNetCore.Mvc;
        using Microsoft.AspNetCore.Mvc.Filters;
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Net;
        using System.Security.Claims;
        using System.Threading.Tasks;

        namespace LoginDemo.CustomAttributes
        {
            public class AuthorizeAttribute : TypeFilterAttribute
            {
                public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
                {
                    Arguments = new object[] { claim };
                }
            }

            public class AuthorizeFilter : IAuthorizationFilter
            {
                readonly string[] _claim;

                public AuthorizeFilter(params string[] claim)
                {
                    _claim = claim;
                }

                public void OnAuthorization(AuthorizationFilterContext context)
                {
                    var IsAuthenticated = 
                           context.HttpContext.User.Identity.IsAuthenticated;
                    var claimsIndentity = 
                           context.HttpContext.User.Identity as ClaimsIdentity;
            
                    if (IsAuthenticated)
                    {
                        bool flagClaim = false;
                        foreach (var item in _claim)
                        {
                            if (context.HttpContext.User.HasClaim(item, item))
                                flagClaim = true;
                        }
                        if (!flagClaim)
                        {
                            if (context.HttpContext.Request.IsAjaxRequest())
                                context.HttpContext.Response.StatusCode = 
                                (int)HttpStatusCode.Unauthorized; //Set HTTP 401 
                                                                  //Unauthorized - JRozario
                            else
                                context.Result = 
                                     new RedirectResult("~/Dashboard/NoPermission");
                        }
                    }
                    else
                    {
                        if (context.HttpContext.Request.IsAjaxRequest())
                        {
                            context.HttpContext.Response.StatusCode = 
                              (int)HttpStatusCode.Forbidden; //Set HTTP 403 - JRozario
                        }
                        else
                        {
                            context.Result = new RedirectResult("~/Home/Index");
                        }
                    }
                    return;
                }
            }
        }    

Catch Http Status Code for Ajax Call in Jquery and Redirect to Login Page

Here, we are going to get back the HTTP Status Code set in [UnAuthorized] filter attribute. It should be done from the place where Ajax call is made. As said before, in this demo, we are using Jquery to make Ajax calls. Jquery comes with a method .ajaxError to catch errors happened during Ajax call. But are we going to write .ajaxError method for each and every Ajax call? No, .ajaxError method can be implemented globally for the whole application which would handle errors for all Ajax calls. Only point, .ajaxError method should be available in that page. All we have to do, is to add it in a common page _Layout.cshtml which is the master page. Add this script to your _Layout.cshtml or any master page you use.

HTML
        <script>
            $(document).ajaxError(function (xhr, result) {
                //Catch Ajax error with http status code 403 Forbidden 
                //and 401 Unauthorized – Jrozario
                if (result.status === 403) {
                    window.location.href = '/Home/Index';
                }
                if (result.status === 401) {
                    window.location.href = '/Dashboard/NoPermission';
                }
            });
        </script>    

.ajaxError gets the Http headers in one of the parameters, here, it's declared as “result”. Http header and status code 403 Forbidden set in [UnAuthorized] filter attribute is returned in “result” parameter. If the result.status is 403, then we redirect the user to login page from client side.

C#
if (result.status === 403) {
    window.location.href = '/Home/Index';
}

In the demo project, there is a page CheckAjaxCalls.cshtml where you can check authentication/authorization for Ajax call. To check for authentication, Login to demo project as a Director jsmith@email.com. You should see the below page.

Image 4

Click on the link “See how Authentication & Authorization handled for Ajax Calls”. In this page, you can check both authentication and authorization.

Image 5

The above screen has 3 buttons, “End Session”, “Authenticate Ajax Call” and “Authorize Ajax Call”. All 3 have JavaScript function (OnClick_EndSession, OnClick_AuthenticateAjaxCall, OnClick_AuthorizeAjaxCall) which makes the Ajax call. Below is the complete code with HTML tags.

HTML
<script>
    function OnClick_EndSession() {
        $.ajax({
            type: 'GET',
            url: '/Home/EndSession',
            data: {},
            cache: false,
            success: function (result) { }
        });
        alert("End of User Session,
        Click on Ajax Call button to autneticate Ajax calls,
        It should take you to login page.");
    }

    function OnClick_AuthenticateAjaxCall() {
        $.ajax({
            type: 'GET',
            url: '/Dashboard/AuthenticateAjaxCalls',
            data: {},
            cache: false,
            success: function (result) {
                if (result != "")
                    alert("Your session is still active,
                    end session to see how authentication for Ajax call works!");
            }
        });
    }

    function OnClick_AuthorizeAjaxCall() {
        $.ajax({
            type: 'GET',
            url: '/Dashboard/AuthorizeAjaxCalls',
            data: {},
            cache: false,
            success: function (result) {
                if (result != "")
                    alert("Your have permission for this Ajax call!");
            }
        });
    }
</script>

Below is the Controller action method for the 3 Ajax calls for the 3 buttons in the page which returns Json result. This code is in demo project DashboardController.cs file. You can see “AuthorizeAjaxCalls” action method has [Authorize] attribute set for Director and Supervisor.

C#
        //This action method is in Dashboard.cs
        public JsonResult AuthenticateAjaxCalls()
        {
            return Json(new {result = "success" });
        }

        [Authorize(Roles.DIRECTOR, Roles.SUPERVISOR)]
        public JsonResult AuthorizeAjaxCalls()
        {
            return Json(new { result = "success" });
        }

        //This action method is in HomeController.cs 
        public JsonResult EndSession()
        {
            HttpContext.Session.Clear();
            return Json(new {result = "success"});
        } 

 

Now click on “Authenticate Ajax Call” button. If the session is still active, you should get an alert message.

Image 6

To end session, click on “End Session”, then click on “Authenticate Ajax Call”. You should be redirected to login page.

Image 7

To check Authorization, login to demo project as JBlack@email.com who is in Analyst Role. In this demo, Ajax call is only for Director and Supervisor. Analysts do not have permission for the Ajax call, so they will be redirect to “No Permission” page. After login, click the link “See how Authentication & Authorization handled for Ajax Calls”. In next page, click “Authorize Ajax Call”. Since JBlack@email.com is an Analyst, he does not have permission and will be redirected to “No Permission” page.

Image 8

Image 9

If you log in as a Director or Supervisor, then you would see an alert message when you click on “Authorize Ajax Call”.

Image 10

LoginDemo.sln Project

Fully working LoginDemo.sln project is available for download. See the below screen shots for quick glimpse of the LoginDemo.sln project.

Login Page

Image 11

Landing Page

Top banner shows signed in user name and his role for all the pages. Click on the given links to check different scenarios.

Image 12

Part 3

In Part 3, we will cover how to do Forgot and Reset password.

ASP.NET CORE Token Authentication and Authorization using JWT (No Cookies) – Part 1

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
.NET Solution Architect and Developer.

C#, .NET, .NET Core, JQuery, AngularJS, Angular, RestFul API Web Service, MSSQL Server

Comments and Discussions

 
QuestionPart-3 Pin
Vincent Paukgyi12-Nov-20 7:59
Vincent Paukgyi12-Nov-20 7:59 
QuestionUnable to get token value from session in middleware when request comes from jQuery Pin
Member 1359048924-Aug-20 18:22
Member 1359048924-Aug-20 18:22 
AnswerRe: Unable to get token value from session in middleware when request comes from jQuery Pin
Joseph Rozario28-Aug-20 5:58
Joseph Rozario28-Aug-20 5:58 
QuestionExpired or revoked token Pin
Member 1491637218-Aug-20 3:39
Member 1491637218-Aug-20 3:39 
AnswerRe: Expired or revoked token Pin
Joseph Rozario19-Aug-20 5:18
Joseph Rozario19-Aug-20 5:18 
GeneralRe: Expired or revoked token Pin
Member 1491637219-Aug-20 9:29
Member 1491637219-Aug-20 9:29 
GeneralRe: Expired or revoked token Pin
Joseph Rozario21-Aug-20 10:26
Joseph Rozario21-Aug-20 10:26 
QuestionFor APIs Pin
clark1031-Mar-20 21:29
clark1031-Mar-20 21:29 
GeneralMy vote of 4 Pin
Federico Navarrete14-Mar-20 7:01
professionalFederico Navarrete14-Mar-20 7:01 
GeneralRe: My vote of 4 Pin
Joseph Rozario16-Mar-20 4:15
Joseph Rozario16-Mar-20 4:15 
QuestionQuestion regarding User.HasClaim Pin
Member 1471292910-Jan-20 10:27
Member 1471292910-Jan-20 10:27 
AnswerRe: Question regarding User.HasClaim Pin
Joseph Rozario13-Jan-20 6:18
Joseph Rozario13-Jan-20 6:18 
AnswerRe: Question regarding User.HasClaim Pin
Federico Navarrete14-Mar-20 6:16
professionalFederico Navarrete14-Mar-20 6:16 
GeneralRe: Question regarding User.HasClaim Pin
Joseph Rozario16-Mar-20 4:19
Joseph Rozario16-Mar-20 4:19 
QuestionGreat article. Pin
Member710109-Jan-20 9:41
Member710109-Jan-20 9:41 
AnswerRe: Great article. Pin
Joseph Rozario10-Jan-20 5:27
Joseph Rozario10-Jan-20 5:27 
QuestionAccess vs. Permission Pin
Chris A Clarke11-Oct-19 8:36
Chris A Clarke11-Oct-19 8:36 
AnswerRe: Access vs. Permission Pin
Joseph Rozario11-Oct-19 10:51
Joseph Rozario11-Oct-19 10:51 
QuestionAjax Call Auth Suggestion Pin
Uffman10-Oct-19 20:20
Uffman10-Oct-19 20:20 
AnswerRe: Ajax Call Auth Suggestion Pin
Joseph Rozario11-Oct-19 8:21
Joseph Rozario11-Oct-19 8:21 
QuestionTypo Pin
John Perryn9-Oct-19 3:25
John Perryn9-Oct-19 3:25 
AnswerRe: Typo Pin
Joseph Rozario9-Oct-19 4:26
Joseph Rozario9-Oct-19 4:26 
AnswerRe: Typo Pin
Joseph Rozario11-Oct-19 10:53
Joseph Rozario11-Oct-19 10:53 
QuestionOne Suggestion Pin
mango-lion8-Oct-19 17:34
mango-lion8-Oct-19 17:34 
AnswerRe: One Suggestion Pin
Joseph Rozario9-Oct-19 4:23
Joseph Rozario9-Oct-19 4:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.