From 56fdc40290f77ccfe571328cd84a46eb480def3e Mon Sep 17 00:00:00 2001
From: Clement Michaud <clement.michaud34@gmail.com>
Date: Tue, 10 Oct 2017 23:03:30 +0200
Subject: [PATCH] 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.
---
 Gruntfile.js                                  |  2 +-
 .../lib/firstfactor/FirstFactorValidator.ts   | 11 ++-
 client/src/lib/firstfactor/index.ts           |  9 +-
 .../lib/reset-password/reset-password-form.ts | 17 ++--
 .../reset-password/reset-password-request.ts  | 18 ++--
 client/src/lib/secondfactor/TOTPValidator.ts  | 36 ++++----
 client/src/lib/secondfactor/U2FValidator.ts   | 91 ++++++++++---------
 client/src/lib/secondfactor/index.ts          | 11 +--
 client/src/lib/u2f-register/u2f-register.ts   | 13 ++-
 .../firstfactor/FirstFactorValidator.test.ts  |  2 +-
 .../test/secondfactor/TOTPValidator.test.ts   | 46 +++++-----
 package.json                                  |  6 +-
 server/src/lib/ErrorReplies.ts                | 10 +-
 server/src/lib/FirstFactorValidator.ts        |  4 +-
 server/src/lib/IdentityCheckMiddleware.ts     | 27 +++---
 server/src/lib/routes/FirstFactorBlocker.ts   |  3 +-
 server/src/lib/routes/firstfactor/post.ts     | 12 +--
 .../lib/routes/password-reset/form/post.ts    |  5 +-
 .../src/lib/routes/secondfactor/redirect.ts   |  3 +-
 .../totp/identity/RegistrationHandler.ts      |  5 +-
 .../lib/routes/secondfactor/totp/sign/post.ts |  9 +-
 .../routes/secondfactor/u2f/register/post.ts  |  8 +-
 .../secondfactor/u2f/register_request/get.ts  |  4 +-
 .../lib/routes/secondfactor/u2f/sign/post.ts  |  4 +-
 .../secondfactor/u2f/sign_request/get.ts      |  5 +-
 server/src/lib/routes/verify/get.ts           |  5 +-
 server/src/views/layout/layout.pug            |  2 +-
 server/test/IdentityCheckMiddleware.test.ts   | 30 +++---
 server/test/routes/firstfactor/post.test.ts   | 25 +++--
 .../test/routes/password-reset/post.test.ts   | 18 ++--
 .../secondfactor/u2f/register/post.test.ts    | 28 ++++--
 .../u2f/register_request/get.test.ts          | 21 +++--
 .../routes/secondfactor/u2f/sign/post.test.ts |  4 +-
 server/test/server/PrivatePages.ts            | 25 ++---
 shared/UserMessages.ts                        | 23 +++++
 test/features/authentication.feature          |  4 +-
 test/features/regulation.feature              | 14 +--
 test/features/reset-password.feature          |  6 ++
 test/features/restrictions.feature            |  8 +-
 39 files changed, 325 insertions(+), 249 deletions(-)
 create mode 100644 shared/UserMessages.ts

diff --git a/Gruntfile.js b/Gruntfile.js
index cf7c78a1..713eb3c4 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -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: {
diff --git a/client/src/lib/firstfactor/FirstFactorValidator.ts b/client/src/lib/firstfactor/FirstFactorValidator.ts
index 175f3c2c..60a4d17c 100644
--- a/client/src/lib/firstfactor/FirstFactorValidator.ts
+++ b/client/src/lib/firstfactor/FirstFactorValidator.ts
@@ -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));
       });
   });
 }
diff --git a/client/src/lib/firstfactor/index.ts b/client/src/lib/firstfactor/index.ts
index 91de7e00..0c5f9828 100644
--- a/client/src/lib/firstfactor/index.ts
+++ b/client/src/lib/firstfactor/index.ts
@@ -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);
   });
 }
diff --git a/client/src/lib/reset-password/reset-password-form.ts b/client/src/lib/reset-password/reset-password-form.ts
index 12f60d77..888026f6 100644
--- a/client/src/lib/reset-password/reset-password-form.ts
+++ b/client/src/lib/reset-password/reset-password-form.ts
@@ -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;
   }
diff --git a/client/src/lib/reset-password/reset-password-request.ts b/client/src/lib/reset-password/reset-password-request.ts
index 0685e176..426bad34 100644
--- a/client/src/lib/reset-password/reset-password-request.ts
+++ b/client/src/lib/reset-password/reset-password-request.ts
@@ -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);
   });
 }
diff --git a/client/src/lib/secondfactor/TOTPValidator.ts b/client/src/lib/secondfactor/TOTPValidator.ts
index 824b591a..5d845edc 100644
--- a/client/src/lib/secondfactor/TOTPValidator.ts
+++ b/client/src/lib/secondfactor/TOTPValidator.ts
@@ -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));
+      });
+  });
 }
