koa 2 not handling unsuccessful login
dagda1 opened this issue · 16 comments
I am confused as to what is the correct way to use koa-passport with koa 2.
So far I have my strategy defined like this:
import Strategy from 'passport-local';
passport.use('local', new Strategy({
usernameField: 'username',
passwordField: 'password'
}, async (username, password, done) => {
try {
const user = await User.findOne({
username
});
if (!user) {return done(null, false)}
try {
const isMatch = await user.validatePassword(password);
if (!isMatch) {
return done(null, false)
}
done(null, user)
} catch (err) {
done(err)
}
} catch (err) {
return done(err)
}
}))
And I have my auth route defined like this:
const router = new Router();
router.post('/', function(ctx, next) {
return passport.authenticate('local', function(err, user, info, status) {
console.dir(arguments.length);
if(user === false) {
console.log('it gets here but then........');
return ctx.throw(401);
}
const token = jwt.sign({ id: user.id }, config.token);
const response = user.toJSON()
delete response.password;
delete response.salt;
ctx.body = {token,user: response};
ctx.status = 201;
})(ctx, next)
})
export default router;
This appears to work fine for the successful login but appears to hang and then comes back with this response
Empty reply from server
Where exactly do you get the error Empty reply from server
?
With the following curl request:
curl -H "Content-Type: application/json" -X POST -d '{"username":"user","password":"fffff"}' http://localhost:5000/auth/
The code seems to hang at the return ctx.throw(401);
line.
Is there an example somewhere of how to properly return a 401?
Could you append -i
to this curl command and put the output here?
If I make this request:
curl -i -H "Content-Type: application/json" -X POST -d '{"username":"dagda1","password":"5a5parellazzzz"}' http://localhost:5000/auth/
After a wait of at least a minute, I get the following response:
curl: (52) Empty reply from server
I am not yet able to reproduce this issue. So we have to isolate it a little bit.
Could you replace your whole return passport.authenticate(...)
function by just return ctx.throw(401);
. Do you get a proper 401 response in this case?
yes I do
It does appear to be the combination of the ctx.throw
in the passport block. If I log in successfully, I get this response:
HTTP/1.1 201 Created
Vary: Accept-Encoding
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Type: application/json; charset=utf-8
Content-Length: 263
Date: Thu, 27 Oct 2016 21:16:25 GMT
Connection: keep-alive
So return ctx.throw(401);
outside of passport.authenticate
does also work?
Yes, outside of the block returns this response:
HTTP/1.1 401 Unauthorized
Vary: Accept-Encoding
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Type: text/plain; charset=utf-8
Content-Length: 12
Date: Thu, 27 Oct 2016 21:20:13 GMT
Connection: keep-alive
I have found the problem, I had not marked the function as async.
If I change the code to this, it works fine:
router.post('/', function(ctx, next) {
return passport.authenticate('local', async function(err, user, info, status) {
if(user === false) {
return ctx.throw(401);
}
Apologies for wasting your time.
Don't worry, such issues always provide a possibility to learn. For example now, I do not really understand why adding the async
there solves this issue. I also found out something else. The try/catch
in the LocalStrategy is related to the issue. Because the ctx.throw
throws an exception, which will be catched by this try/catch
, which just calls done
again.
there must be some await call or generator call somewhere that was just being suspended without the async marker.
I think I have an idea why this happens. I you provide an async function to passport.authenticate
, the error is catched by koa-passport
and handled accordingly. Otherwise, the error is handled by your try/catch. So your solution for the issue is perfectly fine. However, I have to think about how to improve the usage of strategies with async/await
. Having to use a try/catch does not seem like a nice solution ...
With version 3.0.0-rc.2
you should now be able to remove the try/catch
inside of your new LocalStrategy
If the try catch is removed then where is the Promise rejection handled?