Implementing a simple role based authorization is an easy task and I bet, that you can find tons of examples and libraries on the web. As your application grows, you might need a little bit more than role based authorization. Imagine for instance, that you are building a 30 days free trial solution, and that you would like to restrict user to use certain part of your application, when her trial subscription expires. That’s when Kindergarten comes to play!

What?

The README of Kindergarten states:

Kindergarten is an implementation of the sandbox pattern in JavaScript with some extra goodies. Kindergarten helps you to separate your business logic into modules and add some security layer over them.

If you have used YUI Library, then you are probably familiar with the sandbox pattern. The Sandbox pattern relies heavily on modules. You start with an empty sandbox and you load some modules into your sandbox and then they will be available in the sandbox completely isolated from the rest of the application.

// Very high level pseudo-code

sandbox.load(myModule01, myModule02); sandbox.myModule01.someMethod();

sandbox.myModule02.someOtherMethod();

Kindergarten uses this pattern, but additionally it allows you to put a governess on your sandbox to prevent any kind of troubles. Governess is strictly following all rules that are defined in your modules. If child performs any unauthorized activity, then governess will encroach. By default she just throws an error. Luckily there is a way to teach governess to do some other stuff, depending on the context.

The modules in kindergarten are called perimeters, because they usually represent certain part of your application like: admin page, user table, button, footer etc.. As I mentioned earlier they contain rules for the governess and they can optionally contain some methods that will be exposed into your sandbox.

The sandbox is where the magic happens. Sandbox doesn’t have to be a separate object, but your classes can inherit from sandbox, or you can use sandbox decorator to decorate your objects. The sandbox expects to have access to current logged-in user (if any). The current user in kindergarten is called surprisingly the child.

How Does It Work with React?

Now you have a brief idea how kindergarten works, but how does that apply to React? Let me show you how you can protect some routes in react-router. Let’s say we have an admin path and we allow only admin users to got there. The regular users should be redirected to login page.

We start by defining our perimeter. We would like to protect admin route, so let’s call it adminPerimeter . I usually create a new folder called perimeters and put all perimeters there.

import { createPerimeter } from 'kindergarten'; const adminPerimeter = createPerimeter({

purpose: 'admin', govern: {

'can route': function (route){

return route === 'admin' ?

routePermissions() :

true

}

}, routePermissions() {

return this.child && this.child.isAdmin;

}

}); export default adminPerimeter;

Now let’s create our Router:

import React, { Component } from 'react';

import { Router, Route, IndexRoute } from 'react-router';

import { render } from 'react-dom';

import { sandbox } from 'kindergarten'; import App from './containers/App';

import adminPerimeter from './perimeters/adminPerimeter'; import {

AboutPage,

AdminPage,

ContactPage,

HomePage,

LoginPage

} from './pages';

perimeters: [

adminPerimeter

]

})

class BaseRouter extends Component {

guardRoute(route) {

return (nextState, replace, callback) =>

this.guard('route', route, nextState, replace, callback);

}



render() {

return (

<Router history={history}>

<Route path="/" component={App}>

<IndexRoute component={HomePage} />

<Route path="about" component={AboutPage} />

<Route path="contact" component={ContactPage} />

<Route path="admin" component={AdminPage} onEnter={this.guardRoute('admin')} />

<Route path="login" component={LoginPage} />

</Route>

</Router>

);

}

} @sandbox ({ isAdmin: false }, {perimeters: [adminPerimeter})class BaseRouter extends Component {guardRoute(route) {return (nextState, replace, callback) =>this.guard('route', route, nextState, replace, callback);render() {return ( ); render((

<BaseRouter />

), document.getElementById('app'));

Notice that we are using sandbox decorator here. If you don’t want to enable decorators yet (since it is not yet standardized), then you can just call the sandbox method like this:

sandbox({ isAdmin: false }, {

perimeters: [

adminPerimeter

]

})(class BaseRouter extends Component {

// ...

});

The first argument passed to sandbox method is the child (the current user if you like). The second argument is the configuration object for your sandbox. We have specified that on this sandbox we would like to have one perimeter adminPerimeter in our sandbox.

When user enters the admin route, the onEnter callback is called with nextState , replace , callback arguments (that comes from react-router). We call then the guard method which comes from kindergarten — more specifically from the HeadGoverness which is available by default on any sandbox . The first argument of the guard method is the action that we would like to protect. In our case it’s the route action, which we have also defined in our adminPerimeter , see. the govern object inside of perimeter definition.

If you try to run the application and you navigate to /admin , then AccessDenied error will be thrown. That’s not what we want right? We would like to redirect user to the /login page. We could add that functionality to the guardRoute method, but there is also a better way! Let’s create our own RoutingGoverness . I also usually keep my governesses in one folder.

import {

HeadGoverness,

AccessDenied

} from 'kindergarten'; export default class RoutingGoverness extends HeadGoverness {

guard(action, route, nextState, replace, callback) {

try {

super.guard(action, route);

} catch (e) {

if (e instanceof AccessDenied) {

replace('login');

callback(e.message);

} else {

callback(e.message);

}

} callback();

}

}

That’s it! If guard method throws an AccessDenied error, then we will redirect to /login page. Now we have to put this governess on our sandbox and replace the HeadGoverness . It’s simple enough:

import RoutingGoverness from './governesses/RoutingGoverness';

perimeters: [

adminPerimeter

],

governess: RoutingGoverness

}) @sandbox ({ isAdmin: false }, {perimeters: [adminPerimeter],governess: RoutingGoverness})

Conclusion

In this example I have shown you how to protect your routes using kindergarten. Similarly you can protect any React component you like and even more — everything can act as sandbox! Just use your fantasy.

Kindergarten is not meant for small applications, but it may really help you to make a complex authorization logic more readable and DRY.