\ No newline at end of file
diff --git a/client/src/lib/secondfactor/U2FValidator.ts b/client/src/lib/secondfactor/U2FValidator.ts
index 66c7751a..0806e241 100644
--- a/client/src/lib/secondfactor/U2FValidator.ts
+++ b/client/src/lib/secondfactor/U2FValidator.ts
@@ -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);
 }
diff --git a/client/src/lib/secondfactor/index.ts b/client/src/lib/secondfactor/index.ts
index 52875254..c829dea8 100644
--- a/client/src/lib/secondfactor/index.ts
+++ b/client/src/lib/secondfactor/index.ts
@@ -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;
diff --git a/client/src/lib/u2f-register/u2f-register.ts b/client/src/lib/u2f-register/u2f-register.ts
index f895bbf6..f66e3c80 100644
--- a/client/src/lib/u2f-register/u2f-register.ts
+++ b/client/src/lib/u2f-register/u2f-register.ts
@@ -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 () {
diff --git a/client/test/firstfactor/FirstFactorValidator.test.ts b/client/test/firstfactor/FirstFactorValidator.test.ts
index acae7c0d..ac835327 100644
--- a/client/test/firstfactor/FirstFactorValidator.test.ts
+++ b/client/test/firstfactor/FirstFactorValidator.test.ts
@@ -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.");
         });
     });
 });
\ No newline at end of file
diff --git a/client/test/secondfactor/TOTPValidator.test.ts b/client/test/secondfactor/TOTPValidator.test.ts
index e03b0b9a..5952eb61 100644
--- a/client/test/secondfactor/TOTPValidator.test.ts
+++ b/client/test/secondfactor/TOTPValidator.test.ts
@@ -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();
+      });
+  });
 });
\ No newline at end of file
diff --git a/package.json b/package.json
index 5a3e0a06..2393c665 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/server/src/lib/ErrorReplies.ts b/server/src/lib/ErrorReplies.ts
index 6e40e8db..cd6a4599 100644
--- a/server/src/lib/ErrorReplies.ts
+++ b/server/src/lib/ErrorReplies.ts
@@ -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 });
 }
\ No newline at end of file
diff --git a/server/src/lib/FirstFactorValidator.ts b/server/src/lib/FirstFactorValidator.ts
index 678dfde1..045d63f0 100644
--- a/server/src/lib/FirstFactorValidator.ts
+++ b/server/src/lib/FirstFactorValidator.ts
@@ -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();
     });
diff --git a/server/src/lib/IdentityCheckMiddleware.ts b/server/src/lib/IdentityCheckMiddleware.ts
index ffe9292f..362288dc 100644
--- a/server/src/lib/IdentityCheckMiddleware.ts
+++ b/server/src/lib/IdentityCheckMiddleware.ts
@@ -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));
   };
 }
diff --git a/server/src/lib/routes/FirstFactorBlocker.ts b/server/src/lib/routes/FirstFactorBlocker.ts
index 0cab96e9..41b88240 100644
--- a/server/src/lib/routes/FirstFactorBlocker.ts
+++ b/server/src/lib/routes/FirstFactorBlocker.ts
@@ -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));
   };
 }
\ No newline at end of file
diff --git a/server/src/lib/routes/firstfactor/post.ts b/server/src/lib/routes/firstfactor/post.ts
index b7ba0432..086700bb 100644
--- a/server/src/lib/routes/firstfactor/post.ts
+++ b/server/src/lib/routes/firstfactor/post.ts
@@ -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));
 }
diff --git a/server/src/lib/routes/password-reset/form/post.ts b/server/src/lib/routes/password-reset/form/post.ts
index d84c4c86..15dba4be 100644
--- a/server/src/lib/routes/password-reset/form/post.ts
+++ b/server/src/lib/routes/password-reset/form/post.ts
@@ -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));
 }
diff --git a/server/src/lib/routes/secondfactor/redirect.ts b/server/src/lib/routes/secondfactor/redirect.ts
index 4761f2a9..4f258e8a 100644
--- a/server/src/lib/routes/secondfactor/redirect.ts
+++ b/server/src/lib/routes/secondfactor/redirect.ts
@@ -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."));
 }
\ No newline at end of file
diff --git a/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts b/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts
index a395bf13..d289773f 100644
--- a/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts
+++ b/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts
@@ -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 {
diff --git a/server/src/lib/routes/secondfactor/totp/sign/post.ts b/server/src/lib/routes/secondfactor/totp/sign/post.ts
index 134de653..d9b5afd9 100644
--- a/server/src/lib/routes/secondfactor/totp/sign/post.ts
+++ b/server/src/lib/routes/secondfactor/totp/sign/post.ts
@@ -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));
 }
diff --git a/server/src/lib/routes/secondfactor/u2f/register/post.ts b/server/src/lib/routes/secondfactor/u2f/register/post.ts
index 1a64ad64..6d7e9d4b 100644
--- a/server/src/lib/routes/secondfactor/u2f/register/post.ts
+++ b/server/src/lib/routes/secondfactor/u2f/register/post.ts
@@ -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));
 }
