Skip to content

Overriding the default authentication scheme

mrmarbles edited this page May 16, 2011 · 2 revisions

###Overriding the Default Authentication Scheme

Likely the first thing you're going to want to do after installing the komainu security microframework is configure it to perform authentication specific to your application. Using test credentials will only get you so far. Authentication with Komainu comes in two forms;

  • User / Login Authentication
  • Resource Authentication

We'll cover both in this documentation.

Before we get into the details of how to customize this aspect, it's important to understand the default event chain in regards to the login process. The following events will be fired consecutively when the framework detects a loginRequest;

  • loginRequest
  • login

If login was successful;

  • loginSuccess
  • initSession
  • sessionStarted

If login was unsuccessful;

  • loginFailure
  • loginShow

In order to implement your own means to authentication a login request is to override the default login event listener. As specified in the API documentation this listener expects the following arguments (in order);

  • req: Object representing the current request.
  • res: Object representing future response.
  • username: Should be self explanatory.
  • password:

So in order to properly override the default implementation, you would simply need to add your own event listener to the SecurityProvider;

var express = require('express'),
  komainu = require('komainu');

var sp = komainu.createSecurityProvider();

// here's where you implement your own custom login authentication
sp.on('login', function(req, res, username, password) {

  /*
   * You can now evaluate both the provided username and password
   * against whatever domain user store you need in order to authenticate
   * the login request.
   */

   if ({login was successful}) {
     // make sure to emit the next two events to complete the auth chain
     sp.emit('loginSuccess', req, res, username);
     sp.emit('initSession', req, res, username, password, keys);
   } else {
     sp.emit('loginFailure', req, res, username);
   }

});

The general contract to which you'll need to adhere when providing your own login event listener is that within the callback provided to the listener you must emit the following events under the following conditions;

A successful login attempt must emit;

  • loginSuccess
  • initSession

A login failure must emit;

  • loginFailure

The keys parameter that the default event listener for initSession can be any object or collection of objects that suits your domain security needs. The default initSession key is nothing more than a simple string with a value of LOGGED_IN_USER but don't think that this restricts your domain security model, it doesn't. Whether you require Roles, Permissions or some other more specific data element, you can represent those in keys, simply pass it along in the initSession emission. The default event listener for initSession will take that value and set it in a user-specific session like so;

req.session.security = {
  keys: keys
}

So as long as that session remains valid, you can access the keys in req.session.security. As a side note, the session itself is initialized and maintained via the express.session() or connect.session() middleware instances which must precede the sp.secure() one.

Now that you've got custom keys in the session, you must lastly ensure resource authentication takes them into account when authentication a request.

This is where the sp.secure() method comes into play. The secure() method of the SecurityProvider will return a connect-friendly closure that will consult with the session structure to determine whether or not access should be given to any requested resource. This closure maintains a reference to a default authenticator. An authenticator is a function whose method signature is function(req, res) - it's as simple as that. It is the responsibility of the authenticator to return a boolean value indicating whether or not access should be given, taking into consideration req and/or res information. Seeing as how session information may be available in a req that the authenticator processes, it is easy to evaluate session keys to derive an access result. Providing your own authenticator to do just that is as simple as passing it into the secure() method when it is invoked;

var express = require('express'),
  komainu = require('komainu');

var app = module.exports = express.createServer();
var sp = komainu.createSecurityProvider();

app.configure(function() {
  app.use(express.cookieParser());
  app.use(express.session({secret:'mysecretkey'});
  app.use(sp.secure(function(req, res) {
    /*
     * Provide your custom authenticator here.
     * Make sure to return a boolean `true` or `false`
     * and consult `req.session` to check for the existence
     * of and values for `req.session.security.keys`.
     */
  }));
});

Route-Specific Security

The above authenticator will be globally applied to all requests (as long as one is not being ignored) because it was provided in app.use(). However you will most likely need to apply different "rules" to different routes. Remember that the secure() method of the SecurityProvider instance will return a closure which maintains a reference to the supplied authenticator, so this can easily be accomplished by provided a route-specific authenticator implementation as middleware for individual routes;

app.get('/', sp.secure(function(req, res) {
  /*
   * The authentication logic provided in this route-specific
   * `authenticator` is, you guessed it, only applicable
   * to requests that will be mapped to the below route.
   * 
   * Don't forget to consult with `req.session` to determine
   * whether or not the active session should have access and 
   * return a true or false.
   */
}), function(req, res) {
  res.render('index', {
    title: 'Express'
  });
});
Clone this wiki locally