Derick Bailey
Derick Bailey

Reputation: 72868

Patterns for JavaScript security with back-end authorization?

I'm looking for some good resources, patterns and practices on how to handle basic security needs, such as authorization, in client side JavaScript.

I'm building a website with a back-end system running one of the common MVC frameworks. The back-end will handle all of the real security needs: authorization and authentication. The front is going to be built with Backbone.js, jQuery and a few other libraries to facilitate a very rich user experience.

Here's an example of one scenario I need to handle:

I have a grid of data with a few buttons on top of it. If you select an item in the grid, certain buttons become enabled so you can take that action on the selected item. This functionality is easy to build...

Now I need to consider authorization. The back-end server will only render the buttons that the user is allowed to work with. Additionally, the back-end server will check authorization when the user tries to take that action. ... so the back end is covered and the user won't be able to do what they try, if they are not authorized.

But what about the JavaScript? If my code is set up with a bunch of jQuery click handlers or other events that enable and disable buttons, how do I handle the buttons not being there? Do I just write a bunch of ugly if statements checking for the existence of the button? Or do I write the JavaScript in a way that lets me only send the JavaScript for the buttons that exist, down to the browser, based on authorization? or ???

Now imagine a tree view that may or may not allow drag & drop functionality, based on authorization... and an add/edit form that may or may not exist based on authorization... and all those other complicated authorization needs, with a lot of JavaScript to run those pieces of the front end.

I'm looking for resources, patterns and practices to handle these kinds of scenarios, where the back end handles the real authorization but the front end also needs to account for things not being there based on the authorization.

Upvotes: 26

Views: 5941

Answers (7)

Tapio Kulmala
Tapio Kulmala

Reputation: 1

On client side, let JQuery take care of it. If the element is not there, it does not do anything. It only attaches event handlers to those elements that it finds, you will not need any if-an-element-exist check, just use the JQuery selectors.

Upvotes: 0

poezn
poezn

Reputation: 4139

There are three fairly straightforward things I could see:

  • Modular Backbone Views I grew very fond of nested and highly modular Backbone views. That is, every row in your tree could be a Backbone view and reacting to their own authorization requirements.

  • Multiple Event Hashes Set up multiple event hashes on the view that you switch out with delegateEvents() based on your authorization requirements and triggered by an event. That way you get around a set of ugly if statements.

  • Multiple Templates In a similar vein, you specify multiple templates to render based on the authorization requirements.

All three would require an event structure that's setup (for example using your own vent PubSub handler) where you trigger authorization checks based on the response of a RESTful server request, or based on some client-side function.

Upvotes: 7

Johnny Oshika
Johnny Oshika

Reputation: 57602

I would recommend using the Factory pattern together with Backbone's class properties to initialize different views depending on the authorization of the user. In the example below, I define a base GridView which contains all of the default and common behaviors. AdminGridView and EditorGridView contains the authorized user's specific functionality. In the simplified example below, a click handler will only be wired up for admins.

The nice thing is that everything is encapsulated so that only the Factory needs to know about AdminGridView and EditorGridView. Your code would just interact with GridView.

// GridView is an abstract class and should not be invoked directly 
var GridView = Backbone.View.extend({ 

    // put all common / default code here

    _template: _.template($('#grid').html()),

    initialize: function(){
        this.model.bind('change', this.render, this);
    }.

    onButtonClick: function(){
        // do something
    },

    render: function(){ 
        $(this.el).html(this._template(this.model));
    } 
}, { 

    create: function (options) { 
        switch (options.authorization.get('type')) { 
            case 'admin': 
                return new AdminGridView(options); 
            case 'editor': 
                return new EditorGridView(options); 
            default:
                throw new Error('Authorization type ' + options.authorization.get('type') + ' not supported.'); 
        } 
    } 
}); 

var AdminGridView = GridView.extend({
    // put admin specific code here
    events: {
        'click .button': 'onButtonClick'
    }
}); 

var EditorGridView = GridView.extend({
    // put editor specific code here
});

var authorization = new Backbone.Model({ type: 'admin' });
var gridView = GridView.create({ model: someModel, authorization: authorization });
$('body').append((gridView.render().el))

Upvotes: 2

sam
sam

Reputation: 763

Anything on the client side can be hacked. So what we're doing in our singlepage js app is entitlements on the server. We combine a list of entitlements per user and have an OAuth token that we pass to the client and is sent back with each request. then in the server side before an action is taken we see if the user is authenticated for this action.

Of course, this doesn't protect you from someone at a cafe with firesheep...but thats another problem.

Upvotes: 2

caleb briggs
caleb briggs

Reputation: 11

The way we handle this is letting the server send us the html that needs to be rendered based on the the decision or decisions that result in a consequence. something like this

this.ActionFor(Model)
                .Decision<Authentication1>()
                .Decision<Authentication2>()
                .Consequence<HTMLRenderer>()

You could probably use this pattern similarly in JS if you think you needed to.

Upvotes: 1

Esailija
Esailija

Reputation: 140244

Since you mentioned backbone, your code should not be

a bunch of jQuery click handlers

but instead you can use models to store data about authorization and have your views react accordingly.

Upvotes: 1

mythz
mythz

Reputation: 143409

The way I handle authentication on the client is to have a singleton Backbone model that holds an isAuthenticated value that I initially populate from the server with:

@{
  App.user.set({ isAuthenticated: @UserSession.IsAuthenticated.ToString().ToLower() });
}

Then all javascript controls/functionality that change behavior based on Authentication basically just listen to changes on this field and re-renders themselves into the correct state. All this view/logic is done with JavaScript so it works at page generation time (by the server) or on the client with Javascript/ajax.

I don't maintain event handlers to existing/hidden functionality, I re-create all the UI elements and re-hook up all the event handlers after the views are re-rendered based on the isAuthenticated flag.

The nice thing is that once you login with ajax on the client (i.e. after the server page has been rendered) you just need to set the same field and as if by magic (Backbone FTW :) it all works and renders correctly.

Upvotes: 4

Related Questions