From f0da28abb31656dc9b8fe082e323f6468321d73c Mon Sep 17 00:00:00 2001 From: Sebastian Gierlinger Date: Wed, 20 Aug 2014 11:28:55 +0200 Subject: [PATCH] Fix broken status code closes #3779 - added custom error classes for OAuth errors (bug was caused by oauth2orize error classes) - added basic tests for `/authentication/token` endpoints --- core/server/middleware/oauth.js | 15 +- .../routes/api/authentication_test.js | 134 ++++++++++++++++++ 2 files changed, 142 insertions(+), 7 deletions(-) create mode 100644 core/test/functional/routes/api/authentication_test.js diff --git a/core/server/middleware/oauth.js b/core/server/middleware/oauth.js index 31c002b11f..50f36061f3 100644 --- a/core/server/middleware/oauth.js +++ b/core/server/middleware/oauth.js @@ -1,6 +1,7 @@ var oauth2orize = require('oauth2orize'), models = require('../models'), utils = require('../utils'), + errors = require('../errors'), oauth; @@ -25,7 +26,7 @@ oauth = { .fetch() .then(function (client) { if (!client) { - return done(null, false); + return done(new errors.NoPermissionError('Invalid client.'), false); } // Validate the user return models.User.check({email: username, password: password}).then(function (user) { @@ -41,8 +42,8 @@ oauth = { }).then(function () { resetSpamCounter(username); return done(null, accessToken, refreshToken, {expires_in: utils.ONE_HOUR_S}); - }).catch(function () { - return done(null, false); + }).catch(function (error) { + return done(error, false); }); }).catch(function (error) { return done(error); @@ -59,7 +60,7 @@ oauth = { .fetch() .then(function (model) { if (!model) { - return done(null, false); + return done(new errors.NoPermissionError('Invalid refresh token.'), false); } else { var token = model.toJSON(), accessToken = utils.uid(256), @@ -76,11 +77,11 @@ oauth = { return models.Refreshtoken.edit({expires: refreshExpires}, {id: token.id}); }).then(function () { return done(null, accessToken, {expires_in: utils.ONE_HOUR_S}); - }).catch(function () { - return done(null, false); + }).catch(function (error) { + return done(error, false); }); } else { - done(null, false); + done(new errors.UnauthorizedError('Refresh token expired.'), false); } } }); diff --git a/core/test/functional/routes/api/authentication_test.js b/core/test/functional/routes/api/authentication_test.js new file mode 100644 index 0000000000..8710071d08 --- /dev/null +++ b/core/test/functional/routes/api/authentication_test.js @@ -0,0 +1,134 @@ +/*global describe, it, before, after */ +/*jshint expr:true*/ +var supertest = require('supertest'), + express = require('express'), + should = require('should'), + testUtils = require('../../../utils'), + user = testUtils.DataGenerator.forModel.users[0], + ghost = require('../../../../../core'), + httpServer, + request; + + +describe('Authentication API', function () { + var accesstoken = ''; + + before(function (done) { + var app = express(); + + // starting ghost automatically populates the db + // TODO: prevent db init, and manage bringing up the DB with fixtures ourselves + ghost({app: app}).then(function (_httpServer) { + httpServer = _httpServer; + request = supertest.agent(app); + }).then(function () { + return testUtils.doAuth(request); + }).then(function (token) { + accesstoken = token; + done(); + }).catch(function (e) { + console.log('Ghost Error: ', e); + console.log(e.stack); + }); + }); + + after(function (done) { + testUtils.clearData().then(function () { + httpServer.close(); + done(); + }); + }); + + it('can authenticate', function (done) { + request.post(testUtils.API.getApiQuery('authentication/token')) + .send({ grant_type: 'password', username: user.email, password: user.password, client_id: 'ghost-admin'}) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + should.not.exist(res.headers['x-cache-invalidate']); + var jsonResponse = res.body; + should.exist(jsonResponse.access_token); + should.exist(jsonResponse.refresh_token); + should.exist(jsonResponse.expires_in); + should.exist(jsonResponse.token_type); + done(); + }); + }); + + it('can\'t authenticate unknown user', function (done) { + request.post(testUtils.API.getApiQuery('authentication/token')) + .send({ grant_type: 'password', username: 'invalid@email.com', password: user.password, client_id: 'ghost-admin'}) + .expect('Content-Type', /json/) + .expect(404) + .end(function (err, res) { + if (err) { + return done(err); + } + var jsonResponse = res.body; + should.exist(jsonResponse.errors[0].type); + jsonResponse.errors[0].type.should.eql('NotFoundError'); + done(); + }); + }); + + it('can\'t authenticate invalid password user', function (done) { + request.post(testUtils.API.getApiQuery('authentication/token')) + .send({ grant_type: 'password', username: user.email, password: 'invalid', client_id: 'ghost-admin'}) + .expect('Content-Type', /json/) + .expect(401) + .end(function (err, res) { + if (err) { + return done(err); + } + var jsonResponse = res.body; + should.exist(jsonResponse.errors[0].type); + jsonResponse.errors[0].type.should.eql('UnauthorizedError'); + done(); + }); + }); + + it('can request new access token', function (done) { + request.post(testUtils.API.getApiQuery('authentication/token')) + .send({ grant_type: 'password', username: user.email, password: user.password, client_id: 'ghost-admin'}) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + var refreshToken = res.body.refresh_token; + request.post(testUtils.API.getApiQuery('authentication/token')) + .send({ grant_type: 'refresh_token', refresh_token: refreshToken, client_id: 'ghost-admin'}) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + var jsonResponse = res.body; + should.exist(jsonResponse.access_token); + should.exist(jsonResponse.expires_in); + done(); + }); + }); + }); + + it('can\'t request new access token with invalid refresh token', function (done) { + request.post(testUtils.API.getApiQuery('authentication/token')) + .send({ grant_type: 'refresh_token', refresh_token: 'invalid', client_id: 'ghost-admin'}) + .expect('Content-Type', /json/) + .expect(403) + .end(function (err, res) { + if (err) { + return done(err); + } + var jsonResponse = res.body; + should.exist(jsonResponse.errors[0].type); + jsonResponse.errors[0].type.should.eql('NoPermissionError'); + done(); + }); + }); +});