3/20/16

AngularJS SPA Splash View to prevent Blank or Flicker Effect During App Initialization

Depending on network latency and the number of components that an AngularJS SPA contains, the application can initially just show static HTML content until AngularJS completes its initialization process and compiles the HTML/DOM to show the dynamic content as intended. This usually causes a flicker as empty or unbound content is displayed before the dynamic content is bound to the view.

To better understand what happens, let’s take a look at an example of a SPA that takes a couple of seconds to load. This delays causes the app to display the uninitialized state of the application or static content.

Slow initialization with no splash view

As we can see in the example, the static content is displayed with some missing content that is only added after the app is initialized.  The content in the brackets can be replaced by using the ngBind directive, but this only removes the brackets and empty content is displayed instead.

Note that to simulate a delay, we are manually loading the app using angular.bootstrap and a timeout to bootstrap the app onto the DOM.


//simulates the delay in loading the app/module
 setTimeout(function () {
                angular.bootstrap( document, [ "app" ] );
            },2500);



Now that we see the challenge, let’s review how the AngularJS initialization process works:
AngularJS Initialization Process

The initialization process consists of the following steps:

  • Browser loads the static HTML content. (DOMContentLoaded event)
  • AngularJS 
    • Looks for the ngApp directive (automatic initialization)
    • Loads the module associated with the ngApp directive
    • Creates the injectors
    • Compiles the DOM to create the dynamic HTML content
With the understanding on how the initialization process works, we can make changes and improve the user experience by providing a splash view while our app is loading.  A simple splash view can be implemented by adding HTML static content that is displayed while the app initializes. Once the app is initialized, we use the ngIf directive to remove the splash content.

To hide the app content and prevent flickering, we use the ngCloak directive which helps us prevent displaying any un-compiled (HTML with directives but no data) HTML content. This is what hides the app and controller content until the initialization process is complete.


With these changes, we can now see how the loading app animation is displayed while the app is initializing. Once the app executes its run function, the $rootScope variables are set, and the ngCloak directive is removed by AngularJS from the DOM which makes the app elements visible. 

I hope that this provides some insight on AngularJS initialization process and how to provide a loading views to your apps.

Thanks.

3/6/16

Web API Anti-Forgery Token AngularJS Client

When building APIs for public web apps, we cannot add the authorization filters to the APIs because the end users do not have to authenticate. We can however add some security measures to the API which would protect it by allowing only requests from the same origin with an anti-forgery token thus allowing us to handle CSRF (Cross Site Request Forgery) threats. Let's take a look at how we can do this.

Common Scenario

  We have a public web app that allows users to send a contact request from the browser. We do not want to leave our API open to the world. We just want same origin requests to have access to our API. As an example, our web app is hosting a contact form as shown below. The contact request is sent by calling an API with anonymous access.

Contact Page

 The Sample Project can be found at this location:  https://github.com/ozkary/aspnet-samples


Anti-Forgery Token

 An anti-forgery or request verification token is used to protect a resource from a cross-site request forgery (CSRF). The ASP.NET MVC Framework provides an HTML helper that creates the token for us.  This is what we can use to insert a token in our contact form.

HTML Markup Design Mode

We use the Html helper with Razor syntax in our HTML markup as shown below:


<form method="post" id="contact" novalidate name="contactForm">
            @Html.AntiForgeryToken()


HTML Markup Output

After the page is rendered on the browser, the result of the AntiForgeryToken call produces the following HTML mark-up:


<input name="__RequestVerificationToken" type="hidden"
value="AMJ-A-9FvyoZMVpsapx2Dxr4drOUX_616Evo-Xxaq2ooscMXKOZG-qdQ3gzI0y4cekVotAaTql69J5yD1DCGqK4zzYM6c36Gm8DEtuxQkbw1" />



The helper call creates a cookie (_RequestVerificationToken) and a hidden input field with the same name and value. The idea is that this token becomes a signature of the forms that are served by our servers.   When the form is post back, both the cookie and input field are sent to the server for validation. This is when the anti-forgery validation takes place on a typical MVC controller action. In our case, we are using an API Controller, so let's take a look at this approach.

AngularJS Module:

To test our API, we create an AngularJS client. Our AngularJS module has a controller and service. The controller handles the interaction with the contact view. The service manages the request to our contact API.

//angular module
var app = angular.module('appContact', ['ngRoute', 'ui.bootstrap']);

//angular service
app.factory('app.svc.contact', ['$http', svcContact]);

//contact controller
app.controller('app.ctrl.contact', ['$scope', '$uibModal', 'app.svc.contact', 'app.const.contact','$log', ctrlContact]);


