rkusa/koa-passport

Passport.serialize is getting the user, but ctx.isAuthenticated() always returns false

jenkynolasco11 opened this issue · 8 comments

Here is my code:

// passport-init.js
import passport from 'koa-passport'
import { Strategy } from 'passport-local'

import {
  User,
  isValidPass,
  createHash
} from './UserSchema'

passport.serializeUser( (user, done) => {
  console.log(user)  // it returns the user
  done(null, user._id)
})

passport.deserializeUser( (id, done) => User.findById(id, done) )

// local login
passport.use(new Strategy(async (username, password, done) => {
  //
  let user = null
  try{
    user = await User.findOne({ username })
    if(!user || await !isValidPass(user.password, password)) user = null

    //
  } catch(e) {
    //
    console.log('Something happened while logging in: ', e)
  }

  // I always get the user
  return done(null,
    user
    ? user
    : false)
}))

// Signup
passport.use('signup', new Strategy({
  passReqToCallback : true,
  //
}, async (req, username, pass, done) => {
  let user = null
  let password = null
  let { email } = req.body

  try {
    user = await User.findOne({ username })
    email = await User.findOne({ email })
    password = await createHash(pass)

    // if error, then it means username/email already exists
    if(user || email) return done(null, false)

    user = await new User({
      username,
      email,
      password
    }).save()

    //
  } catch (e) {
    //
    console.log('Something happened while signing up: ', e)
  }

  // I always get the user
  return done(null,
    user
    ? user
    : false)
}))

And this is my authentication router

// auth.js
// ...
const routes = () => {
  const route = new Router()

  route.prefix('/auth')

  route.get('/login', ctx => {
    ctx.state = { title : 'Login', login : true, csrf: ctx.csrf }
    ctx.render('login')
  })
  route.get('/signup', ctx => {
    ctx.state = { title : 'Signup', signup : true, csrf: ctx.csrf }
    ctx.render('login')
  })

  // TODO: fix this later
  route.post('/login', passport.authenticate('local', {
    successRedirect: '/',
    failureRedirect: '/auth/login',
  }))

  // TODO: fix this later
  route.post('/signup', passport.authenticate('signup', {
    successRedirect: '/',
    failureRedirect: '/auth/signup',
  }))

  route.get('/logout', async ctx => {
    await ctx.logout()
    ctx.redirect('/')
  })

  return route
}
// index.js
const isAuthenticated = (ctx, next) => {
  console.log(ctx.isAuthenticated()) // always returns false
  console.log(ctx.state)  // gives me the state, but never gives me the user back
  if(ctx.isAuthenticated())
    return next() // this never gets called
  return ctx.redirect('/auth/login') // it always redirects me to login page
}

indexRoute.get('/', isAuthenticated, async (ctx, next) => {
  ctx.status = 200
  ctx.state = { title : 'Index', user : ctx.state.user }
  await ctx.render('index')
})

After transversing through the /auth/login route and entering the user and password properly, passport bounces me out, saying that I'm not authenticated, returning false when I call ctx.isAuthenticated().

Anything I might be missing?

rkusa commented

Wasn't able to spot an error, yet, so I can only guess.

Does User.findById(id, done) in passport.deserializeUser( (id, done) => User.findById(id, done) ) really calls the callback with a user?

@rkusa the done callback was being executed with a user, but after thoroughly searching inside the module, it seemed that passport heavily depends on a session module, which I didn't have working properly at the time. After fixing the session module, this started to work "magically". I can't recall having this experience using express since the session module wasn't dependent on the express version, like in Koa. I had to check various modules to look which one would work properly, even tho the koa-session2 was supposed to work with Koa version 2, but it didn't.

Well, I'm closing this issue since I solved my problem. Thanks for replying 💯

@jenkynolasco11 Which session module did you end up using? I'm running into a similar situation using koa-session.

The same problem. Please tell me how did u solved the problem and what did u fix into the session module exactly.

I just used koa-session and it seems to work for me. Here's my packages and I'm on node 8.1.3

"koa": "^2.3.0",
"koa-bodyparser": "^4.2.0",
"koa-passport": "^3.0.0",
"koa-router": "^7.2.1",
"koa-session": "^5.4.0",

@mrchief I am using the same packages but I am not sure if I am configuring koa-session correctly. Can you provide me with your configuration for koa-session or you are using the default configuration?

@tpopov94 Just the default setup:

app.use(session(
  {
    key: 'myapp',
    maxAge: 'session' 
  },
  app
))

@cpurtlebaugh & @TotallWAR :
I had a similar issue where isAuthenticated() never returned true and after a short debugging reason was found from session configuration. I was missing keys parameter for session encryption. So to configure session correctly I did following :

const CONFIG = {
key:'koa:sess',
maxAge: 10000
};
// sessions
app.keys = ['super-secret-key']; // <--- I was missing this one.
app.use(session(CONFIG, app));

There was a pretty clear debug message saying about the case.