mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
add InvalidCredentialsError with language-agnostic code (#15472)
* add InvalidCredentialsError with language-agnostic code and update backend & web logout logic * error.code in API error responses Updated the error handler to serialize responseErr.code as the JSON error field, falling back to responseErr.name when no code is set. * fix(lint): whitespace and missing def * fix(lint): missed one * add InvalidCredentialsError case for bad token Add test verifying that auth middleware throws InvalidCredentialsError with code "invalid_credentials" and correct translated message when the API token is invalid. * fix(test): user fields implicitly required --------- Co-authored-by: Kalista Payne <sabrecat@gmail.com>
This commit is contained in:
@@ -60,5 +60,20 @@ describe('auth middleware', () => {
|
|||||||
return done();
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -223,11 +223,10 @@ export default {
|
|||||||
|
|
||||||
const errorData = error.response.data;
|
const errorData = error.response.data;
|
||||||
const errorMessage = errorData.message || errorData;
|
const errorMessage = errorData.message || errorData;
|
||||||
|
const errorCode = errorData.error;
|
||||||
|
|
||||||
// Check for conditions to reset the user auth
|
// If 'invalid_credentials' signaled, force logout
|
||||||
// TODO use a specific error like NotificationNotFound instead of checking for the string
|
if (error.response.status === 401 && errorCode === 'invalid_credentials') {
|
||||||
const invalidUserMessage = [this.$t('invalidCredentials'), 'Missing authentication headers.'];
|
|
||||||
if (invalidUserMessage.indexOf(errorMessage) !== -1) {
|
|
||||||
this.$store.dispatch('auth:logout', { redirectToLogin: true });
|
this.$store.dispatch('auth:logout', { redirectToLogin: true });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,3 +117,27 @@ export class InternalServerError extends CustomError {
|
|||||||
this.message = customMessage || 'An unexpected error occurred.';
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import moment from 'moment';
|
|||||||
import nconf from 'nconf';
|
import nconf from 'nconf';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import {
|
import {
|
||||||
|
InvalidCredentialsError,
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
import {
|
import {
|
||||||
@@ -81,7 +82,7 @@ export function authWithHeaders (options = {}) {
|
|||||||
.exec()
|
.exec()
|
||||||
.then(user => {
|
.then(user => {
|
||||||
if (!user || apiToken !== user.apiToken) {
|
if (!user || apiToken !== user.apiToken) {
|
||||||
throw new NotAuthorized(res.t('invalidCredentials'));
|
throw new InvalidCredentialsError(res.t('invalidCredentials'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.auth.blocked) {
|
if (user.auth.blocked) {
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export default function errorHandler (err, req, res, next) { // eslint-disable-l
|
|||||||
|
|
||||||
const jsonRes = {
|
const jsonRes = {
|
||||||
success: false,
|
success: false,
|
||||||
error: responseErr.name,
|
error: responseErr.code || responseErr.name,
|
||||||
message: responseErr.message,
|
message: responseErr.message,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user