Since we are using an AngularJS service to send our requests instead of submitting a form, our approach is a bit different. We still need to send the token. but when we send the request via our service, we need to add the token in the request header. This is what an action filter will be parsing and validating on the server.


(function (angular) {
    'use strict';
    var app = angular.module('appContact');
    app.factory('app.svc.contact', ['$http', svcContact]);
   
    function svcContact($http) {
        var baseRoute = '/api/contact/';
        return {
            add: add
        }

        function add(contact) {
            var token = contact.token;
            if (token && contact.sendToken) {
                $http.defaults.headers.post['X-XSRF-Token'] = contact.token;
            } else {
                delete $http.defaults.headers.post['X-XSRF-Token'];
            }

            return $http.post(baseRoute+"send", contact);
        }
    }
})(angular);


In our contact form, there is a checkbox that is bound to the model property sendToken. If the option to send the token on the form is not checked (defaults to true to send the token), we remove the token from the header. This allows us to test the case with no token.

Validate Token Action Filter

The action filter can be used in our API Controller to block access to the APIs when the token is not valid.  Let's take a look at what the code for our action filter looks like:


[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
    public sealed class ValidateHttpAntiForgeryTokenAttribute : ActionFilterAttribute
    {
        private const string HeaderTokenName = "X-XSRF-Token";
        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            if (actionContext == null)
            {
                throw new ArgumentNullException("actionContext");
            }

            var headers = actionContext.Request.Headers;          
            IEnumerable<string> tokens;

            try
            {
                if (headers.TryGetValues(HeaderTokenName, out tokens))
                {
                    var headerToken = tokens.FirstOrDefault();
                    var cookie = headers.GetCookies().Select(c => c[AntiForgeryConfig.CookieName]).FirstOrDefault();
                    var cookieToken = cookie != null ? cookie.Value : null;
                    AntiForgery.Validate(cookieToken, headerToken);
                }
                else
                {

                    string msg = HttpStatusCode.ExpectationFailed.ToString()+ " - " + HeaderTokenName;
                    throw new InvalidOperationException(msg);
                }   
            } catch{
                actionContext.Response = new HttpResponseMessage
                {
                    RequestMessage = actionContext.ControllerContext.Request,
                    StatusCode = HttpStatusCode.Forbidden,
                    ReasonPhrase = "Invalid Token"
                };                            
            }
                                          
            base.OnActionExecuting(actionContext);
        }      
    }



The code above checks the header for the X-XSRF-Token value and the anti-forgery cookie.  It then calls the AntiForgery.Validate helper method to validate that both values are matching. If this is not the case, or there is no token found on the header and exception is thrown and a forbidden (403) error is returned essentially blocking the access to the API.

API Controller with Action Filter

Since our action filter has been defined with the attribute usage of class or method, we can add the attribute at the method or class level in our API controller. In our case, we are using it at the class level. By decorating our class with this attribute (ValidateHttpAntiForgeryToken), we are adding a declarative security validation to all the methods in the class. 


    [RoutePrefix("api/contact")]
    [helper.ValidateHttpAntiForgeryToken]
    public class ContactApiController : ApiController
    {
        // POST: api/Contact       
        [HttpPost]
        [ResponseType(typeof(Contact))]
        [Route("send")]
        public async Task<IHttpActionResult> send(Contact contact)
        {
            IHttpActionResult result = null;

            if (!ModelState.IsValid)
            {
                result = BadRequest(ModelState);
            }
            else
            {
                //simulate delay for proceesing information
                await Task.Delay(1500);
                contact.Created = DateTime.Now;
                //TODO process contact information
                result = Ok(contact);               
            }

            return result;
        }
}



If the token is not valid, the action filter does not allow access to the api/contact/send route. In the code above, the send method just simulates some asynchronous action (delay) and adds the created date value to the contact request. It then just returns the contact information back to the client.

Demo the Contact Module

When we run the demo application, we can test with or without the token. If we look at the browser dev tools, we can examine the response for each of the requests. This can show us how a request that uses the token returns a HTTP status of 200 (success). Compare it to the request without the token which returns a status of 403 (forbidden – or no token).



When we look at the successful request header, we can see that both the cookie and custom header entries are sent. (highlighted below)



Not a Jason Web Token

We should note that the anti-forgery token is not a Jason Web Token (JWT) which is used for OAuth authentication when there is a login requirement. This is just a validation token to ensure that a request comes from the same origin (domain) and from an authorized client app.

I hope this is able to show you a simple way to protect your public APIs.

The Sample Project can be found at this location:  https://github.com/ozkary/aspnet-samples

Originally Published by ozkary.com