diff --git a/core/templates/login.php b/core/templates/login.php
index 2198f063dbcc6820e3916d2fa9ec6a76e0796b92..f10a81021801cd5ecee6d4c9b10826dc1c420ca0 100644
--- a/core/templates/login.php
+++ b/core/templates/login.php
@@ -19,6 +19,11 @@ script('core', [
 				<small><?php p($l->t('Please contact your administrator.')); ?></small>
 			</div>
 		<?php endif; ?>
+		<?php foreach($_['messages'] as $message): ?>
+			<div class="warning">
+				<?php p($message); ?><br>
+			</div>
+		<?php endforeach; ?>
 		<p id="message" class="hidden">
 			<img class="float-spinner" alt=""
 				src="<?php p(\OCP\Util::imagePath('core', 'loading-dark.gif'));?>" />
diff --git a/lib/base.php b/lib/base.php
index dbfe0eb2f278b812e6c088b7389e30e9b4e5db75..558be6b570f382b4b0a1d07f55ea7af925bdf342 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -846,19 +846,24 @@ class OC {
 	protected static function handleLogin() {
 		OC_App::loadApps(array('prelogin'));
 		$error = array();
+		$messages = [];
 
-		// auth possible via apache module?
-		if (OC::tryApacheAuth()) {
-			$error[] = 'apacheauthfailed';
-		} // remember was checked after last login
-		elseif (OC::tryRememberLogin()) {
-			$error[] = 'invalidcookie';
-		} // logon via web form
-		elseif (OC::tryFormLogin()) {
-			$error[] = 'invalidpassword';
+		try {
+			// auth possible via apache module?
+			if (OC::tryApacheAuth()) {
+				$error[] = 'apacheauthfailed';
+			} // remember was checked after last login
+			elseif (OC::tryRememberLogin()) {
+				$error[] = 'invalidcookie';
+			} // logon via web form
+			elseif (OC::tryFormLogin()) {
+				$error[] = 'invalidpassword';
+			}
+		} catch (\OC\User\LoginException $e) {
+			$messages[] = $e->getMessage();
 		}
 
-		OC_Util::displayLoginPage(array_unique($error));
+		OC_Util::displayLoginPage(array_unique($error), $messages);
 	}
 
 	/**
diff --git a/lib/private/user/loginexception.php b/lib/private/user/loginexception.php
new file mode 100644
index 0000000000000000000000000000000000000000..571f66bd94592249bd436259e342aa593230dc8e
--- /dev/null
+++ b/lib/private/user/loginexception.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\User;
+
+class LoginException extends \Exception {
+}
diff --git a/lib/private/user/session.php b/lib/private/user/session.php
index 3cd83aae52f70c8e07ded04239a75564ccc7cee6..ffb26776f97dee788dec7184992533eee750c3c5 100644
--- a/lib/private/user/session.php
+++ b/lib/private/user/session.php
@@ -189,6 +189,7 @@ class Session implements IUserSession, Emitter {
 	 * @param string $uid
 	 * @param string $password
 	 * @return boolean|null
+	 * @throws LoginException
 	 */
 	public function login($uid, $password) {
 		$this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
@@ -199,7 +200,11 @@ class Session implements IUserSession, Emitter {
 					$this->setUser($user);
 					$this->setLoginName($uid);
 					$this->manager->emit('\OC\User', 'postLogin', array($user, $password));
-					return true;
+					if ($this->isLoggedIn()) {
+						return true;
+					} else {
+						throw new LoginException('Login canceled by app');
+					}
 				} else {
 					return false;
 				}
diff --git a/lib/private/util.php b/lib/private/util.php
index 3b943f046bfa40c2791c59ddffab75d39bbe63e6..e2b7eed1ba5c9049c1d53c75d3eb4e1af8b75061 100644
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -782,12 +782,14 @@ class OC_Util {
 
 	/**
 	 * @param array $errors
+	 * @param string[] $messages
 	 */
-	public static function displayLoginPage($errors = array()) {
+	public static function displayLoginPage($errors = array(), $messages = []) {
 		$parameters = array();
 		foreach ($errors as $value) {
 			$parameters[$value] = true;
 		}
+		$parameters['messages'] = $messages;
 		if (!empty($_REQUEST['user'])) {
 			$parameters["username"] = $_REQUEST['user'];
 			$parameters['user_autofocus'] = false;