Friday, March 7, 2014

Koajs Tutorial - Authenticate with Persistent Session using Redis

This tutorial will demonstrate how to use session to set the redirection path after authentication and how to make persistent session with Redis.

Before you begin

Your environment should have nodejs (>0.11) and redis installed.

Remember to set your GitHub CLIENTID and Secret in auth.js.


1. Install modules

Back to TOC
We need to use additional node modules for this tutorial:

  1. Session (koa-sess, this is not the same as koa-session)
  2. RedisStore (koa-redis)
npm install --save koa-sess koa-redis

2. Using Session

Back to TOC

There are 2 session modules for Koa:
1. koa-sess, which is memory/storage based.
2. koa-session, which is cookie based.

For this tutorial purpose we will use koa-sess.

  • Load the koa-sess library in app.js
//This is app.js
const
  ....
  session = require('koa-sess'),
  ....
  • Initialize session. Put code below after request logger middleware.
app.keys=['koa-tutorial'];
app.use(session());

We will set the app.keys and then use the session middleware.

You can set any key you like.


3. Initialize passport with session

Back to TOC

Add app.use(passport.session()); after the line app.use(passport.initialize());

//This is app.js
...
app.use(passport.initialize());
app.use(passport.session());

4. Redirect for post-authenticated process

Back to TOC

  • Reconfigure publicRouter.get('/auth/github/callback') to use successReturnToOrRedirect instead of successRedirect.
publicRouter.get('/auth/github/callback',
  passport.authenticate('github', {successReturnToOrRedirect: '/', failureRedirect: '/'})
);

successReturnToOrRedirect allow us to use url in this.session.returnTo so it is more dynamic and enable to redirect back where we came from instead of just '/'.

  • Set this.session.returnTo in authed middleware.
//Middleware: authed
function *authed(next){
  if (this.req.isAuthenticated()){
    yield next;
  } else {
    //Set redirect path in session
    this.session.returnTo = this.session.returnTo || this.req.url;
    this.redirect('/auth/github');
  }
}

The code this.session.returnTo || this.req.url; means when returnTo is available, use it back, else set returnTo to request url. This is useful when you have a login form page in between your desired return path and the authentication process.

Might need to keep resetting this.req.url for every public path user going to visit before login.

  • Add one additional securedRouter path for testing purpose:
securedRouter.get('/app2', authed, function *(){
  this.body = 'Secured Zone: koa-tutorial APP2\n'
});

Path to /app2 just to prove we have entered secured zone after authentication passed.

  • Save all your work.

5. Test application I

Back to TOC

  • Start your server with node --harmony app.js.

  • In your browser, go to localhost:3000/app
    You will see this log:

2014-03-07T08:53:09.048Z - GET /app
2014-03-07T08:53:09.077Z - GET /auth/github
2014-03-07T08:53:10.229Z - GET /auth/github/callback?code=a1da082fc1ae504f7b2c
2014-03-07T08:53:12.604Z - GET /app
  • Here’s how each line was output:
Line 1 GET /app
You have request to open localhost:3000/app which is GET /app.
Line 2 GET /auth/github
You were not authenticated, therefore redirects to /auth/github. this.session.returnTo is set to /app
Line 3 GET /auth/github/callback?code=a1da082fc1ae504f7b2c
You will be showing a GitHub login page if you haven’t login there yet.
After login to GitHub, a callback to /auth/github/callback.
Line 4 GET /app
The OAuth login is successful, therefore redirects to this.session.returnTo which is /app.

Now try go to /app2, notice there’s no more redirect to /auth/github because you are already authenticated.


6. Add session expiry

Back to TOC

Let’s add an expiry of 30 seconds after login.

  • Change your session initialization as follow.
app.use(session({
  cookie: {maxAge: 1000 * 30},
}));

maxAge is integer number in millisecond.

  • Restart your server and go to localhost:3000/app. Refresh again and you are still authenticated.

Now wait for 30 seconds and refresh again. You will notice that the session expired and you are redirected to authentication again.

  • Set your maxAge to 5 minutes for the rest of tutorial.
app.use(session({
  cookie: {maxAge: 1000 * 60 * 5}
}));

7. RedisStore Persistent Session

Back to TOC
Suppose you have a clustered setting or you just want to provide a robust experience, you need to share or hold the session even if the server has shutdown, you need to store the session in a persistent storage.

In this section we will use Redis as the session storage.
Its pretty easy to setup.

  • Load redis library
//This is app.js
const
  ...
  redisStorage = require('koa-redis'),
  ...
  • Configure session to use redis
app.use(session({
  cookie: {maxAge: 1000 * 60 * 5},
  store : redisStore()
}));

That’s it. We are now able to run session using Redis.


8. Test application II (Persistent Session)

Back to TOC

  • Save and restart your server.

  • Go to localhost:3000/app and you will be authenticated.

  • Restart your server again.

  • Go to localhost:3000/app2 and you will notice that you did not go through the authentication process again.


9. Complete app.js

Back to TOC

"use strict"

const
  Router = require('koa-router'),
  passport = require('./auth'),
  session = require('koa-sess'),
  redisStore = require('koa-redis'),

  koa = require('koa'),
  app = koa();


//Middleware: request logger
function *reqlogger(next){
  console.log('%s - %s %s',new Date().toISOString(), this.req.method, this.req.url);
  yield next;
}
app.use(reqlogger);

//Initialize session
app.keys=['koa-tutorial'];
app.use(session({
  cookie: {maxAge: 1000 * 60 * 5},
  store : redisStore()
}));

//Initialize passport with session
app.use(passport.initialize());
app.use(passport.session());

app.use(Router(app));

app.get('/', function *(){
  console.log('Express-style example');
  this.body = "This is root page ('/')";
});



const publicRouter = new Router();

//Configure /auth/github & /auth/github/callback
publicRouter.get('/auth/github', passport.authenticate('github', {scope: ['user','repo']}));
publicRouter.get('/auth/github/callback',
  passport.authenticate('github', {successReturnToOrRedirect: '/', failureRedirect: '/'})
);


app.use(publicRouter.middleware());



//Secures routes
const securedRouter = new Router();

//Middleware: authed
function *authed(next){
  if (this.req.isAuthenticated()){
    yield next;
  } else {
    //Set redirect path in session
    this.session.returnTo = this.session.returnTo || this.req.url;
    this.redirect('/auth/github');
  }
}

securedRouter.get('/app', authed, function *(){
  this.body = 'Secured Zone: koa-tutorial\n' + JSON.stringify(this.req.user, null, '\t');
});

securedRouter.get('/app2', authed, function *(){
  this.body = 'Secured Zone: koa-tutorial APP2\n'
});

app.use(securedRouter.middleware());

app.use(function *(){
  this.body = 'Hello World';
});

app.listen(3000);

Download

Back to TOC
You can download this tutorial project here:
https://github.com/Zev23/koa-tutorial-authenticate-with-persistent-session-using-redis






Copyright © Zev23.com 2014 All Rights Reserved. No part of this website may be reproduced without Zev23.com’s express consent.

No comments: