Every public endpoints return 200 with harmonized error messages or 401
Now, /verify can return 401 or 403 depending on the user authentication. Every public API endpoints and pages return 200 with error message in JSON body or 401 if the user is not authorized. This policy makes it complicated for an attacker to know what is the source of the failure and hide server-side bugs (not returning 500), bugs being potential threats.pull/140/head
parent
d5035b8704
commit
56fdc40290
|
@ -53,7 +53,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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -66,8 +66,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",
|
||||
|
@ -96,7 +96,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, 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." });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -127,25 +127,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