diff --git a/auth/oauth2/facebook_token.php b/auth/oauth2/facebook_token.php
index 1daca05bde24a7711f9191d52f55dfdb5fa4f5e0..b914131878a739a5f0f83468c9b5c84d6428a5e0 100755
--- a/auth/oauth2/facebook_token.php
+++ b/auth/oauth2/facebook_token.php
@@ -80,7 +80,7 @@ $fbUser = $response->getGraphUser();
 
 $typedId = $fbUser["id"];
 
-$user = RAP\UserHandler::findUserByIdentity(RAP\Identity::FACEBOOK, $typedId);
+$user = $userHandler->findUserByIdentity(RAP\Identity::FACEBOOK, $typedId);
 
 if ($user === null) {
     $user = new RAP\User();
@@ -93,8 +93,8 @@ if ($user === null) {
 
     $user->addIdentity($identity);
 
-    RAP\UserHandler::saveUser($user);
+    $userHandler->saveUser($user);
 }
 
-RAP\CallbackHandler::manageLoginRedirect($user);
+$callbackHandler->manageLoginRedirect($user, $session);
 ?>
diff --git a/auth/oauth2/google_token.php b/auth/oauth2/google_token.php
index 59e9c014873c9ec099805eb38846a0b2ef91bdb5..a44c19af72e9d4670705ecc6916f0d86e7b4e7a8 100644
--- a/auth/oauth2/google_token.php
+++ b/auth/oauth2/google_token.php
@@ -74,7 +74,7 @@ if ($client->getAccessToken()) {
 
     $typedId = explode('/', $res->getResourceName())[1];
 
-    $user = RAP\UserHandler::findUserByIdentity(RAP\Identity::GOOGLE, $typedId);
+    $user = $userHandler->findUserByIdentity(RAP\Identity::GOOGLE, $typedId);
 
     if ($user === null) {
         $user = new RAP\User();
@@ -87,10 +87,10 @@ if ($client->getAccessToken()) {
 
         $user->addIdentity($identity);
 
-        RAP\UserHandler::saveUser($user);
+        $userHandler->saveUser($user);
     }
 
-    RAP\CallbackHandler::manageLoginRedirect($user);
+    $callbackHandler->manageLoginRedirect($user, $session);
 
     die();
 } else {
diff --git a/auth/saml2/aai.php b/auth/saml2/aai.php
index 02a728d79af0c585b2c8e5d00ef4a45fb5d5faa0..cb93949c0107ae4b882c20e6ec8ce0bf6cb7ad67 100644
--- a/auth/saml2/aai.php
+++ b/auth/saml2/aai.php
@@ -29,7 +29,7 @@ if (isset($_SERVER['Shib-Session-ID'])) {
 
     $eppn = $_SERVER['eppn'];
 
-    $user = RAP\UserHandler::findUserByIdentity(RAP\Identity::EDU_GAIN, $eppn);
+    $user = $userHandler->findUserByIdentity(RAP\Identity::EDU_GAIN, $eppn);
 
     if ($user === null) {
         $user = new RAP\User();
@@ -39,14 +39,15 @@ if (isset($_SERVER['Shib-Session-ID'])) {
         $identity->name = $_SERVER['givenName'];
         $identity->surname = $_SERVER['sn'];
         $identity->typedId = $eppn;
+        $identity->eppn = $eppn;
         //$_SERVER['Shib-Identity-Provider']
 
         $user->addIdentity($identity);
 
-        RAP\UserHandler::saveUser($user);
+        $userHandler->saveUser($user);
     }
 
-    RAP\CallbackHandler::manageLoginRedirect($user);
+    $callbackHandler->manageLoginRedirect($user, $session);
 } else {
     http_response_code(500);
     die("Shib-Session-ID not found!");
diff --git a/auth/x509/certlogin.php b/auth/x509/certlogin.php
index e2319ea6ad1dc425e51cd6f74727526668d716e1..e0ce9ea501a7b91384bccabc39bedf00db736ea0 100644
--- a/auth/x509/certlogin.php
+++ b/auth/x509/certlogin.php
@@ -28,7 +28,7 @@ startSession();
 
 function saveUserFromX509Data($x509Data) {
 
-    global $session;
+    global $session, $userHandler;
 
     $user = new RAP\User();
 
@@ -41,7 +41,7 @@ function saveUserFromX509Data($x509Data) {
 
     $user->addIdentity($identity);
 
-    RAP\UserHandler::saveUser($user);
+    $userHandler->saveUser($user);
 
     $session->x509DataToRegister = null;
 
@@ -58,7 +58,7 @@ if ($session->x509DataToRegister !== null && $session->x509DataToRegister->name
 
         $x509Data = RAP\X509Data::parse($_SERVER);
 
-        $user = RAP\UserHandler::findUserByIdentity(RAP\Identity::X509, $x509Data->serialNumber);
+        $user = $userHandler->findUserByIdentity(RAP\Identity::X509, $x509Data->serialNumber);
 
         if ($user === null) {
 
@@ -77,4 +77,4 @@ if ($session->x509DataToRegister !== null && $session->x509DataToRegister->name
     }
 }
 
-RAP\CallbackHandler::manageLoginRedirect($user);
+$callbackHandler->manageLoginRedirect($user, $session);
diff --git a/classes/CallbackHandler.php b/classes/CallbackHandler.php
index 85d33e978b8c3d128ab6fe642a5751dedee85f9a..a569844e481c03492c11043410941c4e402b3aa9 100644
--- a/classes/CallbackHandler.php
+++ b/classes/CallbackHandler.php
@@ -26,6 +26,16 @@ namespace RAP;
 
 class CallbackHandler {
 
+    private $dao;
+    private $basePath;
+    private $callbacks;
+
+    public function __construct(DAO $dao, $basePath, $callbacks) {
+        $this->dao = $dao;
+        $this->basePath = $basePath;
+        $this->callbacks = $callbacks;
+    }
+
     /**
      * returns null if the callback URL is not listed in configuration file.
      */
@@ -35,9 +45,7 @@ class CallbackHandler {
             return "Account Management";
         }
 
-        global $CALLBACKS;
-
-        foreach ($CALLBACKS as $callback) {
+        foreach ($this->callbacks as $callback) {
             if ($callback['url'] === $callbackURL) {
                 return $callback['title'];
             }
@@ -46,21 +54,19 @@ class CallbackHandler {
         throw new \Exception("Unauthorized callback URL");
     }
 
-    public static function manageLoginRedirect($user) {
-
-        global $BASE_PATH, $session, $log;
+    public function manageLoginRedirect($user, SessionData $session) {
 
         if ($session->getCallbackURL() !== null) {
             // External login using token
             $token = Util::createNewToken();
-            DAO::get()->createLoginToken($token, $user->id);
+            $this->dao->createLoginToken($token, $user->id);
             header('Location: ' . $session->getCallbackURL() . '?token=' . $token);
         } else {
             // Login in session
             $session->user = $user;
             $session->save();
             // Return to index
-            header('Location: ' . $BASE_PATH);
+            header('Location: ' . $this->basePath);
         }
     }
 
diff --git a/classes/DAO.php b/classes/DAO.php
index 888f66c71e0a754c3f92a9f7eead4a7b58b1679c..9a28d92f582d5dd429beb67264522c8941364b2b 100644
--- a/classes/DAO.php
+++ b/classes/DAO.php
@@ -24,58 +24,46 @@
 
 namespace RAP;
 
-abstract class DAO {
+interface DAO {
 
-    public abstract function getDBHandler();
+    function getDBHandler();
 
-    public abstract function createLoginToken($token, $userId);
+    function createLoginToken($token, $userId);
 
-    public abstract function findLoginToken($token);
+    function findLoginToken($token);
 
-    public abstract function deleteLoginToken($token);
+    function deleteLoginToken($token);
 
     /**
      * Return the new identity ID.
      */
-    public abstract function insertIdentity(Identity $identity, $userId);
+    function insertIdentity(Identity $identity, $userId);
 
     /**
      * Return the new user ID.
      */
-    public abstract function createUser();
+    function createUser();
 
-    public abstract function findUserById($userId);
+    function findUserById($userId);
+
+    function setPrimaryIdentity($userId, $identityId);
 
     /**
      * Return a User object, null if nothing was found.
      * @param type $type Identity type (EDU_GAIN, X509, GOOGLE, ...)
      * @param type $identifier value used to search the identity in the database
      */
-    public abstract function findUserByIdentity($type, $identifier);
-
-    public abstract function searchUser($searchText);
-
-    public abstract function addEmailToUser($email, $userId);
-
-    public abstract function createJoinRequest($token, $applicantUserId, $targetUserId);
+    function findUserByIdentity($type, $identifier);
 
-    public abstract function findJoinRequest($token);
+    function searchUser($searchText);
 
-    public abstract function deleteUser($userId);
+    function createJoinRequest($token, $applicantUserId, $targetUserId);
 
-    public abstract function joinUsers($userId1, $userId2);
-    
-    public abstract function deleteJoinRequest($token);
+    function findJoinRequest($token);
 
-    public static function get() {
-        global $DATABASE;
+    function deleteUser($userId);
 
-        switch ($DATABASE['dbtype']) {
-            case 'MySQL':
-                return new MySQLDAO($DATABASE);
-            default:
-                throw new \Exception($DATABASE['dbtype'] . ' not supported yet');
-        }
-    }
+    function joinUsers($userId1, $userId2);
 
+    function deleteJoinRequest($token);
 }
diff --git a/classes/GrouperClient.php b/classes/GrouperClient.php
index 2bef66b38c7c04ee42eec2b85b52f6809dd33e8e..fb8927bfec3729fd0a6c25c1940544f2c9a07cff 100644
--- a/classes/GrouperClient.php
+++ b/classes/GrouperClient.php
@@ -71,9 +71,11 @@ class GrouperClient {
         if ($this->isSuccess($response)) {
             if (count($response->return->results) === 1) {
                 $groups = [];
-                foreach ($response->return->results[0]->wsGroups as $group) {
-                    if (!$this->startsWith($group->name, 'etc:')) {
-                        array_push($groups, $group->name);
+                if ($response->return->results[0]->wsGroups !== null) {
+                    foreach ($response->return->results[0]->wsGroups as $group) {
+                        if (!$this->startsWith($group->name, 'etc:')) {
+                            array_push($groups, $group->name);
+                        }
                     }
                 }
                 return $groups;
diff --git a/classes/Identity.php b/classes/Identity.php
index e731aaba914c59c2300944b24c0af8778cc59596..fc82a1d4fd25292f69bf226c9cac978e909ffcf1 100644
--- a/classes/Identity.php
+++ b/classes/Identity.php
@@ -76,6 +76,11 @@ class Identity {
      */
     public $eppn;
 
+    /**
+     * True if this has been selected as the primary identity.
+     */
+    public $primary;
+
     public function __construct($userType) {
         $isAllowedType = false;
         foreach (Identity::$ALLOWED_TYPES as $type) {
@@ -89,6 +94,8 @@ class Identity {
         }
 
         $this->type = $userType;
+
+        $this->primary = false;
     }
 
 }
diff --git a/classes/MySQLDAO.php b/classes/MySQLDAO.php
index d6343b2bcba89d101b566fb8dd0a44cb4a8b632d..536882d47c411dcdc51e81a8953d0cfb19ecaa0f 100644
--- a/classes/MySQLDAO.php
+++ b/classes/MySQLDAO.php
@@ -26,12 +26,17 @@ namespace RAP;
 
 use PDO;
 
-class MySQLDAO extends DAO {
+class MySQLDAO implements DAO {
+
+    private $config;
+
+    public function __construct($config) {
+        $this->config = $config;
+    }
 
     public function getDBHandler() {
-        global $DATABASE;
-        $connectionString = "mysql:host=" . $DATABASE['hostname'] . ";dbname=" . $DATABASE['dbname'];
-        $dbh = new PDO($connectionString, $DATABASE['username'], $DATABASE['password']);
+        $connectionString = "mysql:host=" . $this->config['hostname'] . ";dbname=" . $this->config['dbname'];
+        $dbh = new PDO($connectionString, $this->config['username'], $this->config['password']);
         // For transaction errors (see https://stackoverflow.com/a/9659366/771431)
         $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
         return $dbh;
@@ -39,8 +44,6 @@ class MySQLDAO extends DAO {
 
     public function createLoginToken($token, $userId) {
 
-        global $log;
-
         $dbh = $this->getDBHandler();
         $stmt = $dbh->prepare("INSERT INTO login_token (token, user_id) VALUES(:token, :user_id)");
 
@@ -52,7 +55,7 @@ class MySQLDAO extends DAO {
         if ($stmt->execute($params)) {
             return $token;
         } else {
-            $log->error($stmt->errorInfo()[2]);
+            error_log($stmt->errorInfo()[2]);
             throw new \Exception("SQL error while storing user token");
         }
     }
@@ -118,6 +121,7 @@ class MySQLDAO extends DAO {
         $identity = new Identity($row['type']);
 
         $identity->id = $row['id'];
+        $identity->primary = $row['primary'];
         $identity->typedId = $row['typed_id'];
         $identity->email = $row['email'];
         $identity->name = $row['name'];
@@ -136,8 +140,11 @@ class MySQLDAO extends DAO {
 
         $dbh = $this->getDBHandler();
 
-        $stmt = $dbh->prepare("SELECT `id`, `type`, `typed_id`, `email`, `name`, `surname`, `institution`, `eppn`"
-                . " FROM identity WHERE user_id = :user_id");
+        $stmt = $dbh->prepare("SELECT (u.`primary_identity` = i.`id`) AS `primary`,"
+                . " i.`id`, `type`, `typed_id`, `email`, `name`, `surname`, `institution`, `eppn`"
+                . " FROM identity i"
+                . " JOIN `user` u on u.id = i.user_id"
+                . " WHERE i.user_id = :user_id");
 
         $stmt->bindParam(':user_id', $userId);
         $stmt->execute();
@@ -155,16 +162,19 @@ class MySQLDAO extends DAO {
             $user->addIdentity($identity);
         }
 
-        $stmtMail = $dbh->prepare("SELECT `email` FROM `additional_email` WHERE `user_id` = :user_id");
-        $stmtMail->bindParam(':user_id', $userId);
-        $stmtMail->execute();
-        foreach ($stmtMail->fetchAll() as $row) {
-            $user->addAdditionalEmail($row['email']);
-        }
-
         return $user;
     }
 
+    public function setPrimaryIdentity($userId, $identityId) {
+
+        $dbh = $this->getDBHandler();
+
+        $stmt = $dbh->prepare("UPDATE `user` SET `primary_identity` = :identity_id WHERE `id` = :user_id");
+        $stmt->bindParam(':identity_id', $identityId);
+        $stmt->bindParam(':user_id', $userId);
+        $stmt->execute();
+    }
+
     public function findUserByIdentity($type, $identifier) {
 
         $dbh = $this->getDBHandler();
@@ -194,10 +204,11 @@ class MySQLDAO extends DAO {
 
         $dbh = $this->getDBHandler();
 
-        // TODO: Add additional email search...
-
-        $query = "SELECT `user_id`, `id`, `type`, `typed_id`, `email`, `name`, `surname`, `institution`, `eppn`"
-                . " FROM identity WHERE `email` LIKE :email OR `name` LIKE :name OR `surname` LIKE :surname";
+        $query = "SELECT `user_id`, (u.`primary_identity` = i.`id`) AS `primary`,"
+                . " i.`id`, `type`, `typed_id`, `email`, `name`, `surname`, `institution`, `eppn`"
+                . " FROM identity i"
+                . " JOIN `user` u on u.id = i.user_id"
+                . " WHERE `email` LIKE :email OR `name` LIKE :name OR `surname` LIKE :surname";
 
         $stmt = $dbh->prepare($query);
 
@@ -233,17 +244,6 @@ class MySQLDAO extends DAO {
         return $users;
     }
 
-    public function addEmailToUser($email, $userId) {
-
-        $dbh = $this->getDBHandler();
-
-        $stmt = $dbh->prepare("INSERT INTO `additional_email`(`user_id`, `email`) VALUES(:user_id, :email)");
-        $stmt->bindParam(':user_id', $userId);
-        $stmt->bindParam(':email', $email);
-
-        $stmt->execute();
-    }
-
     public function createJoinRequest($token, $applicantUserId, $targetUserId) {
 
         $dbh = $this->getDBHandler();
@@ -298,12 +298,6 @@ class MySQLDAO extends DAO {
             $stmt1->bindParam(':id2', $userId2);
             $stmt1->execute();
 
-            // Moving additional email addresses from user2 to user1
-            $stmt2 = $dbh->prepare("UPDATE `additional_email` SET `user_id` = :id1 WHERE `user_id` = :id2");
-            $stmt2->bindParam(':id1', $userId1);
-            $stmt2->bindParam(':id2', $userId2);
-            $stmt2->execute();
-
             // Deleting user2 join requests
             $stmt3 = $dbh->prepare("DELETE FROM `join_request` WHERE `target_user_id` = :id2");
             $stmt3->bindParam(':id2', $userId2);
diff --git a/classes/SessionData.php b/classes/SessionData.php
index cc578f21031ade8cfaf4e26ee6242c5942830fe6..8dd9f52aef31003d8c71f55fb8646e30efcfceaa 100644
--- a/classes/SessionData.php
+++ b/classes/SessionData.php
@@ -26,28 +26,33 @@ namespace RAP;
 
 class SessionData {
 
+    private $dao;
     private $callbackURL;
     private $callbackTitle;
     public $user;
     public $userSearchResults;
     public $x509DataToRegister;
 
+    public function __construct(DAO $dao) {
+        $this->dao = $dao;
+    }
+
     public function save() {
         $_SESSION['SessionData'] = $this;
     }
 
-    public static function get() {
+    public static function get(DAO $dao) {
 
         if (!isset($_SESSION['SessionData'])) {
-            $session = new SessionData();
+            $session = new SessionData($dao);
             $session->save();
         }
         return $_SESSION['SessionData'];
     }
 
-    public function setCallbackURL($callbackURL) {
+    public function setCallbackURL(CallbackHandler $callbackHandler, $callbackURL) {
         $this->callbackURL = $callbackURL;
-        $this->callbackTitle = CallbackHandler::getCallbackTitle($callbackURL);
+        $this->callbackTitle = $callbackHandler->getCallbackTitle($callbackURL);
         $this->save();
     }
 
@@ -60,7 +65,7 @@ class SessionData {
     }
 
     public function searchUser($searchText) {
-        $users = DAO::get()->searchUser($searchText);
+        $users = $this->dao->searchUser($searchText);
 
         $this->userSearchResults = [];
         foreach ($users as $user) {
@@ -74,4 +79,10 @@ class SessionData {
         $this->save();
     }
 
+    public function updatePrimaryIdentity($identityId) {
+        foreach ($this->user->identities as $identity) {
+            $identity->primary = ($identity->id === $identityId);
+        }
+    }
+
 }
diff --git a/classes/User.php b/classes/User.php
index 1d7a6cb9b9250637224eb8c4d3b4a357b38d7cef..e82db49d2f42c4a707db4eee35aa32fbbf02f82f 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -28,22 +28,22 @@ class User {
 
     public $id;
     public $identities;
-    public $additionalEmailAddresses;
 
     public function __construct() {
         $this->identities = [];
-        $this->additionalEmailAddresses = [];
     }
 
     public function addIdentity(Identity $identity) {
         array_push($this->identities, $identity);
     }
 
-    public function addAdditionalEmail($email) {
-        array_push($this->additionalEmailAddresses, $email);
-    }
-
     public function getPrimaryEmail() {
-        return $this->identities[0]->email;
+        foreach ($this->identities as $identity) {
+            if ($identity->primary) {
+                return $identity->email;
+            }
+        }
+        throw new \Exception("No primary identity defined for user " . $this->id);
     }
+
 }
diff --git a/classes/UserHandler.php b/classes/UserHandler.php
index 58c046248fbd838b53953d33187f3237f5d36714..47824bbe0810e965cdcc5c25979e03998ccba373 100644
--- a/classes/UserHandler.php
+++ b/classes/UserHandler.php
@@ -26,45 +26,51 @@ namespace RAP;
 
 class UserHandler {
 
-    public static function saveUser(User $user) {
+    private $dao;
+    private $grouperConfig;
 
-        $dao = DAO::get();
+    public function __construct(DAO $dao, $grouperConfig) {
+        $this->dao = $dao;
+        $this->grouperConfig = $grouperConfig;
+    }
+
+    public function saveUser(User $user) {
 
+        $primarySpecified = true;
+
+        // If new user
         if ($user->id === null) {
-            $user->id = $dao->createUser();
+            $primarySpecified = false;
+            $user->id = $this->dao->createUser();
         }
 
         foreach ($user->identities as $identity) {
             if ($identity->id === null) {
-                $identity->id = $dao->insertIdentity($identity, $user->id);
-            }
-        }
-
-        foreach ($user->additionalEmailAddresses as $email) {
-            if (!in_array($email, $user->additionalEmailAddresses)) {
-                
+                $identity->id = $this->dao->insertIdentity($identity, $user->id);
+                if (!$primarySpecified) {
+                    $this->dao->setPrimaryIdentity($user->id, $identity->id);
+                    $identity->primary = true;
+                }
             }
         }
     }
 
-    public static function findUserByIdentity($type, $identifier) {
+    public function findUserByIdentity($type, $identifier) {
 
-        return DAO::get()->findUserByIdentity($type, $identifier);
+        return $this->dao->findUserByIdentity($type, $identifier);
     }
 
-    public static function joinUsers($userId1, $userId2) {
-
-        global $GROUPER;
+    public function joinUsers($userId1, $userId2) {
 
-        if (isset($GROUPER)) {
-            $gc = new GrouperClient($GROUPER);
+        if ($this->grouperConfig !== null) {
+            $gc = new GrouperClient($this->grouperConfig);
 
             $groupsToMove = $gc->getSubjectGroups('RAP:' . $userId2);
             $gc->addMemberships('RAP:' . $userId1, $groupsToMove);
             $gc->removeMemberships('RAP:' . $userId2, $groupsToMove);
         }
 
-        DAO::get()->joinUsers($userId1, $userId2);
+        $this->dao->joinUsers($userId1, $userId2);
     }
 
 }
diff --git a/classes/X509Data.php b/classes/X509Data.php
index c1bb523d9c1ded4aa10b7c1cef60492efd0cbdcc..3c774111bd090605597f182aa384b3653eaf489e 100644
--- a/classes/X509Data.php
+++ b/classes/X509Data.php
@@ -128,11 +128,7 @@ class X509Data {
         $parsedData = new X509Data();
 
         if (isset($server['SSL_CLIENT_CERT'])) {
-            $parsedData->parseUsingOpenSSL(['SSL_CLIENT_CERT']);
-            // If all mandatory information has been parsed return the object
-            if ($parsedData->email !== null && $parsedData->fullName !== null && $parsedData->serialNumber !== null) {
-                return $parsedData;
-            }
+            $parsedData->parseUsingOpenSSL($server['SSL_CLIENT_CERT']);
         }
 
         if ($parsedData->fullName === null) {
@@ -158,7 +154,6 @@ class X509Data {
                 // Here a hex->dec conversion is performed, in order to store the
                 // serial number in a consistent format:
                 $hexSerial = $server['SSL_CLIENT_M_SERIAL'];
-                // TODO
                 $parsedData->serialNumber = X509Data::bchexdec($hexSerial);
             } else {
                 throw new Exception("Unable to retrieve serial number from certificate");
diff --git a/css/animation.css b/css/animation.css
new file mode 100644
index 0000000000000000000000000000000000000000..ac5a9562fbd637637048f00b8a8eb86e6dbb4f88
--- /dev/null
+++ b/css/animation.css
@@ -0,0 +1,85 @@
+/*
+   Animation example, for spinners
+*/
+.animate-spin {
+  -moz-animation: spin 2s infinite linear;
+  -o-animation: spin 2s infinite linear;
+  -webkit-animation: spin 2s infinite linear;
+  animation: spin 2s infinite linear;
+  display: inline-block;
+}
+@-moz-keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@-webkit-keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@-o-keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@-ms-keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@keyframes spin {
+  0% {
+    -moz-transform: rotate(0deg);
+    -o-transform: rotate(0deg);
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+
+  100% {
+    -moz-transform: rotate(359deg);
+    -o-transform: rotate(359deg);
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
diff --git a/css/style.css b/css/style.css
index 9affc9a218da8eb85aadc4e0a952242850cdf9e6..cc7f30afc9cf325a7d089d2eb0e4046b83d27698 100644
--- a/css/style.css
+++ b/css/style.css
@@ -3,6 +3,30 @@ body {
     padding-bottom: 150px;
 }
 
+.waiting {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    right: 0;
+    left: 0;
+    background-color: rgba(255, 255, 255, 0.5);
+    z-index: 100000;
+    font-size: 40px;
+    text-align: center;
+}
+
+.waiting .icon-wrapper {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    display: table;
+}
+
+.waiting .glyphicon {
+    display: table-cell;
+    vertical-align: middle;
+}
+
 @keyframes pulse {
     from {
         transform: scale(1, 1);
@@ -141,3 +165,13 @@ body {
     font-size: 30px;
 }
 
+.primary-identity-icon {
+    font-size: 17px;
+    color: #4ea64e;
+}
+.primary-identity-icon a {
+    color: #ddd;
+}
+.primary-identity-icon a:hover {
+    color: #888;
+}
\ No newline at end of file
diff --git a/include/footer.php b/include/footer.php
index 0de7f71286b14074892fc0ca7fe0144ed099a376..5d0b79f247b35a8184b75b34a6afb30da2e3b2a1 100644
--- a/include/footer.php
+++ b/include/footer.php
@@ -1,4 +1,9 @@
 </div>
+<div class="waiting hide">
+    <span class="icon-wrapper">
+        <span class="glyphicon glyphicon-refresh animate-spin"></span>
+    </span>
+</div>
 <footer id="main-footer-wrapper" class="text-center">
     <p id="footer-credits">This software has been adapted by the IA2 team from the Remote Authentication Portal written by Franco Tinarelli at INAF-IRA.</p>
     <div id="main-footer">
diff --git a/include/front-controller.php b/include/front-controller.php
index 8c334d7cbe48fe26f3a22f3f80287f4d8617a56a..f1996e20b225a4733acf0a7693ede42d2ed0d450 100644
--- a/include/front-controller.php
+++ b/include/front-controller.php
@@ -8,10 +8,10 @@
 //
 
 function setCallback() {
-    global $session;
+    global $session, $callbackHandler;
 
     $callback = Flight::request()->data['callback'];
-    $session->setCallbackURL(isset($callback) ? $callback : null);
+    $session->setCallbackURL($callbackHandler, isset($callback) ? $callback : null);
 }
 
 Flight::route('/', function() {
@@ -56,7 +56,7 @@ Flight::route('GET /confirm-join', function() {
         die("Token not found");
     }
 
-    $dao = RAP\DAO::get();
+    global $dao;
 
     $userIds = $dao->findJoinRequest($token);
     if ($userIds === null) {
@@ -75,6 +75,8 @@ Flight::route('GET /confirm-join', function() {
 
 Flight::route('POST /confirm-join', function() {
 
+    global $dao, $userHandler;
+
     $token = Flight::request()->data['token'];
 
     if ($token === null) {
@@ -82,15 +84,13 @@ Flight::route('POST /confirm-join', function() {
         die("Token not found");
     }
 
-    $dao = RAP\DAO::get();
-
     $userIds = $dao->findJoinRequest($token);
     if ($userIds === null) {
         http_response_code(422);
         die("Invalid token");
     }
 
-    RAP\UserHandler::joinUsers($userIds[0], $userIds[1]);
+    $userHandler->joinUsers($userIds[0], $userIds[1]);
     $dao->deleteJoinRequest($token);
 
     // Force user to relogin to see changes to him/her identities
@@ -120,7 +120,7 @@ Flight::route('GET /x509-name-surname', function() {
 Flight::route('POST /submit-x509-name', function() {
 
     $selectedNameIndex = Flight::request()->data['selected-name'];
-    
+
     startSession();
     global $session, $BASE_PATH;
 
diff --git a/include/gui-backend.php b/include/gui-backend.php
index 2506bbbb735495d2fe3c157cf97f72900900863d..0d1b28115e83564a3292c5c89f05cd3e2d5238bc 100644
--- a/include/gui-backend.php
+++ b/include/gui-backend.php
@@ -35,15 +35,31 @@ Flight::route('GET /user', function() {
 Flight::route('POST /join', function() {
 
     checkSession();
-    global $session;
+    global $session, $dao;
 
     $selectedUserIndex = Flight::request()->data['selectedUserIndex'];
     $selectedSearchResult = $session->userSearchResults[$selectedUserIndex];
     $targetUserId = $selectedSearchResult->getUser()->id;
 
     $token = RAP\Util::createNewToken();
-    RAP\DAO::get()->createJoinRequest($token, $session->user->id, $targetUserId);
+    $dao->createJoinRequest($token, $session->user->id, $targetUserId);
     RAP\MailSender::sendJoinEmail($selectedSearchResult->getUser(), $session->user);
 
     echo $selectedSearchResult->userDisplayText;
 });
+
+
+Flight::route('POST /primary-identity', function() {
+
+    checkSession();
+    global $session, $dao;
+
+    $identityId = Flight::request()->data['id'];
+
+    $dao->setPrimaryIdentity($session->user->id, $identityId);
+    $session->updatePrimaryIdentity($identityId);
+    
+    // Following variable is used to render user-data
+    $user = $session->user;
+    include 'user-data.php';
+});
diff --git a/include/header.php b/include/header.php
index 7413c9aa95ee9d648dadfdede27e9df3d6782e3c..45b48d5e4444515ef4a3531bde4439cc76872359 100644
--- a/include/header.php
+++ b/include/header.php
@@ -8,6 +8,8 @@
         <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
         <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
         <link rel="stylesheet" href="css/style.css" />
+        <link rel="stylesheet" href="css/animation.css" />
+        <script src="js/script.js"></script>
     </head>
     <body>
         <header id="main-header">
diff --git a/include/init.php b/include/init.php
index 086d512461d038c60f952177a096019f21803651..dccbb58e6b4d7750e32089660ae98e95ba800940 100644
--- a/include/init.php
+++ b/include/init.php
@@ -45,8 +45,19 @@ date_default_timezone_set("Europe/Rome");
 $log = new Monolog\Logger('mainLogger');
 $log->pushHandler(new Monolog\Handler\StreamHandler($LOG_PATH, $LOG_LEVEL));
 
+switch ($DATABASE['dbtype']) {
+    case 'MySQL':
+        $dao = new RAP\MySQLDAO($DATABASE);
+        break;
+    default:
+        throw new Exception($DATABASE['dbtype'] . ' not supported yet');
+}
+
+$callbackHandler = new RAP\CallbackHandler($dao, $BASE_PATH, $CALLBACKS);
+$userHandler = new RAP\UserHandler($dao, $GROUPER);
+
 function startSession() {
     session_start();
-    global $session;
-    $session = RAP\SessionData::get();
+    global $session, $dao;
+    $session = RAP\SessionData::get($dao);
 }
diff --git a/include/rest-web-service.php b/include/rest-web-service.php
index 2eca4f1a0dc0194785daa851b242d21475030ab3..27aca4c81db10b482bead590619a21964df4c798 100644
--- a/include/rest-web-service.php
+++ b/include/rest-web-service.php
@@ -8,15 +8,17 @@ $WS_PREFIX = '/ws';
 
 Flight::route('GET ' . $WS_PREFIX . '/user-info', function() {
 
+    global $dao;
+
     $token = Flight::request()->query['token'];
-    $userData = RAP\DAO::get()->findLoginToken($token);
+    $userData = $dao->findLoginToken($token);
 
     if (is_null($userData)) {
         http_response_code(404);
         die("Token not found");
     }
 
-    RAP\DAO::get()->deleteLoginToken($token);
+    $dao->deleteLoginToken($token);
 
     header('Content-Type: text/plain');
     echo $userData;
@@ -24,7 +26,9 @@ Flight::route('GET ' . $WS_PREFIX . '/user-info', function() {
 
 Flight::route('GET ' . $WS_PREFIX . '/user/@userId', function($userId) {
 
-    $user = RAP\DAO::get()->findUserById($userId);
+    global $dao;
+
+    $user = $dao->findUserById($userId);
     if ($user !== null) {
         header('Content-Type: application/json');
         echo json_encode($user);
@@ -36,8 +40,10 @@ Flight::route('GET ' . $WS_PREFIX . '/user/@userId', function($userId) {
 
 Flight::route('GET ' . $WS_PREFIX . '/user', function() {
 
+    global $dao;
+    
     $searchText = Flight::request()->query['search'];
-    $users = RAP\DAO::get()->searchUser($searchText);
+    $users = $dao->searchUser($searchText);
     echo json_encode($users);
 });
 
diff --git a/include/user-data.php b/include/user-data.php
index 755cfcd9acfa5a483f55604cdd0af9041263a6fa..92219036c1c580bd6daa1160edd0bbce14a5a806 100644
--- a/include/user-data.php
+++ b/include/user-data.php
@@ -1,6 +1,19 @@
 <?php foreach ($user->identities as $identity) { ?>
     <dl class="dl-horizontal">
-        <dt>Type</dt>
+        <dt>
+            <?php if ($identity->primary) { ?>
+                <span class="primary-identity-icon" data-toggle="tooltip" data-placement="left" title="This is your primary identity. You will receive email messages on the address related to this identity.">
+                    <span class="glyphicon glyphicon-star"></span>
+                </span>
+            <?php } else { ?>
+                <span class="primary-identity-icon" data-toggle="tooltip" data-placement="left" title="Click on this icon to set this as the primary identity">
+                    <a href="#" onclick="setPrimaryIdentity(<?php echo $identity->id; ?>);">
+                        <span class="glyphicon glyphicon-star-empty"></span>
+                    </a>
+                </span>
+            <?php } ?>
+            Type
+        </dt>
         <dd><?php echo $identity->type; ?></dd>
         <dt>E-mail</dt>
         <dd><?php echo $identity->email; ?></dd>
@@ -8,10 +21,6 @@
             <dt>EduPersonPrincipalName</dt>
             <dd><?php echo $identity->eppn; ?></dd>
         <?php } ?>
-        <?php if ($identity->username !== null) { ?>
-            <dt>Username</dt>
-            <dd><?php echo $identity->username; ?></dd>
-        <?php } ?>
         <?php if ($identity->name !== null) { ?>
             <dt>Name</dt>
             <dd><?php echo $identity->name; ?></dd>
diff --git a/js/index.js b/js/index.js
index bc1a24912f0b0cf0963f036dafa5204d193bf5ac..7ca705780f22db0aa06120a4857979892095b30f 100644
--- a/js/index.js
+++ b/js/index.js
@@ -34,6 +34,20 @@
         }
     }
 
+    window.setPrimaryIdentity = function (identityId) {
+        $.post('primary-identity', {
+            id: identityId
+        }, function (response) {
+            $('#panel-identities .panel-body').html(response);
+            // restore tooltips
+            loadTooltips();
+        });
+    };
+
+    function loadTooltips() {
+        $('.primary-identity-icon').tooltip();
+    }
+
     // When the document is loaded
     $(document).ready(function () {
 
@@ -52,6 +66,8 @@
         $(document).on('click', '#info-message-alert .close', function () {
             $('#info-message-alert').addClass('hide');
         });
+
+        loadTooltips();
     });
 
 })(jQuery);
\ No newline at end of file
diff --git a/js/script.js b/js/script.js
new file mode 100644
index 0000000000000000000000000000000000000000..744088f5a496bb7552150503c5872c39dcbc71e8
--- /dev/null
+++ b/js/script.js
@@ -0,0 +1,14 @@
+(function ($) {
+
+    // Loading/waiting animation
+    window.showWaiting = function () {
+        $('.waiting').removeClass('hide');
+    };
+    window.hideWaiting = function () {
+        $('.waiting').addClass('hide');
+    };
+
+    $(document).ajaxStart(showWaiting);
+    $(document).ajaxStop(hideWaiting);
+
+})(jQuery);
\ No newline at end of file
diff --git a/sql/setup-database.sql b/sql/setup-database.sql
index 60bdc47858125b2832c28a9938c56e82ca392f96..8bb38e087f120b4dd5720fbe035cdda090eb01bb 100644
--- a/sql/setup-database.sql
+++ b/sql/setup-database.sql
@@ -1,5 +1,6 @@
 CREATE TABLE `user` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `primary_identity` bigint(20) DEFAULT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
@@ -17,13 +18,9 @@ CREATE TABLE `identity` (
   FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-CREATE TABLE `additional_email` (
-  `id` bigint(20) NOT NULL AUTO_INCREMENT,
-  `user_id` bigint(20) NOT NULL,
-  `email` varchar(255) NOT NULL,
-  PRIMARY KEY (`id`),
-  FOREIGN KEY (`user_id`) REFERENCES `user`(`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+SET FOREIGN_KEY_CHECKS=0;
+ALTER TABLE `user` ADD FOREIGN KEY (`primary_identity`) REFERENCES `identity`(`id`);
+SET FOREIGN_KEY_CHECKS=1;
 
 CREATE TABLE `login_token` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT,
diff --git a/views/confirm-join.php b/views/confirm-join.php
index 60ef13782a4f7d2a4dbec215895f46f8ee2a15e3..aafd5fda9fdbd7f54655209097d6796ffdb2f783 100644
--- a/views/confirm-join.php
+++ b/views/confirm-join.php
@@ -35,7 +35,7 @@ include 'include/header.php';
 
 <div class="row">
     <div class="col-xs-12 text-center">
-        <p>Pressing the following button the identities listed below will be joined.</p>
+        <p>Pressing the following button the identities listed above will be joined.</p>
         <form action="confirm-join" method="POST">
             <input type="hidden" name="token" value="<?php echo $token; ?>" />
             <input type="submit" class="btn btn-success btn-lg" value="Join users" />
diff --git a/views/index.php b/views/index.php
index e5ba7470c53b1d9d79743b7d2b8150dd4219824e..053d47880c349b4137c5589ffe66d432c075d3c0 100644
--- a/views/index.php
+++ b/views/index.php
@@ -67,7 +67,7 @@ include 'include/header.php';
     </div>
     <div class="row">
         <div class="col-sm-5 col-xs-12">
-            <div class="panel panel-default">
+            <div class="panel panel-default" id="panel-identities">
                 <div class="panel-heading">
                     <h3 class="panel-title">Your identities</h3>
                 </div>