AngularJS with ASP.Net MVC

AngularJS with ASP.Net MVC (Part 11)

A step by step tutorial on how to use AngularJS with ASP.Net MVC

Precap

A secure AngularJS with ASP.Net MVC application has been created during the course of this tutorial. We have created a minimalist ASP.Net MVC application and implemented AngularJS framework on top of that. The security for the content is currently at the client side and is managed by AngularJS. In case you have not gone through the posts, I would recommend going through them. Here is the link for the first post in the series “AngularJS with ASP.Net MVC“.

Adding ASP.Net Authorization

For this tutorial we will be using authorization filters on the actions where we are expecting a login. If the user is logged in, it should send the token as a header named “X-Request-Template” (you are free to choose the name). Let’s create a new class file named “AuthorizedAccessFilterAttribute.cs” in folder Customization (in case you don’t have that folder, please go ahead and create in the project root). Here is the code for the class.

using System.Net;
using System.Web.Mvc;

namespace AngularJSwithMVC.Customization
{
    public class AuthorizedAccessFilterAttribute : FilterAttribute, IAuthorizationFilter
    {
        public string Role { get; set; }

        public void OnAuthorization(AuthorizationContext filterContext)
        {
            // check if the token is present
            var token = filterContext.RequestContext.HttpContext.Request.Headers["X-Request-Template"];

            // validate the token and access
            if (string.IsNullOrEmpty(token))
            {
                filterContext.Result = new HttpUnauthorizedResult();
            }
            else {
                // check for existence of role in claims
                if (!TokenHasRole(Role)) {
                    filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden);
                }
            }
        }

        private bool TokenHasRole(string role) {
            //TODO: check if the token as the claim for the given role
            return true;
        }
    }
}

The above class has defined a new Attribute which will be using to decorate the Index Action of DefaultController from Customer Area. Here is the code for that controller after the modification.

using AngularJSwithMVC.Customization;
using System.Web.Mvc;

namespace AngularJSwithMVC.Areas.Customer.Controllers
{
    public class DefaultController : Controller
    {
        [AuthorizedAccessFilter]
        // GET: Customer/Default
        public ActionResult Index()
        {
            return PartialView();
        }
    }
}

If you try to navigate to the Customer link, you will not be able to do that. This is because, this Action now requires a header named “X-Request-Template” containing the JWT.

Pass the Token

In our previous post, we have already created a JWT and the same is stored in the localStorage of the browser, which now we should pass to each template requests. For this we will be modifying the app.config.js file. Here is the code after the above said modification.

(function () {
    'use strict';

    angular.module('app')
        .config(['$locationProvider', '$templateRequestProvider', 'authorizationProvider', function ($locationProvider, $templateRequestProvider, authorizationProvider) {
            $locationProvider.hashPrefix('');

            // add the token to the view requests
            var headers = [];
            var httpOptions = {};

            headers['X-Request-Template'] = authorizationProvider.getToken();
            httpOptions.headers = headers;

            $templateRequestProvider.httpOptions(httpOptions);
        }]);
})();

The authorization service is also modified to read and return the token from the localStorage. Here is the code for same.

(function () {
    'use strict';

    angular.module('app')
        .provider('authorization', function () {
            var getToken = function () {
                return localStorage.getItem('access-token');
            }

            this.getToken = getToken;

            this.$get = ['$q', function ($q) {
                var service = {};

                service.chechAuthorization = function (role) {
                    var deferred = $q.defer();

                    var token = getToken();
                    // TODO: logic to see if the token has
                    // the permission required for the given role.
                    // here should be logic to extract the claims
                    // from token and get the roles from it
                    // we are simply checking as of now, if the token exists
                    if (token) {
                        deferred.resolve();
                    }
                    else {
                        alert("You are not authorized to view this section.")
                        deferred.reject();
                    }

                    return deferred.promise;
                };

                return service;
            }];
        });
})();

At this point, if you run the application, the token stored in the localStorage is picked up and sent with the template request and you are able to see the response.

Notifying Access Denied

If you are not having the token in the localStorage, and the user tries to access the Customer link, he simply sees no response. We should show some sort of message to user, telling what just happened. Let’s create an interceptor and attach it to the $httpProvider. Here a new js file named “app.templateRequestInterceptor.provider.js” created under the providers folder and is included in the BundleConfig.cs. Below is the code for the file.

(function () {
    'use strict';

    angular.module('app')
        .provider('templateRequestInterceptor', function () {
            this.$get = ['$q', function ($q) {
                var interceptor = {};

                interceptor.responseError = function (response) {
                    if (response.config.showAlertsOnError) {
                        if (response.status === 401) {
                            alert(response.config.message401 || "Access Denied!");
                        } else if (response.status === 403) {
                            alert(response.config.message403 || "Access Denied!");
                        }
                    }
                    return $q.reject(response);
                }

                return interceptor;
            }]
        });
})();

Above interceptor captures the error response and shows an alert, in case the request config has this enabled. The request config has these few new custom properties, which are added to the $httpOptions of the $templateRequestProvider. The newly created interceptor is also added to the $httpProvider as shown in the code block below for app.config.js file.

(function () {
    'use strict';

    angular.module('app')
        .config(['$locationProvider', '$templateRequestProvider', 'authorizationProvider', '$httpProvider', function ($locationProvider, $templateRequestProvider, authorizationProvider, $httpProvider) {
            $locationProvider.hashPrefix('');

            // add the token to the view requests
            var headers = [];
            var httpOptions = {};

            headers['X-Request-Template'] = authorizationProvider.getToken();
            httpOptions.headers = headers;
            httpOptions.showAlertsOnError = true;
            httpOptions.message403 = "You are not authorized to view this section.";
            httpOptions.message401 = "Please sign in to view this section.";

            $templateRequestProvider.httpOptions(httpOptions);

            $httpProvider.interceptors.push('templateRequestInterceptor');
        }]);
})();

We are Secured

That’s all, we are ready to showcase the application now. Here is how it is reacting now.

The End

I have tried to cover a few of many aspects to AngularJS with ASP.Net MVC here during the series of posts. I can’t say that this is an exclusive guide of best practices and all the things you can do with this combination of technology, but it is most common practice. It should help you kick start on the concept and let you move ahead with a fresh idea.

In case you are stuck up some where for this topic, or you may have any generic question related to this topic, please feel free to leave your comments below. I will do my best to answer your questions as early as possible. Your feedback is important to me and help me improve the content in upcoming posts.

While, we are talking about the upcoming blog, I guess, I have told you during the previous post that I am going to start a tutorial on TypeScript for Beginners. Please subscribe to the blog in case you are interested in following that and all upcoming topics.

Thanks for following the series “AngularJS with ASP.Net MVC“. See you next time … cheers!

Series Links

Part 1 Part 2 Part 3 Part 4 Part 5 Part 6 Part 7 Part 8 Part 9 Part 10

Leave a Reply