Protecting a Javascript Single Page Application with Laravel 4

September 12, 2014

Single Page Apps (SPA) built with frameworks like Backbone.js and AngularJS are all the rage these days. They allow for a web application to have a native-like feel while running in a browser by eliminating page reloads. They also allow developers to more rapidly build complex applications for the web browser by making use of things like MVC architecture, routing, and DOM/Javascript data-binding.

To serve an SPA from your web server to a user's browser, you must place the Javascript for that app in a publicly accessible directory on your webserver. But what happens if your SPA contains proprietary intellectual property that you want to keep private? Well, there are a few things you can do.

The first is obfuscation. You can easily obfuscate your Javascript code by using a tool like Uglify to mangle your Javascript and make it difficult to reverse engineer. I'll go over this in a later post.

The second tool that you can use to protect your SPA resources is requiring user authentication to access your Javascript, SPA templates, and stylesheets. This can be easily done with Laravel.

Laravel normally serves static resources out of an [app path]/public folder. The first thing we'll need to do is move these static resources out of the public folder and into another folder. It can be called [app path]/private or anything else you want.

private/
  css/
  js/
  views/
public/

Next, create a route group in routes.php that is protected by Laravel's auth filter. The protected route will serve our static resources.

Route::group([ 'before' => 'auth'], function() 
{
  Route::get('/private/{path}', [ 'as' => 'private-asset', 'uses' => 'AssetController@getPrivateAsset' ])
    ->where('path', '(.*)');
});

Implementing auth in Laravel is very straightforward and is outlined well in the Laravel security documentation. If you implement your own auth filter, just change the name in the 'before' => 'auth' directive.

To implement the getPrivateAsset() method inside our AssetController we first double check that the user is logged in, then look for the file that the user is requesting, find out its mime type, set some headers, and serve it out.

public function getPrivateAsset($path)
{
    // if the user is not logged in, return a 404 error
    if (!Auth::check()) {
        App::abort(404);
        return;
    }

    // build the file path
    $file= sprintf("%s/private/%s", base_path(), $path);

    // if the file requested does not exist, return a 404 error
    if (!file_exists($file)) {
      App::abort(404);
      return;
    }

    // use your favorite mime checker here
    // for illustrative purposes a simple mime checker follows:
    $type = pathinfo($file)['extension'];

    $mime = null;

    switch ($type) {
      case 'html' :
        $mime = 'text/html';
        break;
      case 'js' :
      case 'json' :
        $mime = 'text/javascript';
        break;
      case 'css' :
        $mime = 'text/css';
        break;
      case 'png' :
        $mime = 'image/png';
        break;
    }

    $headers = [];

    // set the Content-type header
    if ($mime) {
      $headers['Content-Type'] = $mime;  
    }

    // set any other cache headers you may need for your static resources
    // $headers['whatever'] = 'whatever'; 

    // return the file as output with 200 success code
    return Response::make(file_get_contents($file), 200, $headers);
}

Lastly, any references to our Javascript/templates/CSS must be changed to reflect the path of the protected route we created.

    <link rel="stylesheet" href="/private/css/main.css" />
    ...
    <script src="/private/js/main.js"></script>

Be sure that you change your template path in your SPA (whatever the specifics of that are for your framework) to the new protected route as well. /private/views/myview.html

Now, only logged in users will be able to access your SPA!

In a later post I'll show how to use the Gulp.js build system to automate the obfuscation and minification of your static assets.

 

comments powered by Disqus

About This Site

This site was designed by We Are How.

This site is powered by Sculpin static site generator and the source is available here.

Yotta = 10^24, or 1 000 000 000 000 000 000 000 000, the largest metric prefix.


Contact

Get in touch to find out how we can help you refine your vision and implement a dynamite product that will help your business grow. Our agile product development process is thoughtfully designed to give clients ongoing feedback and visibility from project inception to completion.