diff --git a/server/src/lib/routes/secondfactor/u2f/register_request/get.ts b/server/src/lib/routes/secondfactor/u2f/register_request/get.ts
index 7d1119c3..4c9a148c 100644
--- a/server/src/lib/routes/secondfactor/u2f/register_request/get.ts
+++ b/server/src/lib/routes/secondfactor/u2f/register_request/get.ts
@@ -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));
 }
\ No newline at end of file
diff --git a/server/src/lib/routes/secondfactor/u2f/sign/post.ts b/server/src/lib/routes/secondfactor/u2f/sign/post.ts
index 1df8a184..dfa9c38d 100644
--- a/server/src/lib/routes/secondfactor/u2f/sign/post.ts
+++ b/server/src/lib/routes/secondfactor/u2f/sign/post.ts
@@ -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));
 }
 
diff --git a/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts b/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts
index af0ebf29..bdd40f44 100644
--- a/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts
+++ b/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts
@@ -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));
 }
 
diff --git a/server/src/lib/routes/verify/get.ts b/server/src/lib/routes/verify/get.ts
index fcbc7336..ccf21be0 100644
--- a/server/src/lib/routes/verify/get.ts
+++ b/server/src/lib/routes/verify/get.ts
@@ -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));
 }
 
diff --git a/server/src/views/layout/layout.pug b/server/src/views/layout/layout.pug
index 3f95cb9b..24d96080 100644
--- a/server/src/views/layout/layout.pug
+++ b/server/src/views/layout/layout.pug
@@ -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
diff --git a/server/test/IdentityCheckMiddleware.test.ts b/server/test/IdentityCheckMiddleware.test.ts
index 37388c3e..62416179 100644
--- a/server/test/IdentityCheckMiddleware.test.ts
+++ b/server/test/IdentityCheckMiddleware.test.ts
@@ -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();
         });
     });
diff --git a/server/test/routes/firstfactor/post.test.ts b/server/test/routes/firstfactor/post.test.ts
index 17b3548c..89c1d72e 100644
--- a/server/test/routes/firstfactor/post.test.ts
+++ b/server/test/routes/firstfactor/post.test.ts
@@ -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."
+        });
       });
   });
 });
diff --git a/server/test/routes/password-reset/post.test.ts b/server/test/routes/password-reset/post.test.ts
index 86fda49c..436715ad 100644
--- a/server/test/routes/password-reset/post.test.ts
+++ b/server/test/routes/password-reset/post.test.ts
@@ -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();
         });
     });
diff --git a/server/test/routes/secondfactor/u2f/register/post.test.ts b/server/test/routes/secondfactor/u2f/register/post.test.ts
index 601e7f09..86d0cd01 100644
--- a/server/test/routes/secondfactor/u2f/register/post.test.ts
+++ b/server/test/routes/secondfactor/u2f/register/post.test.ts
@@ -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();
         });
     });
diff --git a/server/test/routes/secondfactor/u2f/register_request/get.test.ts b/server/test/routes/secondfactor/u2f/register_request/get.test.ts
index 7465beb1..168dca30 100644
--- a/server/test/routes/secondfactor/u2f/register_request/get.test.ts
+++ b/server/test/routes/secondfactor/u2f/register_request/get.test.ts
@@ -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]);
         });
     });
   });
diff --git a/server/test/routes/secondfactor/u2f/sign/post.test.ts b/server/test/routes/secondfactor/u2f/sign/post.test.ts
index 80d7394e..aef978bb 100644
--- a/server/test/routes/secondfactor/u2f/sign/post.test.ts
+++ b/server/test/routes/secondfactor/u2f/sign/post.test.ts
@@ -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." });
       });
   });
 });
diff --git a/server/test/server/PrivatePages.ts b/server/test/server/PrivatePages.ts
index 09bdb383..cb2d8372 100644
--- a/server/test/server/PrivatePages.ts
+++ b/server/test/server/PrivatePages.ts
@@ -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 () {
diff --git a/shared/UserMessages.ts b/shared/UserMessages.ts
new file mode 100644
index 00000000..40640b1d
--- /dev/null
+++ b/shared/UserMessages.ts
@@ -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.";
\ No newline at end of file
diff --git a/test/features/authentication.feature b/test/features/authentication.feature
index 69ea8884..11a600fc 100644
--- a/test/features/authentication.feature
+++ b/test/features/authentication.feature
@@ -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/"
diff --git a/test/features/regulation.feature b/test/features/regulation.feature
index f361c2dd..69e5a69b 100644
--- a/test/features/regulation.feature
+++ b/test/features/regulation.feature
@@ -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"
diff --git a/test/features/reset-password.feature b/test/features/reset-password.feature
index c386f28c..ba893f65 100644
--- a/test/features/reset-password.feature
+++ b/test/features/reset-password.feature
@@ -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"
diff --git a/test/features/restrictions.feature b/test/features/restrictions.feature
index c2cf9889..e331482b 100644
--- a/test/features/restrictions.feature
+++ b/test/features/restrictions.feature
@@ -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        |
\ No newline at end of file
+      | 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        |
\ No newline at end of file