4
votes

I'm using koa and passport trying to implement middleware to prevent access to URIs when not authenticated.

var koa = require('koa');
var session = require('koa-generic-session');
var bodyParser = require('koa-bodyparser');
var koaRouter = require('koa-router');
var passport = require('koa-passport');
var views = require('co-views');
var render = views('.', { map: { html: 'swig' }});
var localStrategy = require('passport-local').Strategy;

var app = koa();
var router = koaRouter();

app.keys = ['secret'];
app.use(session());
app.use(bodyParser());
app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser(function(user, done) {
    done(null, user);
});

passport.deserializeUser(function(user, done) {
    done(null, user);
});

passport.use(new localStrategy(function(username, password, done) {
    if (username === 'user1' && password === 'password2') {
        done(null, { userId: 99, userName: 'redBallons' });
    } else {
        done(null, false);
    }
}));

router.get('/login', function *(next) {
    this.body = yield render('index.html');
});

router.post('/login', passport.authenticate('local', {
    successRedirect: '/secretBankAccount',
    failureRedirect: '/login'
}));

router.get('*', function *(next) {
    if (! this.isAuthenticated()) {
        console.log('not authenticated');
        this.redirect('/login');
    } else {
        console.log('authenticated');
        yield next;
    }
});

router.get('/secretBankAccount', function *(next) {
    this.body = '2 dollars';
});

app.use(router.routes());
app.listen(8080);

however, I can never get to my secretBankAccount. I can enter the correct user and password and can see the authenicated message, but the yield next in router.get('*') does not pass me through to the next routing function

2

2 Answers

5
votes

When using koa-router it is expected that only one route is hit. So when you hit the '*' route it won't hit another route even if you yield next.

So you should replace the universal route with your own authentication middleware:

app.use(function*(next) {
  if (this.isAuthenticated()) {
    yield next
  } else {
    this.redirect('/login')
  }
});

The authentication middleware will force you to do your routing with two routing objects instead of one. This is so you can distinguish between public and secured routes. So something like:

var public = new koaRouter();

public.get('/login', function *(next) {
    this.body = yield render('index.html');
});

public.post('/login', passport.authenticate('local', {
    successRedirect: '/secretBankAccount',
    failureRedirect: '/login'
}));

app.use(public.routes());

app.use(function*(next) {
  if (this.isAuthenticated()) {
    yield next;
  } else {
    this.redirect('/login');
  }
})

var secured = new koaRouter();

secured.get('/secretBankAccount', function *(next) {
  this.body = '2 dollars';
});

app.use(secured.routes());

In the above example a request will first hit the public routing middleware. Then if it doesn't match the current request with a public route it will move onto the authentication middleware. If isAuthenticated() is false a redirect will occur. If isAuthenticated() is true it'll move onto the secured routing.

This approach is based on the koa-passport-example project which was created by the author of koa-passport.

0
votes

Answer from peadar-doyle is the way to do this but needs updating to avoid the warning: koa deprecated Support for generators will be removed in v3.

Here's the updated version. I'm sending 401 instead of redirecting:

// all requests must now be authenticated
app.use(async (ctx, next) => {
    if (ctx.isAuthenticated()) {
        await next();
    } else {
        ctx.body = "access denied";
        ctx.status = 401;
    }
})