diff --git a/test/api/unit/middlewares/auth.test.js b/test/api/unit/middlewares/auth.test.js index f4a9324189..b75e339d63 100644 --- a/test/api/unit/middlewares/auth.test.js +++ b/test/api/unit/middlewares/auth.test.js @@ -60,5 +60,20 @@ describe('auth middleware', () => { return done(); }); }); + + it('errors with InvalidCredentialsError and code when token is wrong', done => { + const authWithHeaders = authWithHeadersFactory({ userFieldsToExclude: [] }); + + req.headers['x-api-user'] = user._id; + req.headers['x-api-key'] = 'totally-wrong-token'; + + authWithHeaders(req, res, err => { + expect(err).to.exist; + expect(err.name).to.equal('InvalidCredentialsError'); + expect(err.code).to.equal('invalid_credentials'); + expect(err.message).to.equal(res.t('invalidCredentials')); + return done(); + }); + }); }); }); diff --git a/website/client/src/app.vue b/website/client/src/app.vue index 839b297e0a..5b72ea8782 100644 --- a/website/client/src/app.vue +++ b/website/client/src/app.vue @@ -223,11 +223,10 @@ export default { const errorData = error.response.data; const errorMessage = errorData.message || errorData; + const errorCode = errorData.error; - // Check for conditions to reset the user auth - // TODO use a specific error like NotificationNotFound instead of checking for the string - const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.']; - if (invalidUserMessage.indexOf(errorMessage) !== -1) { + // If 'invalid_credentials' signaled, force logout + if (error.response.status === 401 && errorCode === 'invalid_credentials') { this.$store.dispatch('auth:logout', { redirectToLogin: true }); return null; } diff --git a/website/server/libs/errors.js b/website/server/libs/errors.js index 2b570dbb7e..0e3a2f0486 100644 --- a/website/server/libs/errors.js +++ b/website/server/libs/errors.js @@ -117,3 +117,27 @@ export class InternalServerError extends CustomError { this.message = customMessage || 'An unexpected error occurred.'; } } + +/** + * @apiDefine InvalidCredentials + * @apiError InvalidCredentials The user’s credentials are no longer valid. + * + * @apiNote + * The 'invalid_credentials' error code is language-agnostic: + * clients should use this code (regardless of locale or translated message) + * to unambiguously trigger a user logout. + * + * @apiErrorExample Error-Response: + * HTTP/1.1 401 Unauthorized + * { + * "error": "invalid_credentials", + * "message": "There is no account that uses those credentials." + * } + */ +export class InvalidCredentialsError extends NotAuthorized { + constructor (message) { + super(message); + this.name = this.constructor.name; + this.code = 'invalid_credentials'; + } +} diff --git a/website/server/middlewares/auth.js b/website/server/middlewares/auth.js index f5d5614847..ff1db6fd5b 100644 --- a/website/server/middlewares/auth.js +++ b/website/server/middlewares/auth.js @@ -2,6 +2,7 @@ import moment from 'moment'; import nconf from 'nconf'; import url from 'url'; import { + InvalidCredentialsError, NotAuthorized, } from '../libs/errors'; import { @@ -81,7 +82,7 @@ export function authWithHeaders (options = {}) { .exec() .then(user => { if (!user || apiToken !== user.apiToken) { - throw new NotAuthorized(res.t('invalidCredentials')); + throw new InvalidCredentialsError(res.t('invalidCredentials')); } if (user.auth.blocked) { diff --git a/website/server/middlewares/errorHandler.js b/website/server/middlewares/errorHandler.js index bd317a2b4a..c3cdf11fab 100644 --- a/website/server/middlewares/errorHandler.js +++ b/website/server/middlewares/errorHandler.js @@ -82,7 +82,7 @@ export default function errorHandler (err, req, res, next) { // eslint-disable-l const jsonRes = { success: false, - error: responseErr.name, + error: responseErr.code || responseErr.name, message: responseErr.message, };