Merge pull request #140 from clems4ever/improve-endpoint-errors
Every public endpoints return 200 with harmonized error messages or 401pull/144/head
commit
f041b946d9
|
@ -59,7 +59,7 @@ module.exports = function (grunt) {
|
|||
},
|
||||
"include-minified-script": {
|
||||
cmd: "sed",
|
||||
args: ["-i", "s/authelia\.js/authelia.min.js/", `${buildDir}/server/src/views/layout/layout.pug`]
|
||||
args: ["-i", "s/authelia.\(js\|css\)/authelia.min.\1/", `${buildDir}/server/src/views/layout/layout.pug`]
|
||||
}
|
||||
},
|
||||
copy: {
|
||||
|
|
|
@ -3,6 +3,7 @@ import BluebirdPromise = require("bluebird");
|
|||
import Endpoints = require("../../../../shared/api");
|
||||
import Constants = require("../../../../shared/constants");
|
||||
import Util = require("util");
|
||||
import UserMessages = require("../../../../shared/UserMessages");
|
||||
|
||||
export function validate(username: string, password: string,
|
||||
redirectUrl: string, $: JQueryStatic): BluebirdPromise<string> {
|
||||
|
@ -24,11 +25,15 @@ export function validate(username: string, password: string,
|
|||
password: password,
|
||||
}
|
||||
})
|
||||
.done(function (data: { redirect: string }) {
|
||||
resolve(data.redirect);
|
||||
.done(function (body: any) {
|
||||
if (body && body.error) {
|
||||
reject(new Error(body.error));
|
||||
return;
|
||||
}
|
||||
resolve(body.redirect);
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
reject(new Error("Authetication failed. Please check your credentials."));
|
||||
reject(new Error(UserMessages.AUTHENTICATION_FAILED));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Notifier } from "../Notifier";
|
|||
import { QueryParametersRetriever } from "../QueryParametersRetriever";
|
||||
import Constants = require("../../../../shared/constants");
|
||||
import Endpoints = require("../../../../shared/api");
|
||||
import UserMessages = require("../../../../shared/UserMessages");
|
||||
|
||||
export default function (window: Window, $: JQueryStatic,
|
||||
firstFactorValidator: typeof FirstFactorValidator, jslogger: typeof JSLogger) {
|
||||
|
@ -23,18 +24,14 @@ export default function (window: Window, $: JQueryStatic,
|
|||
}
|
||||
|
||||
function onFirstFactorSuccess(redirectUrl: string) {
|
||||
jslogger.debug("First factor validated.");
|
||||
window.location.href = redirectUrl;
|
||||
window.location.href = redirectUrl;
|
||||
}
|
||||
|
||||
function onFirstFactorFailure(err: Error) {
|
||||
jslogger.debug("First factor failed.");
|
||||
notifier.error("Authentication failed. Please double check your credentials.");
|
||||
notifier.error(UserMessages.AUTHENTICATION_FAILED);
|
||||
}
|
||||
|
||||
|
||||
$(window.document).ready(function () {
|
||||
jslogger.info("Enter first factor");
|
||||
$("form").on("submit", onFormSubmitted);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import Endpoints = require("../../../../shared/api");
|
||||
import UserMessages = require("../../../../shared/UserMessages");
|
||||
|
||||
import Constants = require("./constants");
|
||||
import { Notifier } from "../Notifier";
|
||||
|
||||
|
@ -12,8 +14,12 @@ export default function (window: Window, $: JQueryStatic) {
|
|||
$.post(Endpoints.RESET_PASSWORD_FORM_POST, {
|
||||
password: newPassword,
|
||||
})
|
||||
.done(function (data) {
|
||||
resolve(data);
|
||||
.done(function (body: any) {
|
||||
if (body && body.error) {
|
||||
reject(new Error(body.error));
|
||||
return;
|
||||
}
|
||||
resolve(body);
|
||||
})
|
||||
.fail(function (xhr, status) {
|
||||
reject(status);
|
||||
|
@ -26,22 +32,21 @@ export default function (window: Window, $: JQueryStatic) {
|
|||
const password2 = $("#password2").val();
|
||||
|
||||
if (!password1 || !password2) {
|
||||
notifier.warning("You must enter your new password twice.");
|
||||
notifier.warning(UserMessages.MISSING_PASSWORD);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (password1 != password2) {
|
||||
notifier.warning("The passwords are different.");
|
||||
notifier.warning(UserMessages.DIFFERENT_PASSWORDS);
|
||||
return false;
|
||||
}
|
||||
|
||||
modifyPassword(password1)
|
||||
.then(function () {
|
||||
notifier.success("Your password has been changed. Please log in again.");
|
||||
window.location.href = Endpoints.FIRST_FACTOR_GET;
|
||||
})
|
||||
.error(function () {
|
||||
notifier.warning("An error occurred during password reset. Your password has not been changed.");
|
||||
notifier.error(UserMessages.RESET_PASSWORD_FAILED);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import Endpoints = require("../../../../shared/api");
|
||||
import UserMessages = require("../../../../shared/UserMessages");
|
||||
import Constants = require("./constants");
|
||||
import jslogger = require("js-logger");
|
||||
import { Notifier } from "../Notifier";
|
||||
|
||||
export default function(window: Window, $: JQueryStatic) {
|
||||
export default function (window: Window, $: JQueryStatic) {
|
||||
const notifier = new Notifier(".notification", $);
|
||||
|
||||
function requestPasswordReset(username: string) {
|
||||
|
@ -14,7 +15,11 @@ export default function(window: Window, $: JQueryStatic) {
|
|||
$.get(Endpoints.RESET_PASSWORD_IDENTITY_START_GET, {
|
||||
userid: username,
|
||||
})
|
||||
.done(function () {
|
||||
.done(function (body: any) {
|
||||
if (body && body.error) {
|
||||
reject(new Error(body.error));
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
|
@ -27,25 +32,24 @@ export default function(window: Window, $: JQueryStatic) {
|
|||
const username = $("#username").val();
|
||||
|
||||
if (!username) {
|
||||
notifier.warning("You must provide your username to reset your password.");
|
||||
notifier.warning(UserMessages.MISSING_USERNAME);
|
||||
return;
|
||||
}
|
||||
|
||||
requestPasswordReset(username)
|
||||
.then(function () {
|
||||
notifier.success("An email has been sent to you. Follow the link to change your password.");
|
||||
notifier.success(UserMessages.MAIL_SENT);
|
||||
setTimeout(function () {
|
||||
window.location.replace(Endpoints.FIRST_FACTOR_GET);
|
||||
}, 1000);
|
||||
})
|
||||
.error(function () {
|
||||
notifier.warning("Are you sure this is your username?");
|
||||
notifier.error(UserMessages.MAIL_NOT_SENT);
|
||||
});
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
jslogger.debug("Reset password request form setup");
|
||||
$(Constants.FORM_SELECTOR).on("submit", onFormSubmitted);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,20 +3,24 @@ import BluebirdPromise = require("bluebird");
|
|||
import Endpoints = require("../../../../shared/api");
|
||||
|
||||
export function validate(token: string, $: JQueryStatic): BluebirdPromise<string> {
|
||||
return new BluebirdPromise<string>(function (resolve, reject) {
|
||||
$.ajax({
|
||||
url: Endpoints.SECOND_FACTOR_TOTP_POST,
|
||||
data: {
|
||||
token: token,
|
||||
},
|
||||
method: "POST",
|
||||
dataType: "json"
|
||||
} as JQueryAjaxSettings)
|
||||
.done(function (data: any) {
|
||||
resolve(data);
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
reject(new Error(textStatus));
|
||||
});
|
||||
});
|
||||
return new BluebirdPromise<string>(function (resolve, reject) {
|
||||
$.ajax({
|
||||
url: Endpoints.SECOND_FACTOR_TOTP_POST,
|
||||
data: {
|
||||
token: token,
|
||||
},
|
||||
method: "POST",
|
||||
dataType: "json"
|
||||
} as JQueryAjaxSettings)
|
||||
.done(function (body: any) {
|
||||
if (body && body.error) {
|
||||
reject(new Error(body.error));
|
||||
return;
|
||||
}
|
||||
resolve(body);
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
reject(new Error(textStatus));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -4,59 +4,64 @@ import U2f = require("u2f");
|
|||
import BluebirdPromise = require("bluebird");
|
||||
import { SignMessage } from "../../../../shared/SignMessage";
|
||||
import Endpoints = require("../../../../shared/api");
|
||||
import UserMessages = require("../../../../shared/UserMessages");
|
||||
import { INotifier } from "../INotifier";
|
||||
|
||||
function finishU2fAuthentication(responseData: U2fApi.SignResponse, $: JQueryStatic): BluebirdPromise<void> {
|
||||
return new BluebirdPromise<void>(function (resolve, reject) {
|
||||
$.ajax({
|
||||
url: Endpoints.SECOND_FACTOR_U2F_SIGN_POST,
|
||||
data: responseData,
|
||||
method: "POST",
|
||||
dataType: "json"
|
||||
} as JQueryAjaxSettings)
|
||||
.done(function (data) {
|
||||
resolve(data);
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
reject(new Error(textStatus));
|
||||
});
|
||||
});
|
||||
return new BluebirdPromise<void>(function (resolve, reject) {
|
||||
$.ajax({
|
||||
url: Endpoints.SECOND_FACTOR_U2F_SIGN_POST,
|
||||
data: responseData,
|
||||
method: "POST",
|
||||
dataType: "json"
|
||||
} as JQueryAjaxSettings)
|
||||
.done(function (body: any) {
|
||||
if (body && body.error) {
|
||||
reject(new Error(body.error));
|
||||
return;
|
||||
}
|
||||
resolve(body);
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
reject(new Error(textStatus));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function startU2fAuthentication($: JQueryStatic, notifier: INotifier, u2fApi: typeof U2fApi): BluebirdPromise<void> {
|
||||
return new BluebirdPromise<void>(function (resolve, reject) {
|
||||
$.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, undefined, "json")
|
||||
.done(function (signResponse: SignMessage) {
|
||||
notifier.info("Please touch the token");
|
||||
return new BluebirdPromise<void>(function (resolve, reject) {
|
||||
$.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, undefined, "json")
|
||||
.done(function (signResponse: SignMessage) {
|
||||
notifier.info(UserMessages.PLEASE_TOUCH_TOKEN);
|
||||
|
||||
const signRequest: U2fApi.SignRequest = {
|
||||
appId: signResponse.request.appId,
|
||||
challenge: signResponse.request.challenge,
|
||||
keyHandle: signResponse.keyHandle, // linked to the client session cookie
|
||||
version: "U2F_V2"
|
||||
};
|
||||
const signRequest: U2fApi.SignRequest = {
|
||||
appId: signResponse.request.appId,
|
||||
challenge: signResponse.request.challenge,
|
||||
keyHandle: signResponse.keyHandle, // linked to the client session cookie
|
||||
version: "U2F_V2"
|
||||
};
|
||||
|
||||
u2fApi.sign([signRequest], 60)
|
||||
.then(function (signResponse: U2fApi.SignResponse) {
|
||||
finishU2fAuthentication(signResponse, $)
|
||||
.then(function (data) {
|
||||
resolve(data);
|
||||
}, function (err) {
|
||||
notifier.error("Error when finish U2F transaction");
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch(function (err: Error) {
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
reject(new Error(textStatus));
|
||||
});
|
||||
});
|
||||
u2fApi.sign([signRequest], 60)
|
||||
.then(function (signResponse: U2fApi.SignResponse) {
|
||||
finishU2fAuthentication(signResponse, $)
|
||||
.then(function (data) {
|
||||
resolve(data);
|
||||
}, function (err) {
|
||||
notifier.error(UserMessages.U2F_TRANSACTION_FINISH_FAILED);
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch(function (err: Error) {
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
reject(new Error(textStatus));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function validate($: JQueryStatic, notifier: INotifier, u2fApi: typeof U2fApi): BluebirdPromise<void> {
|
||||
return startU2fAuthentication($, notifier, u2fApi);
|
||||
return startU2fAuthentication($, notifier, u2fApi);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Notifier } from "../Notifier";
|
|||
import { QueryParametersRetriever } from "../QueryParametersRetriever";
|
||||
import Endpoints = require("../../../../shared/api");
|
||||
import ServerConstants = require("../../../../shared/constants");
|
||||
|
||||
import UserMessages = require("../../../../shared/UserMessages");
|
||||
|
||||
export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi) {
|
||||
const notifierTotp = new Notifier(".notification-totp", $);
|
||||
|
@ -20,7 +20,7 @@ export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi)
|
|||
if (redirectUrl)
|
||||
window.location.href = redirectUrl;
|
||||
else
|
||||
notifier.success("Authentication succeeded. You can now access your services.");
|
||||
notifier.success(UserMessages.AUTHENTICATION_SUCCEEDED);
|
||||
}
|
||||
|
||||
function onSecondFactorTotpSuccess(data: any) {
|
||||
|
@ -28,7 +28,7 @@ export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi)
|
|||
}
|
||||
|
||||
function onSecondFactorTotpFailure(err: Error) {
|
||||
notifierTotp.error("Problem with TOTP validation.");
|
||||
notifierTotp.error(UserMessages.AUTHENTICATION_TOTP_FAILED);
|
||||
}
|
||||
|
||||
function onU2fAuthenticationSuccess(data: any) {
|
||||
|
@ -36,13 +36,11 @@ export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi)
|
|||
}
|
||||
|
||||
function onU2fAuthenticationFailure() {
|
||||
notifierU2f.error("Problem with U2F validation. Did you register before authenticating?");
|
||||
notifierU2f.error(UserMessages.AUTHENTICATION_U2F_FAILED);
|
||||
}
|
||||
|
||||
function onTOTPFormSubmitted(): boolean {
|
||||
const token = $(Constants.TOTP_TOKEN_SELECTOR).val();
|
||||
jslogger.debug("TOTP token is %s", token);
|
||||
|
||||
TOTPValidator.validate(token, $)
|
||||
.then(onSecondFactorTotpSuccess)
|
||||
.catch(onSecondFactorTotpFailure);
|
||||
|
@ -50,7 +48,6 @@ export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi)
|
|||
}
|
||||
|
||||
function onU2FFormSubmitted(): boolean {
|
||||
jslogger.debug("Start U2F authentication");
|
||||
U2FValidator.validate($, notifierU2f, U2fApi)
|
||||
.then(onU2fAuthenticationSuccess, onU2fAuthenticationFailure);
|
||||
return false;
|
||||
|
|
|
@ -5,6 +5,7 @@ import u2fApi = require("u2f-api");
|
|||
import jslogger = require("js-logger");
|
||||
import { Notifier } from "../Notifier";
|
||||
import Endpoints = require("../../../../shared/api");
|
||||
import UserMessages = require("../../../../shared/UserMessages");
|
||||
|
||||
export default function (window: Window, $: JQueryStatic) {
|
||||
const notifier = new Notifier(".notification", $);
|
||||
|
@ -16,8 +17,12 @@ export default function (window: Window, $: JQueryStatic) {
|
|||
|
||||
return new BluebirdPromise<string>(function (resolve, reject) {
|
||||
$.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, registrationData, undefined, "json")
|
||||
.done(function (data) {
|
||||
resolve(data.redirection_url);
|
||||
.done(function (body: any) {
|
||||
if (body && body.error) {
|
||||
reject(new Error(body.error));
|
||||
return;
|
||||
}
|
||||
resolve(body.redirection_url);
|
||||
})
|
||||
.fail(function (xhr, status) {
|
||||
reject();
|
||||
|
@ -29,8 +34,6 @@ export default function (window: Window, $: JQueryStatic) {
|
|||
return new BluebirdPromise<string>(function (resolve, reject) {
|
||||
$.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, {}, undefined, "json")
|
||||
.done(function (registrationRequest: U2f.Request) {
|
||||
jslogger.debug("registrationRequest = %s", JSON.stringify(registrationRequest));
|
||||
|
||||
const registerRequest: u2fApi.RegisterRequest = registrationRequest;
|
||||
u2fApi.register([registerRequest], [], 120)
|
||||
.then(function (res: u2fApi.RegisterResponse) {
|
||||
|
@ -47,7 +50,7 @@ export default function (window: Window, $: JQueryStatic) {
|
|||
}
|
||||
|
||||
function onRegisterFailure(err: Error) {
|
||||
notifier.error("Problem while registering your U2F device.");
|
||||
notifier.error(UserMessages.REGISTRATION_U2F_FAILED);
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
|
|
|
@ -38,7 +38,7 @@ describe("test FirstFactorValidator", function () {
|
|||
|
||||
describe("should fail first factor validation", () => {
|
||||
it("should fail with error", () => {
|
||||
return should_fail_first_factor_validation("Authetication failed. Please check your credentials.");
|
||||
return should_fail_first_factor_validation("Authentication failed. Please check your credentials.");
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,33 +5,33 @@ import BluebirdPromise = require("bluebird");
|
|||
import Assert = require("assert");
|
||||
|
||||
describe("test TOTPValidator", function () {
|
||||
it("should initiate an identity check successfully", () => {
|
||||
const postPromise = JQueryMock.JQueryDeferredMock();
|
||||
postPromise.done.yields();
|
||||
postPromise.done.returns(postPromise);
|
||||
it("should initiate an identity check successfully", () => {
|
||||
const postPromise = JQueryMock.JQueryDeferredMock();
|
||||
postPromise.done.yields();
|
||||
postPromise.done.returns(postPromise);
|
||||
|
||||
const jqueryMock = JQueryMock.JQueryMock();
|
||||
jqueryMock.jquery.ajax.returns(postPromise);
|
||||
const jqueryMock = JQueryMock.JQueryMock();
|
||||
jqueryMock.jquery.ajax.returns(postPromise);
|
||||
|
||||
return TOTPValidator.validate("totp_token", jqueryMock.jquery as any);
|
||||
});
|
||||
return TOTPValidator.validate("totp_token", jqueryMock.jquery as any);
|
||||
});
|
||||
|
||||
it("should fail validating TOTP token", () => {
|
||||
const errorMessage = "Error while validating TOTP token";
|
||||
it("should fail validating TOTP token", () => {
|
||||
const errorMessage = "Error while validating TOTP token";
|
||||
|
||||
const postPromise = JQueryMock.JQueryDeferredMock();
|
||||
postPromise.fail.yields(undefined, errorMessage);
|
||||
postPromise.done.returns(postPromise);
|
||||
const postPromise = JQueryMock.JQueryDeferredMock();
|
||||
postPromise.fail.yields(undefined, errorMessage);
|
||||
postPromise.done.returns(postPromise);
|
||||
|
||||
const jqueryMock = JQueryMock.JQueryMock();
|
||||
jqueryMock.jquery.ajax.returns(postPromise);
|
||||
const jqueryMock = JQueryMock.JQueryMock();
|
||||
jqueryMock.jquery.ajax.returns(postPromise);
|
||||
|
||||
return TOTPValidator.validate("totp_token", jqueryMock.jquery as any)
|
||||
.then(function () {
|
||||
return BluebirdPromise.reject(new Error("Registration successfully finished while it should have not."));
|
||||
}, function (err: Error) {
|
||||
Assert.equal(errorMessage, err.message);
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
return TOTPValidator.validate("totp_token", jqueryMock.jquery as any)
|
||||
.then(function () {
|
||||
return BluebirdPromise.reject(new Error("Registration successfully finished while it should have not."));
|
||||
}, function (err: Error) {
|
||||
Assert.equal(errorMessage, err.message);
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -67,8 +67,8 @@
|
|||
"@types/query-string": "^4.3.1",
|
||||
"@types/randomstring": "^1.1.5",
|
||||
"@types/redis": "^2.6.0",
|
||||
"@types/request": "0.0.46",
|
||||
"@types/request-promise": "^4.1.37",
|
||||
"@types/request": "^2.0.5",
|
||||
"@types/request-promise": "^4.1.38",
|
||||
"@types/selenium-webdriver": "^3.0.4",
|
||||
"@types/sinon": "^2.2.1",
|
||||
"@types/speakeasy": "^2.0.1",
|
||||
|
@ -97,7 +97,7 @@
|
|||
"power-assert": "^1.4.4",
|
||||
"proxyquire": "^1.8.0",
|
||||
"query-string": "^4.3.4",
|
||||
"request": "^2.81.0",
|
||||
"request": "^2.83.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"selenium-webdriver": "^3.5.0",
|
||||
"should": "^11.1.1",
|
||||
|
|
|
@ -3,12 +3,12 @@ import BluebirdPromise = require("bluebird");
|
|||
import { IRequestLogger } from "./logging/IRequestLogger";
|
||||
|
||||
function replyWithError(req: express.Request, res: express.Response,
|
||||
code: number, logger: IRequestLogger): (err: Error) => void {
|
||||
code: number, logger: IRequestLogger, body?: Object): (err: Error) => void {
|
||||
return function (err: Error): void {
|
||||
logger.error(req, "Reply with error %d: %s", code, err.message);
|
||||
logger.debug(req, "%s", err.stack);
|
||||
res.status(code);
|
||||
res.send();
|
||||
res.send(body);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ export function replyWithError403(req: express.Request,
|
|||
return replyWithError(req, res, 403, logger);
|
||||
}
|
||||
|
||||
export function replyWithError500(req: express.Request,
|
||||
res: express.Response, logger: IRequestLogger) {
|
||||
return replyWithError(req, res, 500, logger);
|
||||
export function replyWithError200(req: express.Request,
|
||||
res: express.Response, logger: IRequestLogger, message: string) {
|
||||
return replyWithError(req, res, 200, logger, { error: message });
|
||||
}
|
|
@ -9,7 +9,9 @@ export function validate(req: express.Request): BluebirdPromise<void> {
|
|||
return AuthenticationSession.get(req)
|
||||
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
|
||||
if (!authSession.userid || !authSession.first_factor)
|
||||
return BluebirdPromise.reject(new Exceptions.FirstFactorValidationError("First factor has not been validated yet."));
|
||||
return BluebirdPromise.reject(
|
||||
new Exceptions.FirstFactorValidationError(
|
||||
"First factor has not been validated yet."));
|
||||
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
|
|
|
@ -28,8 +28,10 @@ export interface IdentityValidable {
|
|||
preValidationInit(req: express.Request): BluebirdPromise<Identity.Identity>;
|
||||
postValidationInit(req: express.Request): BluebirdPromise<void>;
|
||||
|
||||
preValidationResponse(req: express.Request, res: express.Response): void; // Serves a page after identity check request
|
||||
postValidationResponse(req: express.Request, res: express.Response): void; // Serves the page if identity validated
|
||||
// Serves a page after identity check request
|
||||
preValidationResponse(req: express.Request, res: express.Response): void;
|
||||
// Serves the page if identity validated
|
||||
postValidationResponse(req: express.Request, res: express.Response): void;
|
||||
mailSubject(): string;
|
||||
}
|
||||
|
||||
|
@ -50,7 +52,8 @@ function consumeToken(token: string, challenge: string, userDataStore: IUserData
|
|||
return userDataStore.consumeIdentityValidationToken(token, challenge);
|
||||
}
|
||||
|
||||
export function register(app: express.Application, pre_validation_endpoint: string, post_validation_endpoint: string, handler: IdentityValidable) {
|
||||
export function register(app: express.Application, pre_validation_endpoint: string,
|
||||
post_validation_endpoint: string, handler: IdentityValidable) {
|
||||
app.get(pre_validation_endpoint, get_start_validation(handler, post_validation_endpoint));
|
||||
app.get(post_validation_endpoint, get_finish_validation(handler));
|
||||
}
|
||||
|
@ -91,14 +94,13 @@ export function get_finish_validation(handler: IdentityValidable): express.Reque
|
|||
handler.postValidationResponse(req, res);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(Exceptions.FirstFactorValidationError, ErrorReplies.replyWithError401(req, res, logger))
|
||||
.catch(Exceptions.AccessDeniedError, ErrorReplies.replyWithError403(req, res, logger))
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError401(req, res, logger));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function get_start_validation(handler: IdentityValidable, postValidationEndpoint: string): express.RequestHandler {
|
||||
export function get_start_validation(handler: IdentityValidable, postValidationEndpoint: string)
|
||||
: express.RequestHandler {
|
||||
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const notifier = ServerVariablesHandler.getNotifier(req.app);
|
||||
|
@ -113,13 +115,15 @@ export function get_start_validation(handler: IdentityValidable, postValidationE
|
|||
logger.info(req, "Start identity validation of user \"%s\"", userid);
|
||||
|
||||
if (!(email && userid))
|
||||
return BluebirdPromise.reject(new Exceptions.IdentityError("Missing user id or email address"));
|
||||
return BluebirdPromise.reject(new Exceptions.IdentityError(
|
||||
"Missing user id or email address"));
|
||||
|
||||
return createAndSaveToken(userid, handler.challenge(), userDataStore);
|
||||
})
|
||||
.then(function (token: string) {
|
||||
const host = req.get("Host");
|
||||
const link_url = util.format("https://%s%s?identity_token=%s", host, postValidationEndpoint, token);
|
||||
const link_url = util.format("https://%s%s?identity_token=%s", host,
|
||||
postValidationEndpoint, token);
|
||||
logger.info(req, "Notification sent to user \"%s\"", identity.userid);
|
||||
return notifier.notify(identity.email, handler.mailSubject(), link_url);
|
||||
})
|
||||
|
@ -127,9 +131,6 @@ export function get_start_validation(handler: IdentityValidable, postValidationE
|
|||
handler.preValidationResponse(req, res);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(Exceptions.FirstFactorValidationError, ErrorReplies.replyWithError401(req, res, logger))
|
||||
.catch(Exceptions.IdentityError, ErrorReplies.replyWithError400(req, res, logger))
|
||||
.catch(Exceptions.AccessDeniedError, ErrorReplies.replyWithError403(req, res, logger))
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError401(req, res, logger));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import ErrorReplies = require("../ErrorReplies");
|
|||
import objectPath = require("object-path");
|
||||
import { ServerVariablesHandler } from "../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../AuthenticationSession");
|
||||
import UserMessages = require("../../../../shared/UserMessages");
|
||||
|
||||
type Handler = (req: express.Request, res: express.Response) => BluebirdPromise<void>;
|
||||
|
||||
|
@ -21,6 +22,6 @@ export default function (callback: Handler): Handler {
|
|||
.then(function () {
|
||||
return callback(req, res);
|
||||
})
|
||||
.catch(Exceptions.FirstFactorValidationError, ErrorReplies.replyWithError401(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError401(req, res, logger));
|
||||
};
|
||||
}
|
|
@ -12,6 +12,7 @@ import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
|||
import AuthenticationSession = require("../../AuthenticationSession");
|
||||
import Constants = require("../../../../../shared/constants");
|
||||
import { DomainExtractor } from "../../utils/DomainExtractor";
|
||||
import UserMessages = require("../../../../../shared/UserMessages");
|
||||
|
||||
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const username: string = req.body.username;
|
||||
|
@ -22,9 +23,7 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
const config = ServerVariablesHandler.getConfiguration(req.app);
|
||||
|
||||
if (!username || !password) {
|
||||
const err = new Error("No username or password");
|
||||
ErrorReplies.replyWithError401(req, res, logger)(err);
|
||||
return BluebirdPromise.reject(err);
|
||||
return BluebirdPromise.reject(new Error("No username or password."));
|
||||
}
|
||||
|
||||
const regulator = ServerVariablesHandler.getAuthenticationRegulator(req.app);
|
||||
|
@ -93,12 +92,9 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
}
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(exceptions.LdapSearchError, ErrorReplies.replyWithError500(req, res, logger))
|
||||
.catch(exceptions.LdapBindError, function (err: Error) {
|
||||
regulator.mark(username, false);
|
||||
return ErrorReplies.replyWithError401(req, res, logger)(err);
|
||||
return ErrorReplies.replyWithError200(req, res, logger, UserMessages.OPERATION_FAILED)(err);
|
||||
})
|
||||
.catch(exceptions.AuthenticationRegulationError, ErrorReplies.replyWithError403(req, res, logger))
|
||||
.catch(exceptions.DomainAccessDenied, ErrorReplies.replyWithError401(req, res, logger))
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger, UserMessages.OPERATION_FAILED));
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import exceptions = require("../../../Exceptions");
|
|||
import { ServerVariablesHandler } from "../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../AuthenticationSession");
|
||||
import ErrorReplies = require("../../../ErrorReplies");
|
||||
import UserMessages = require("../../../../../../shared/UserMessages");
|
||||
|
||||
import Constants = require("./../constants");
|
||||
|
||||
|
@ -23,8 +24,6 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
logger.debug(req, "Challenge %s", authSession.identity_check.challenge);
|
||||
|
||||
if (authSession.identity_check.challenge != Constants.CHALLENGE) {
|
||||
res.status(403);
|
||||
res.send();
|
||||
return BluebirdPromise.reject(new Error("Bad challenge."));
|
||||
}
|
||||
return ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
|
||||
|
@ -37,5 +36,5 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
res.send();
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger, UserMessages.RESET_PASSWORD_FAILED));
|
||||
}
|
||||
|
|
|
@ -18,5 +18,6 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
});
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger,
|
||||
"Unexpected error."));
|
||||
}
|
|
@ -11,6 +11,7 @@ import Endpoints = require("../../../../../../../shared/api");
|
|||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||
|
||||
import FirstFactorValidator = require("../../../../FirstFactorValidator");
|
||||
|
||||
|
@ -62,8 +63,6 @@ export default class RegistrationHandler implements IdentityValidable {
|
|||
const challenge = authSession.identity_check.challenge;
|
||||
|
||||
if (challenge != Constants.CHALLENGE || !userid) {
|
||||
res.status(403);
|
||||
res.send();
|
||||
return BluebirdPromise.reject(new Error("Bad challenge."));
|
||||
}
|
||||
|
||||
|
@ -83,7 +82,7 @@ export default class RegistrationHandler implements IdentityValidable {
|
|||
});
|
||||
});
|
||||
})
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger, UserMessages.OPERATION_FAILED));
|
||||
}
|
||||
|
||||
mailSubject(): string {
|
||||
|
|
|
@ -10,6 +10,7 @@ import redirect from "../../redirect";
|
|||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import { ServerVariablesHandler } from "./../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||
|
||||
const UNAUTHORIZED_MESSAGE = "Unauthorized access";
|
||||
|
||||
|
@ -25,7 +26,7 @@ export function handler(req: express.Request, res: express.Response): BluebirdPr
|
|||
return AuthenticationSession.get(req)
|
||||
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
|
||||
authSession = _authSession;
|
||||
logger.info(req, "Initiate TOTP validation for user '%s'", authSession.userid);
|
||||
logger.info(req, "Initiate TOTP validation for user '%s'.", authSession.userid);
|
||||
return userDataStore.retrieveTOTPSecret(authSession.userid);
|
||||
})
|
||||
.then(function (doc: TOTPSecretDocument) {
|
||||
|
@ -33,11 +34,11 @@ export function handler(req: express.Request, res: express.Response): BluebirdPr
|
|||
return totpValidator.validate(token, doc.secret.base32);
|
||||
})
|
||||
.then(function () {
|
||||
logger.debug(req, "TOTP validation succeeded");
|
||||
logger.debug(req, "TOTP validation succeeded.");
|
||||
authSession.second_factor = true;
|
||||
redirect(req, res);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(exceptions.InvalidTOTPError, ErrorReplies.replyWithError401(req, res, logger))
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger,
|
||||
UserMessages.OPERATION_FAILED));
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import redirect from "../../redirect";
|
|||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||
|
||||
|
||||
export default FirstFactorBlocker(handler);
|
||||
|
@ -31,15 +32,11 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
|
|||
const registrationRequest = authSession.register_request;
|
||||
|
||||
if (!registrationRequest) {
|
||||
res.status(403);
|
||||
res.send();
|
||||
return BluebirdPromise.reject(new Error("No registration request"));
|
||||
}
|
||||
|
||||
if (!authSession.identity_check
|
||||
|| authSession.identity_check.challenge != "u2f-register") {
|
||||
res.status(403);
|
||||
res.send();
|
||||
return BluebirdPromise.reject(new Error("Bad challenge for registration request"));
|
||||
}
|
||||
|
||||
|
@ -67,5 +64,6 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
|
|||
redirect(req, res);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger,
|
||||
UserMessages.OPERATION_FAILED));
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
|||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||
|
||||
export default FirstFactorBlocker(handler);
|
||||
|
||||
|
@ -41,5 +42,6 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
|
|||
res.json(registrationRequest);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger,
|
||||
UserMessages.OPERATION_FAILED));
|
||||
}
|
|
@ -13,6 +13,7 @@ import redirect from "../../redirect";
|
|||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||
|
||||
export default FirstFactorBlocker(handler);
|
||||
|
||||
|
@ -50,6 +51,7 @@ export function handler(req: express.Request, res: express.Response): BluebirdPr
|
|||
redirect(req, res);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger,
|
||||
UserMessages.OPERATION_FAILED));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
|||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||
|
||||
export default FirstFactorBlocker(handler);
|
||||
|
||||
|
@ -50,7 +51,7 @@ export function handler(req: express.Request, res: express.Response): BluebirdPr
|
|||
res.json(authenticationMessage);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(exceptions.AccessDeniedError, ErrorReplies.replyWithError401(req, res, logger))
|
||||
.catch(ErrorReplies.replyWithError500(req, res, logger));
|
||||
.catch(ErrorReplies.replyWithError200(req, res, logger,
|
||||
UserMessages.OPERATION_FAILED));
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,10 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
res.send();
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(exceptions.DomainAccessDenied, ErrorReplies.replyWithError403(req, res, logger))
|
||||
// The user is authenticated but has restricted access -> 403
|
||||
.catch(exceptions.DomainAccessDenied, ErrorReplies
|
||||
.replyWithError403(req, res, logger))
|
||||
// The user is not yet authenticated -> 401
|
||||
.catch(ErrorReplies.replyWithError401(req, res, logger));
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ html
|
|||
title Authelia - 2FA
|
||||
meta(name="viewport", content="width=device-width, initial-scale=1.0")/
|
||||
link(rel="icon", href="/img/icon.png" type="image/png" sizes="32x32")/
|
||||
link(rel="stylesheet", type="text/css", href="/css/authelia.min.css")/
|
||||
link(rel="stylesheet", type="text/css", href="/css/authelia.css")/
|
||||
if redirection_url
|
||||
<meta http-equiv="refresh" content="5;url=#{redirection_url}">
|
||||
body
|
||||
|
|
|
@ -5,7 +5,7 @@ import AuthenticationSession = require("../src/lib/AuthenticationSession");
|
|||
import { UserDataStore } from "../src/lib/storage/UserDataStore";
|
||||
|
||||
import exceptions = require("../src/lib/Exceptions");
|
||||
import assert = require("assert");
|
||||
import Assert = require("assert");
|
||||
import Promise = require("bluebird");
|
||||
import express = require("express");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
@ -69,11 +69,11 @@ describe("test identity check process", function () {
|
|||
return callback(req as any, res as any, undefined)
|
||||
.then(function () { return BluebirdPromise.reject("Should fail"); })
|
||||
.catch(function () {
|
||||
assert.equal(res.status.getCall(0).args[0], 401);
|
||||
Assert.equal(res.status.getCall(0).args[0], 401);
|
||||
});
|
||||
});
|
||||
|
||||
it("should send 400 if email is missing in provided identity", function () {
|
||||
it("should send 401 if email is missing in provided identity", function () {
|
||||
const identity = { userid: "abc" };
|
||||
|
||||
identityValidable.preValidationInit.returns(BluebirdPromise.resolve(identity));
|
||||
|
@ -82,11 +82,11 @@ describe("test identity check process", function () {
|
|||
return callback(req as any, res as any, undefined)
|
||||
.then(function () { return BluebirdPromise.reject("Should fail"); })
|
||||
.catch(function () {
|
||||
assert.equal(res.status.getCall(0).args[0], 400);
|
||||
Assert.equal(res.status.getCall(0).args[0], 401);
|
||||
});
|
||||
});
|
||||
|
||||
it("should send 400 if userid is missing in provided identity", function () {
|
||||
it("should send 401 if userid is missing in provided identity", function () {
|
||||
const endpoint = "/protected";
|
||||
const identity = { email: "abc@example.com" };
|
||||
|
||||
|
@ -96,7 +96,7 @@ describe("test identity check process", function () {
|
|||
return callback(req as any, res as any, undefined)
|
||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||
.catch(function (err: Error) {
|
||||
assert.equal(res.status.getCall(0).args[0], 400);
|
||||
Assert.equal(res.status.getCall(0).args[0], 401);
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
|
@ -111,23 +111,23 @@ describe("test identity check process", function () {
|
|||
|
||||
return callback(req as any, res as any, undefined)
|
||||
.then(function () {
|
||||
assert(notifier.notify.calledOnce);
|
||||
assert(mocks.userDataStore.produceIdentityValidationTokenStub.calledOnce);
|
||||
assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[0], "user");
|
||||
assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[3], 240000);
|
||||
Assert(notifier.notify.calledOnce);
|
||||
Assert(mocks.userDataStore.produceIdentityValidationTokenStub.calledOnce);
|
||||
Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[0], "user");
|
||||
Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[3], 240000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function test_finish_get_handler() {
|
||||
it("should send 403 if no identity_token is provided", function () {
|
||||
it("should send 401 if no identity_token is provided", function () {
|
||||
|
||||
const callback = IdentityValidator.get_finish_validation(identityValidable);
|
||||
|
||||
return callback(req as any, res as any, undefined)
|
||||
.then(function () { return BluebirdPromise.reject("Should fail"); })
|
||||
.catch(function () {
|
||||
assert.equal(res.status.getCall(0).args[0], 403);
|
||||
Assert.equal(res.status.getCall(0).args[0], 401);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -138,7 +138,7 @@ describe("test identity check process", function () {
|
|||
return callback(req as any, res as any, undefined);
|
||||
});
|
||||
|
||||
it("should return 500 if identity_token is provided but invalid", function () {
|
||||
it("should return 401 if identity_token is provided but invalid", function () {
|
||||
req.query.identity_token = "token";
|
||||
|
||||
mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.reject(new Error("Invalid token")));
|
||||
|
@ -147,7 +147,7 @@ describe("test identity check process", function () {
|
|||
return callback(req as any, res as any, undefined)
|
||||
.then(function () { return BluebirdPromise.reject("Should fail"); })
|
||||
.catch(function () {
|
||||
assert.equal(res.status.getCall(0).args[0], 500);
|
||||
Assert.equal(res.status.getCall(0).args[0], 401);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -165,7 +165,7 @@ describe("test identity check process", function () {
|
|||
})
|
||||
.then(function () { return BluebirdPromise.reject("Should fail"); })
|
||||
.catch(function () {
|
||||
assert.equal(authSession.identity_check.userid, "user");
|
||||
Assert.equal(authSession.identity_check.userid, "user");
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
import sinon = require("sinon");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
import assert = require("assert");
|
||||
import Assert = require("assert");
|
||||
import winston = require("winston");
|
||||
|
||||
import FirstFactorPost = require("../../../src/lib/routes/firstfactor/post");
|
||||
|
@ -87,8 +87,8 @@ describe("test the first factor validation route", function () {
|
|||
return FirstFactorPost.default(req as any, res as any);
|
||||
})
|
||||
.then(function () {
|
||||
assert.equal("username", authSession.userid);
|
||||
assert(res.send.calledOnce);
|
||||
Assert.equal("username", authSession.userid);
|
||||
Assert(res.send.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -113,27 +113,32 @@ describe("test the first factor validation route", function () {
|
|||
return FirstFactorPost.default(req as any, res as any);
|
||||
})
|
||||
.then(function () {
|
||||
assert.equal("test_ok@example.com", authSession.email);
|
||||
Assert.equal("test_ok@example.com", authSession.email);
|
||||
});
|
||||
});
|
||||
|
||||
it("should return status code 401 when LDAP authenticator throws", function () {
|
||||
it("should return error message when LDAP authenticator throws", function () {
|
||||
(serverVariables.ldapAuthenticator as any).authenticate.withArgs("username", "password")
|
||||
.returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials")));
|
||||
return FirstFactorPost.default(req as any, res as any)
|
||||
.then(function () {
|
||||
assert.equal(401, res.status.getCall(0).args[0]);
|
||||
assert.equal(regulator.mark.getCall(0).args[0], "username");
|
||||
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||
Assert.equal(regulator.mark.getCall(0).args[0], "username");
|
||||
Assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "Operation failed."
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should return status code 403 when regulator rejects authentication", function () {
|
||||
it("should return error message when regulator rejects authentication", function () {
|
||||
const err = new exceptions.AuthenticationRegulationError("Authentication regulation...");
|
||||
regulator.regulate.returns(BluebirdPromise.reject(err));
|
||||
return FirstFactorPost.default(req as any, res as any)
|
||||
.then(function () {
|
||||
assert.equal(403, res.status.getCall(0).args[0]);
|
||||
assert.equal(1, res.send.callCount);
|
||||
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||
Assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "Operation failed."
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ import { ServerVariablesHandler } from "../../../src/lib/ServerVariablesHandler"
|
|||
import { UserDataStore } from "../../../src/lib/storage/UserDataStore";
|
||||
import Sinon = require("sinon");
|
||||
import winston = require("winston");
|
||||
import assert = require("assert");
|
||||
import Assert = require("assert");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import ExpressMock = require("../../mocks/express");
|
||||
|
@ -85,9 +85,9 @@ describe("test reset password route", function () {
|
|||
.then(function () {
|
||||
return AuthenticationSession.get(req as any);
|
||||
}).then(function (_authSession: AuthenticationSession.AuthenticationSession) {
|
||||
assert.equal(res.status.getCall(0).args[0], 204);
|
||||
assert.equal(_authSession.first_factor, false);
|
||||
assert.equal(_authSession.second_factor, false);
|
||||
Assert.equal(res.status.getCall(0).args[0], 204);
|
||||
Assert.equal(_authSession.first_factor, false);
|
||||
Assert.equal(_authSession.second_factor, false);
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
|
@ -102,7 +102,10 @@ describe("test reset password route", function () {
|
|||
return PasswordResetFormPost.default(req as any, res as any);
|
||||
})
|
||||
.then(function () {
|
||||
assert.equal(res.status.getCall(0).args[0], 403);
|
||||
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||
Assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "An error occurred during password reset. Your password has not been changed."
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -121,7 +124,10 @@ describe("test reset password route", function () {
|
|||
};
|
||||
return PasswordResetFormPost.default(req as any, res as any);
|
||||
}).then(function () {
|
||||
assert.equal(res.status.getCall(0).args[0], 500);
|
||||
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||
Assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "An error occurred during password reset. Your password has not been changed."
|
||||
});
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -84,7 +84,7 @@ describe("test u2f routes: register", function () {
|
|||
});
|
||||
});
|
||||
|
||||
it("should return unauthorized on finishRegistration error", function () {
|
||||
it("should return error message on finishRegistration error", function () {
|
||||
const user_key_container = {};
|
||||
const u2f_mock = U2FMock.U2FMock();
|
||||
u2f_mock.checkRegistration.returns({ errorCode: 500 });
|
||||
|
@ -103,12 +103,15 @@ describe("test u2f routes: register", function () {
|
|||
})
|
||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||
.catch(function () {
|
||||
assert.equal(500, res.status.getCall(0).args[0]);
|
||||
assert.equal(200, res.status.getCall(0).args[0]);
|
||||
assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "Operation failed."
|
||||
});
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return 403 when register_request is not provided", function () {
|
||||
it("should return error message when register_request is not provided", function () {
|
||||
const user_key_container = {};
|
||||
const u2f_mock = U2FMock.U2FMock();
|
||||
u2f_mock.checkRegistration.returns(BluebirdPromise.resolve());
|
||||
|
@ -121,12 +124,15 @@ describe("test u2f routes: register", function () {
|
|||
})
|
||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||
.catch(function () {
|
||||
assert.equal(403, res.status.getCall(0).args[0]);
|
||||
assert.equal(200, res.status.getCall(0).args[0]);
|
||||
assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "Operation failed."
|
||||
});
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return forbidden error when no auth request has been initiated", function () {
|
||||
it("should return error message when no auth request has been initiated", function () {
|
||||
const user_key_container = {};
|
||||
const u2f_mock = U2FMock.U2FMock();
|
||||
u2f_mock.checkRegistration.returns(BluebirdPromise.resolve());
|
||||
|
@ -139,12 +145,15 @@ describe("test u2f routes: register", function () {
|
|||
})
|
||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||
.catch(function () {
|
||||
assert.equal(403, res.status.getCall(0).args[0]);
|
||||
assert.equal(200, res.status.getCall(0).args[0]);
|
||||
assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "Operation failed."
|
||||
});
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return forbidden error when identity has not been verified", function () {
|
||||
it("should return error message when identity has not been verified", function () {
|
||||
return AuthenticationSession.get(req as any)
|
||||
.then(function (authSession) {
|
||||
authSession.identity_check = undefined;
|
||||
|
@ -152,7 +161,10 @@ describe("test u2f routes: register", function () {
|
|||
})
|
||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||
.catch(function () {
|
||||
assert.equal(403, res.status.getCall(0).args[0]);
|
||||
assert.equal(200, res.status.getCall(0).args[0]);
|
||||
assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "Operation failed."
|
||||
});
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
import sinon = require("sinon");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
import assert = require("assert");
|
||||
import Assert = require("assert");
|
||||
import U2FRegisterRequestGet = require("../../../../../src/lib/routes/secondfactor/u2f/register_request/get");
|
||||
import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession");
|
||||
import { ServerVariablesHandler } from "../../../../../src/lib/ServerVariablesHandler";
|
||||
|
@ -67,28 +67,31 @@ describe("test u2f routes: register_request", function () {
|
|||
mocks.u2f = u2f_mock;
|
||||
return U2FRegisterRequestGet.default(req as any, res as any)
|
||||
.then(function () {
|
||||
assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]);
|
||||
Assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]);
|
||||
});
|
||||
});
|
||||
|
||||
it("should return internal error on registration request", function (done) {
|
||||
res.send = sinon.spy(function (data: any) {
|
||||
assert.equal(500, res.status.getCall(0).args[0]);
|
||||
done();
|
||||
});
|
||||
it("should return internal error on registration request", function () {
|
||||
res.send = sinon.spy();
|
||||
const user_key_container = {};
|
||||
const u2f_mock = U2FMock.U2FMock();
|
||||
u2f_mock.request.returns(BluebirdPromise.reject("Internal error"));
|
||||
|
||||
mocks.u2f = u2f_mock;
|
||||
U2FRegisterRequestGet.default(req as any, res as any);
|
||||
return U2FRegisterRequestGet.default(req as any, res as any)
|
||||
.then(function() {
|
||||
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||
Assert.deepEqual(res.send.getCall(0).args[0], {
|
||||
error: "Operation failed."
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should return forbidden if identity has not been verified", function () {
|
||||
authSession.identity_check = undefined;
|
||||
return U2FRegisterRequestGet.default(req as any, res as any)
|
||||
.then(function () {
|
||||
assert.equal(403, res.status.getCall(0).args[0]);
|
||||
Assert.equal(403, res.status.getCall(0).args[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -97,7 +97,9 @@ describe("test u2f routes: sign", function () {
|
|||
mocks.u2f = u2f_mock;
|
||||
return U2FSignPost.default(req as any, res as any)
|
||||
.then(function () {
|
||||
Assert.equal(500, res.status.getCall(0).args[0]);
|
||||
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||
Assert.deepEqual(res.send.getCall(0).args[0],
|
||||
{ error: "Operation failed." });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -128,25 +128,18 @@ describe("Private pages of the server must not be accessible without session", f
|
|||
});
|
||||
|
||||
describe("Second factor endpoints must be protected if first factor is not validated", function () {
|
||||
function should_post_and_reply_with(url: string, status_code: number): BluebirdPromise<void> {
|
||||
return requestp.postAsync(url).then(function (response: request.RequestResponse) {
|
||||
Assert.equal(response.statusCode, status_code);
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function should_get_and_reply_with(url: string, status_code: number): BluebirdPromise<void> {
|
||||
return requestp.getAsync(url).then(function (response: request.RequestResponse) {
|
||||
Assert.equal(response.statusCode, status_code);
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function should_post_and_reply_with_401(url: string): BluebirdPromise<void> {
|
||||
return should_post_and_reply_with(url, 401);
|
||||
return requestp.postAsync(url).then(function (response: request.RequestResponse) {
|
||||
Assert.equal(response.statusCode, 401);
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function should_get_and_reply_with_401(url: string): BluebirdPromise<void> {
|
||||
return should_get_and_reply_with(url, 401);
|
||||
return requestp.getAsync(url).then(function (response: request.RequestResponse) {
|
||||
Assert.equal(response.statusCode, 401);
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
it("should block " + Endpoints.SECOND_FACTOR_GET, function () {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
export const AUTHENTICATION_FAILED = "Authentication failed. Please check your credentials.";
|
||||
export const AUTHENTICATION_SUCCEEDED = "Authentication succeeded. You can now access your services.";
|
||||
|
||||
export const AUTHENTICATION_U2F_FAILED = "Authentication failed. Have you already registered your device?";
|
||||
export const AUTHENTICATION_TOTP_FAILED = "Authentication failed. Have you already registered your secret?";
|
||||
|
||||
export const U2F_TRANSACTION_FINISH_FAILED = "U2F validation failed unexpectedly.";
|
||||
export const PLEASE_TOUCH_TOKEN = "Please touch the token on your U2F device.";
|
||||
|
||||
export const REGISTRATION_U2F_FAILED = "Registration of U2F device failed.";
|
||||
|
||||
export const DIFFERENT_PASSWORDS = "The passwords are different.";
|
||||
export const MISSING_PASSWORD = "You must enter your password twice.";
|
||||
export const RESET_PASSWORD_FAILED = "An error occurred during password reset. Your password has not been changed.";
|
||||
|
||||
// Password reset request
|
||||
export const MISSING_USERNAME = "You must provide your username to reset your password.";
|
||||
export const MAIL_SENT = "An email has been sent to you. Follow the link to change your password.";
|
||||
export const MAIL_NOT_SENT = "The email cannot be sent. Please retry in few minutes.";
|
||||
|
||||
export const UNAUTHORIZED_OPERATION = "You are not allowed to perform this operation.";
|
||||
export const OPERATION_FAILED = "Operation failed.";
|
|
@ -12,7 +12,7 @@ Feature: Authentication scenarii
|
|||
When I set field "username" to "john"
|
||||
And I set field "password" to "bad-password"
|
||||
And I click on "Sign in"
|
||||
Then I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
|
||||
Then I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
|
||||
Scenario: User registers TOTP secret and succeeds authentication
|
||||
Given I visit "https://auth.test.local:8080/"
|
||||
|
@ -31,7 +31,7 @@ Feature: Authentication scenarii
|
|||
And I login with user "john" and password "password"
|
||||
And I use "BADTOKEN" as TOTP token
|
||||
And I click on "TOTP"
|
||||
Then I get a notification of type "error" with message "Problem with TOTP validation."
|
||||
Then I get a notification of type "error" with message "Authentication failed. Have you already registered your secret?"
|
||||
|
||||
Scenario: Logout redirects user to redirect URL given in parameter
|
||||
When I visit "https://auth.test.local:8080/logout?redirect=https://home.test.local:8080/"
|
||||
|
|
|
@ -7,16 +7,16 @@ Feature: Authelia regulates authentication to avoid brute force
|
|||
And I set field "username" to "blackhat"
|
||||
And I set field "password" to "bad-password"
|
||||
And I click on "Sign in"
|
||||
And I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
|
||||
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
And I set field "password" to "bad-password"
|
||||
And I click on "Sign in"
|
||||
And I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
|
||||
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
And I set field "password" to "bad-password"
|
||||
And I click on "Sign in"
|
||||
And I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
|
||||
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
When I set field "password" to "password"
|
||||
And I click on "Sign in"
|
||||
Then I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
|
||||
Then I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
|
||||
@needs-test-config
|
||||
@need-registered-user-blackhat
|
||||
|
@ -25,13 +25,13 @@ Feature: Authelia regulates authentication to avoid brute force
|
|||
And I set field "username" to "blackhat"
|
||||
And I set field "password" to "bad-password"
|
||||
And I click on "Sign in"
|
||||
And I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
|
||||
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
And I set field "password" to "bad-password"
|
||||
And I click on "Sign in"
|
||||
And I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
|
||||
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
And I set field "password" to "bad-password"
|
||||
And I click on "Sign in"
|
||||
And I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
|
||||
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
When I wait 6 seconds
|
||||
And I set field "password" to "password"
|
||||
And I click on "Sign in"
|
||||
|
|
|
@ -11,6 +11,12 @@ Feature: User is able to reset his password
|
|||
And I click on "Reset Password"
|
||||
Then I get a notification of type "success" with message "An email has been sent to you. Follow the link to change your password."
|
||||
|
||||
Scenario: Request password for unexisting user should behave like existing user
|
||||
Given I'm on https://auth.test.local:8080/password-reset/request
|
||||
When I set field "username" to "fake_user"
|
||||
And I click on "Reset Password"
|
||||
Then I get a notification of type "success" with message "An email has been sent to you. Follow the link to change your password."
|
||||
|
||||
Scenario: User resets his password
|
||||
Given I'm on https://auth.test.local:8080/password-reset/request
|
||||
And I set field "username" to "james"
|
||||
|
|
|
@ -9,8 +9,8 @@ Feature: Non authenticated users have no access to certain pages
|
|||
| https://auth.test.local:8080/secondfactor | 401 |
|
||||
| https://auth.test.local:8080/verify | 401 |
|
||||
| https://auth.test.local:8080/secondfactor/u2f/identity/start | 401 |
|
||||
| https://auth.test.local:8080/secondfactor/u2f/identity/finish | 403 |
|
||||
| https://auth.test.local:8080/secondfactor/u2f/identity/finish | 401 |
|
||||
| https://auth.test.local:8080/secondfactor/totp/identity/start | 401 |
|
||||
| https://auth.test.local:8080/secondfactor/totp/identity/finish | 403 |
|
||||
| https://auth.test.local:8080/password-reset/identity/start | 403 |
|
||||
| https://auth.test.local:8080/password-reset/identity/finish | 403 |
|
||||
| https://auth.test.local:8080/secondfactor/totp/identity/finish | 401 |
|
||||
| https://auth.test.local:8080/password-reset/identity/start | 401 |
|
||||
| https://auth.test.local:8080/password-reset/identity/finish | 401 |
|
Loading…
Reference in New Issue