diff --git a/auth/saml2/aai.php b/auth/saml2/aai.php
deleted file mode 100644
index 0b8277958298ca17ce7f111d99336cdf8c878078..0000000000000000000000000000000000000000
--- a/auth/saml2/aai.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/* ----------------------------------------------------------------------------
- *               INAF - National Institute for Astrophysics
- *               IRA  - Radioastronomical Institute - Bologna
- *               OATS - Astronomical Observatory - Trieste
- * ----------------------------------------------------------------------------
- *
- * Copyright (C) 2016 Istituto Nazionale di Astrofisica
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License Version 3 as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/* This page MUST be protected by Shibboleth authentication 
- * On Apache httpd:
- * AuthType shibboleth
- * ShibRequestSetting requireSession 1
- * Require valid-user
- */
-
-include '../../include/init.php';
-startSession();
-
-if (isset($_SERVER['Shib-Session-ID'])) {
-
-    // Retrieving eduPersonPrincipalName (eppn)
-    $eppn = $_SERVER['eppn'];
-
-    // Search if the user is already registered into RAP using the eppn.
-    // The persistent id should be a more appropriate identifier, however at IA2
-    // we need to import all INAF user into RAP, even if they will never register,
-    // and in that case we know only their eppn.
-    $user = $userHandler->findUserByIdentity(RAP\Identity::EDU_GAIN, $eppn);
-
-    if ($user === null) {
-        // Creating a new user
-        $user = new RAP\User();
-
-        $identity = new RAP\Identity(RAP\Identity::EDU_GAIN);
-        $identity->email = $_SERVER['mail'];
-        $identity->name = $_SERVER['givenName'];
-        $identity->surname = $_SERVER['sn'];
-        $identity->typedId = $eppn;
-        $identity->eppn = $eppn;
-        //$_SERVER['Shib-Identity-Provider']
-
-        $user->addIdentity($identity);
-
-        $session->userToLogin = $user;
-        $session->save();
-        header('Location: ' . $BASE_PATH . '/tou-check');
-        die();
-    }
-
-    $auditLog->info("LOGIN,eduGAIN," . $user->id);
-    $callbackHandler->manageLoginRedirect($user, $session);
-} else {
-    http_response_code(500);
-    die("Shib-Session-ID not found!");
-}
diff --git a/auth/social/facebook_login.php b/auth/social/facebook_login.php
deleted file mode 100755
index 844cf9f6a6dc8c677f1f43c1229a89e949d3bda3..0000000000000000000000000000000000000000
--- a/auth/social/facebook_login.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/* ----------------------------------------------------------------------------
- *               INAF - National Institute for Astrophysics
- *               IRA  - Radioastronomical Institute - Bologna
- *               OATS - Astronomical Observatory - Trieste
- * ----------------------------------------------------------------------------
- *
- * Copyright (C) 2016 Istituto Nazionale di Astrofisica
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License Version 3 as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/* This page uses the Facebook API for generating the redirect URL to use for Facebook login */
-
-include '../../include/init.php';
-startSession();
-
-// Retrieve Facebook configuration
-$Facebook = $AUTHENTICATION_METHODS['Facebook'];
-
-$fb = new Facebook\Facebook([
-    'app_id' => $Facebook['id'],
-    'app_secret' => $Facebook['secret'],
-    'default_graph_version' => $Facebook['version'],
-        ]);
-
-$helper = $fb->getRedirectLoginHelper();
-
-$permissions = ['email']; // Optional permissions: we need user email
-
-$loginUrl = $helper->getLoginUrl($Facebook['callback'], $permissions);
-
-header("Location: $loginUrl");
-?>
diff --git a/auth/social/facebook_token.php b/auth/social/facebook_token.php
deleted file mode 100755
index ef7446143aea2c92d8cf87bcd2bcbe322a60bc0e..0000000000000000000000000000000000000000
--- a/auth/social/facebook_token.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-/* ----------------------------------------------------------------------------
- *               INAF - National Institute for Astrophysics
- *               IRA  - Radioastronomical Institute - Bologna
- *               OATS - Astronomical Observatory - Trieste
- * ----------------------------------------------------------------------------
- *
- * Copyright (C) 2016 Istituto Nazionale di Astrofisica
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License Version 3 as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/* Facebook callback page */
-
-include '../../include/init.php';
-startSession();
-
-// Retrieve Facebook configuration
-$Facebook = $AUTHENTICATION_METHODS['Facebook'];
-
-$fb = new Facebook\Facebook([
-    'app_id' => $Facebook['id'],
-    'app_secret' => $Facebook['secret'],
-    'default_graph_version' => $Facebook['version'],
-        ]);
-
-$helper = $fb->getRedirectLoginHelper();
-if (isset($_GET['state'])) {
-    $helper->getPersistentDataHandler()->set('state', $_GET['state']);
-}
-
-try {
-    // obtaining current URL without query string
-    $url = "https://$_SERVER[HTTP_HOST]" . strtok($_SERVER["REQUEST_URI"], '?');
-    $accessToken = $helper->getAccessToken($url);
-} catch (Facebook\Exceptions\FacebookResponseException $e) {
-    // When Graph returns an error
-    http_response_code(500);
-    die('Graph returned an error: ' . $e->getMessage());
-} catch (Facebook\Exceptions\FacebookSDKException $e) {
-    // When validation fails or other local issues
-    http_response_code(500);
-    die('Facebook SDK returned an error: ' . $e->getMessage());
-}
-if (!isset($accessToken)) {
-    if ($helper->getError()) {
-        $errorMessage = "Error: " . $helper->getError() . "<br>";
-        $errorMessage = $errorMessage . "Error Code: " . $helper->getErrorCode() . "<br>";
-        $errorMessage = $errorMessage . "Error Reason: " . $helper->getErrorReason() . "<br>";
-        $errorMessage = $errorMessage . "Error Description: " . $helper->getErrorDescription();
-    } else {
-        $errorMessage = "Bad request";
-    }
-
-    http_response_code(500);
-    die($errorMessage);
-}
-
-try {
-    // Returns a `Facebook\FacebookResponse` object
-    $response = $fb->get('/me?fields=id,first_name,last_name,email', $accessToken);
-} catch (Facebook\Exceptions\FacebookResponseException $e) {
-    echo 'Graph returned an error: ' . $e->getMessage();
-    exit;
-} catch (Facebook\Exceptions\FacebookSDKException $e) {
-    echo 'Facebook SDK returned an error: ' . $e->getMessage();
-    exit;
-}
-
-$_SESSION['fb_access_token'] = (string) $accessToken;
-
-$fbUser = $response->getGraphUser();
-
-$typedId = $fbUser["id"];
-
-// Search if the user is already registered into RAP using the Facebook ID.
-$user = $userHandler->findUserByIdentity(RAP\Identity::FACEBOOK, $typedId);
-
-if ($user === null) {
-    // Create new user
-    $user = new RAP\User();
-
-    $identity = new RAP\Identity(RAP\Identity::FACEBOOK);
-    $identity->email = $fbUser["email"];
-    $identity->name = $fbUser["first_name"];
-    $identity->surname = $fbUser["last_name"];
-    $identity->typedId = $typedId;
-
-    $user->addIdentity($identity);
-
-    $session->userToLogin = $user;
-    $session->save();
-    header('Location: ' . $BASE_PATH . '/tou-check');
-    die();
-}
-
-$auditLog->info("LOGIN,Facebook," . $user->id);
-$callbackHandler->manageLoginRedirect($user, $session);
-?>
diff --git a/auth/social/google_token.php b/auth/social/google_token.php
deleted file mode 100644
index 16831cc69bb0a67df476cc4a2e6a4f29b86d5db8..0000000000000000000000000000000000000000
--- a/auth/social/google_token.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-/* ----------------------------------------------------------------------------
- *               INAF - National Institute for Astrophysics
- *               IRA  - Radioastronomical Institute - Bologna
- *               OATS - Astronomical Observatory - Trieste
- * ----------------------------------------------------------------------------
- *
- * Copyright (C) 2016 Istituto Nazionale di Astrofisica
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License Version 3 as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/* Google redirect and callback page */
-
-include '../../include/init.php';
-startSession();
-
-// Retrieve Google configuration
-$Google = $AUTHENTICATION_METHODS['Google'];
-
-$client = new Google_Client(array(
-    'client_id' => $Google['id'],
-    'client_secret' => $Google['secret'],
-    'redirect_uri' => $Google['callback'],
-        ));
-
-// Ask permission to obtain user email and profile information
-$client->setScopes(array(Google_Service_People::USERINFO_EMAIL, Google_Service_People::USERINFO_PROFILE));
-
-if (isset($_REQUEST['logout'])) {
-// Reset the access token stored into the session
-    unset($_SESSION['access_token']);
-}
-
-if (isset($_GET['code'])) {
-// An access token has been returned from the auth URL.
-    $client->authenticate($_GET['code']);
-    $_SESSION['access_token'] = $client->getAccessToken();
-}
-
-//if (isset($_SESSION['access_token'])) {
-//    $client->setAccessToken($_SESSION['access_token']);
-//}
-
-if ($client->getAccessToken()) {
-
-    // Query web service for retrieving user information
-    $service = new Google_Service_People($client);
-
-    try {
-        $res = $service->people->get('people/me', array('requestMask.includeField' => 'person.names,person.email_addresses'));
-    } catch (Google_Service_Exception $e) {
-        echo '<p>' . json_encode($e->getErrors()) . '</p>';
-        $thisPage = $PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
-        echo '<p><a href="' . $thisPage . '?logout">Click here to unset the access token</a></p>';
-    }
-
-    $name = $res->getNames()[0]->getGivenName();
-    $surname = $res->getNames()[0]->getFamilyName();
-
-    $emailAddresses = [];
-    foreach ($res->getEmailAddresses() as $addr) {
-        array_push($emailAddresses, $addr->value);
-    }
-
-    $typedId = explode('/', $res->getResourceName())[1];
-
-    // Search if the user is already registered into RAP using the Google ID.
-    $user = $userHandler->findUserByIdentity(RAP\Identity::GOOGLE, $typedId);
-
-    if ($user === null) {
-        // Create new user
-        $user = new RAP\User();
-
-        $identity = new RAP\Identity(RAP\Identity::GOOGLE);
-        $identity->email = $emailAddresses[0];
-        $identity->name = $name;
-        $identity->surname = $surname;
-        $identity->typedId = $typedId;
-
-        $user->addIdentity($identity);
-
-        $session->userToLogin = $user;
-        $session->save();
-        header('Location: ' . $BASE_PATH . '/tou-check');
-        die();
-    }
-
-    $auditLog->info("LOGIN,Google," . $user->id);
-    $callbackHandler->manageLoginRedirect($user, $session);
-
-    die();
-} else {
-    // Redirect to Google authorization URL for obtaining an access token
-    $authUrl = $client->createAuthUrl();
-    header('Location: ' . $authUrl);
-    die();
-}
-?>
diff --git a/auth/social/linkedin_login.php b/auth/social/linkedin_login.php
deleted file mode 100644
index 2c64969c6e03d78549090327a7af3cc67fa5b61a..0000000000000000000000000000000000000000
--- a/auth/social/linkedin_login.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/* ----------------------------------------------------------------------------
- *               INAF - National Institute for Astrophysics
- *               IRA  - Radioastronomical Institute - Bologna
- *               OATS - Astronomical Observatory - Trieste
- * ----------------------------------------------------------------------------
- *
- * Copyright (C) 2016 Istituto Nazionale di Astrofisica
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License Version 3 as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/* This page redirects to LinkedIn login page */
-
-include '../../include/init.php';
-startSession();
-
-// Retrieve LinkedIn configuration
-$LinkedIn = $AUTHENTICATION_METHODS['LinkedIn'];
-
-$url = "https://www.linkedin.com/oauth/v2/authorization?response_type=code";
-$url .= "&client_id=" . $LinkedIn['id'];
-$url .= "&redirect_uri=" . $LinkedIn['callback'];
-$url .= "&state=789654123";
-$url .= "&scope=r_basicprofile r_emailaddress";
-
-header("Location: $url");
-?>
diff --git a/auth/social/linkedin_token.php b/auth/social/linkedin_token.php
deleted file mode 100644
index 64a564733dcc1ddd1d79c8cfa1ba866aaf0e2046..0000000000000000000000000000000000000000
--- a/auth/social/linkedin_token.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/* ----------------------------------------------------------------------------
- *               INAF - National Institute for Astrophysics
- *               IRA  - Radioastronomical Institute - Bologna
- *               OATS - Astronomical Observatory - Trieste
- * ----------------------------------------------------------------------------
- *
- * Copyright (C) 2016 Istituto Nazionale di Astrofisica
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License Version 3 as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/* LinkedIn callback page. Curl is used, because LinkedIn doesn't provide official PHP API. */
-
-include '../../include/init.php';
-startSession();
-
-// Retrieve LinkedIn configuration
-$LinkedIn = $AUTHENTICATION_METHODS['LinkedIn'];
-
-if (!isset($_REQUEST['code'])) {
-    die("Unable to get LinkedIn client code");
-}
-
-//create array of data to be posted to get AccessToken
-$post_data = array(
-    'grant_type' => "authorization_code",
-    'code' => $_REQUEST['code'],
-    'redirect_uri' => $LinkedIn['callback'],
-    'client_id' => $LinkedIn['id'],
-    'client_secret' => $LinkedIn['secret']);
-
-//traverse array and prepare data for posting (key1=value1)
-foreach ($post_data as $key => $value) {
-    $post_items[] = $key . '=' . $value;
-}
-
-//create the final string to be posted
-$post_string = implode('&', $post_items);
-
-//create cURL connection
-$conn1 = curl_init('https://www.linkedin.com/oauth/v2/accessToken');
-
-//set options
-curl_setopt($conn1, CURLOPT_CONNECTTIMEOUT, 30);
-curl_setopt($conn1, CURLOPT_RETURNTRANSFER, true);
-curl_setopt($conn1, CURLOPT_SSL_VERIFYPEER, true);
-curl_setopt($conn1, CURLOPT_FOLLOWLOCATION, 1);
-
-//set data to be posted
-curl_setopt($conn1, CURLOPT_POSTFIELDS, $post_string);
-
-//perform our request
-$result1 = curl_exec($conn1);
-$info1 = curl_getinfo($conn1);
-
-if ($info1['http_code'] === 200) {
-    $my_token = json_decode($result1, TRUE);
-    $access_token = $my_token['access_token'];
-    $expires_in = $my_token['expires_in'];
-    curl_close($conn1);
-} else {
-    //show information regarding the error
-    $errorMessage = "Error: LinkedIn server response code: " . $info1['http_code'] . " - ";
-    $errorMessage .= curl_error($conn1);
-    curl_close($conn1);
-    http_response_code(500);
-    die($errorMessage);
-}
-
-// Call to API
-$conn2 = curl_init();
-curl_setopt($conn2, CURLOPT_URL, "https://api.linkedin.com/v1/people/~:(first-name,last-name,email-address,id)?format=json");
-curl_setopt($conn2, CURLOPT_HTTPHEADER, array(
-    'Authorization: Bearer ' . $access_token
-));
-
-curl_setopt($conn2, CURLOPT_RETURNTRANSFER, true);
-$result = curl_exec($conn2);
-$info2 = curl_getinfo($conn2);
-
-if ($info2['http_code'] === 200) {
-    $data = json_decode($result, TRUE);
-
-    curl_close($conn2);
-
-    if (isset($data['errorCode'])) {
-        $errorMessage = $data['message'];
-        die($errorMessage);
-    }
-
-    $typedId = $data['id'];
-
-    // Search if the user is already registered into RAP using the LinkedIn ID.
-    $user = $userHandler->findUserByIdentity(RAP\Identity::LINKEDIN, $typedId);
-
-    if ($user === null) {
-        // Create new user
-        $user = new RAP\User();
-
-        $identity = new RAP\Identity(RAP\Identity::LINKEDIN);
-        $identity->email = $data['emailAddress'];
-        $identity->name = $data['firstName'];
-        $identity->surname = $data['lastName'];
-        $identity->typedId = $typedId;
-
-        $user->addIdentity($identity);
-
-        $session->userToLogin = $user;
-        $session->save();
-        header('Location: ' . $BASE_PATH . '/tou-check');
-        die();
-    }
-
-    $auditLog->info("LOGIN,LinkedIn," . $user->id);
-    $callbackHandler->manageLoginRedirect($user, $session);
-} else {
-    //show information regarding the error
-    $errorMessage = "Error: LinkedIn server response code: " . $info2['http_code'] . " - ";
-    $errorMessage = $errorMessage . curl_error($conn2);
-    curl_close($conn2);
-    die($errorMessage);
-}
-?>
diff --git a/auth/x509/certlogin.php b/auth/x509/certlogin.php
deleted file mode 100644
index c811cd83f49fbd3d9df74134901c9fca3f38120c..0000000000000000000000000000000000000000
--- a/auth/x509/certlogin.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-/* ----------------------------------------------------------------------------
- *               INAF - National Institute for Astrophysics
- *               IRA  - Radioastronomical Institute - Bologna
- *               OATS - Astronomical Observatory - Trieste
- * ----------------------------------------------------------------------------
- *
- * Copyright (C) 2016 Istituto Nazionale di Astrofisica
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License Version 3 as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/* This page must be protected by client certificate authentication 
- * On Apache httpd:
- * SSLVerifyClient require
- * SSLVerifyDepth 10
- * SSLOptions +ExportCertData
- */
-
-include '../../include/init.php';
-startSession();
-
-if (isset($_SERVER['SSL_CLIENT_VERIFY']) && isset($_SERVER['SSL_CLIENT_V_REMAIN']) &&
-        $_SERVER['SSL_CLIENT_VERIFY'] === 'SUCCESS' && $_SERVER['SSL_CLIENT_V_REMAIN'] > 0) {
-
-    $x509Data = RAP\X509Data::parse($_SERVER);
-
-    $user = $userHandler->findUserByIdentity(RAP\Identity::X509, $x509Data->serialNumber);
-
-    if ($user === null) {
-        /**
-         * We want to extract name and surname from the X.509 certificate, however X.509
-         * puts name and surname together (inside the CN field).
-         * If name and surname are single words it is possible to retrieve them splitting
-         * on the space character, otherwise the user has to choose the correct combination.
-         * In that case partial X.509 data is temporarily stored into the user session and
-         * the page views/x509-name-surname.php is shown to the user before completing the
-         * registration, in order to allow him/her selecting the correct name and surname.
-         */
-        if ($x509Data->name === null) {
-            $session->x509DataToRegister = $x509Data;
-            $session->save();
-            header('Location: ' . $BASE_PATH . '/x509-name-surname');
-        } else {
-            $session->userToLogin = $x509Data->toUser();
-            $session->save();
-            header('Location: ' . $BASE_PATH . '/tou-check');
-        }
-        die();
-    } else {
-        $auditLog->info("LOGIN,X.509," . $user->id);
-        $callbackHandler->manageLoginRedirect($user, $session);
-    }
-} else {
-    http_response_code(500);
-    die("Unable to verify client certificate");
-}
diff --git a/classes/CallbackHandler.php b/classes/CallbackHandler.php
deleted file mode 100644
index 5c5fbbee0b5b804c8d222ec6d8bbee9c299d7896..0000000000000000000000000000000000000000
--- a/classes/CallbackHandler.php
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-
-/* ----------------------------------------------------------------------------
- *               INAF - National Institute for Astrophysics
- *               IRA  - Radioastronomical Institute - Bologna
- *               OATS - Astronomical Observatory - Trieste
- * ----------------------------------------------------------------------------
- *
- * Copyright (C) 2016 Istituto Nazionale di Astrofisica
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License Version 3 as published by the
- * Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 51
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-namespace RAP;
-
-/**
- * Manage callback URL validation and redirection
- */
-class CallbackHandler {
-
-    private $locator;
-
-    public function __construct(Locator $locator) {
-        $this->locator = $locator;
-    }
-
-    /**
-     * If a callback URL is not in the configured list we should return null.
-     */
-    public function filterCallbackURL($callbackURL) {
-        foreach ($this->callbacks as $callback) {
-            if ($callback['url'] === $callbackURL) {
-                return $callbackURL;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Each callback has a title and a logo in order to avoid confusion in users
-     * and show in which application they are logging in using RAP.
-     * @param type $callbackURL
-     * @return type the callback title or null if the callback URL is not listed
-     * in configuration file or it doesn't have a title.
-     */
-    public function getCallbackTitle($callbackURL) {
-
-        foreach ($this->callbacks as $callback) {
-            if ($callback['url'] === $callbackURL) {
-                return $callback['title'];
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Each callback has a title and a logo in order to avoid confusion in users
-     * and show in which application they are logging in using RAP.
-     * @param type $callbackURL
-     * @return type the callback logo or null if the callback URL is not listed
-     * in configuration file or it doesn't have a logo.
-     */
-    public function getCallbackLogo($callbackURL) {
-
-        foreach ($this->callbacks as $callback) {
-            if ($callback['url'] === $callbackURL) {
-                if (array_key_exists('logo', $callback)) {
-                    return $callback['logo'];
-                } else {
-                    return null;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    public function manageLoginRedirect(User $user, SessionData $session) {
-        
-        if($session->getOAuth2Data() !== null) {
-            $session->user = $user;
-            $session->save();
-            $redirectUrl = $this->locator->getOAuth2RequestHandler()->getCodeResponseUrl();
-            $session->setOAuth2Data(null);
-            header('Location: ' . $redirectUrl);
-            die();
-        }
-        
-        if ($session->getCallbackURL() === null) {
-            http_response_code(401);
-            die("Unauthorized callback URL");
-        }
-
-        if ($session->getCallbackURL() === $this->basePath . '/') {
-            // Login in session
-            $session->user = $user;
-            $session->save();
-            // Return to index
-            header('Location: ' . $this->basePath);
-            die();
-        } else {
-            // External login using token
-            header('Location: ' . $this->getLoginWithTokenURL($user->id, $session->getCallbackURL()));
-            die();
-        }
-    }
-
-    public function getLoginWithTokenURL($userId, $callbackURL) {
-        $token = Util::createNewToken();
-        $this->dao->createLoginToken($token, $userId);
-        return $callbackURL . '?token=' . $token;
-    }
-
-}
diff --git a/classes/IdTokenBuilder.php b/classes/IdTokenBuilder.php
index 4150c0ccdcbc0c146f6003682589be46cb189db5..cef3321aa36d6ca8752660bf89e1570ddc276ca6 100644
--- a/classes/IdTokenBuilder.php
+++ b/classes/IdTokenBuilder.php
@@ -23,7 +23,7 @@ class IdTokenBuilder {
 
     private function createPayloadArray(AccessToken $accessToken) {
 
-        $user = $this->locator->getDAO()->findUserById($accessToken->userId);
+        $user = $this->locator->getUserDAO()->findUserById($accessToken->userId);
 
         $payloadArr = array(
             'iss' => $this->locator->config->jwtIssuer,
diff --git a/classes/Locator.php b/classes/Locator.php
index a1e5942891b89601e66ad292862c684041768880..35fe98275fd6c16848b37d7469e57b664b537d50 100644
--- a/classes/Locator.php
+++ b/classes/Locator.php
@@ -32,11 +32,21 @@ class Locator {
         return $this->getProtocol() . $_SERVER['HTTP_HOST'] . $this->config->contextRoot;
     }
 
-    public function getDAO(): DAO {
+    public function getUserDAO(): UserDAO {
         $databaseConfig = $this->config->databaseConfig;
         switch ($databaseConfig->dbtype) {
             case 'MySQL':
-                return new MySQLDAO($this);
+                return new MySQLUserDAO($this);
+            default:
+                throw new \Exception($databaseConfig->dbtype . ' not supported yet');
+        }
+    }
+
+    public function getOAuth2ClientDAO(): OAuth2ClientDAO {
+        $databaseConfig = $this->config->databaseConfig;
+        switch ($databaseConfig->dbtype) {
+            case 'MySQL':
+                return new MySQLOAuth2ClientDAO($this);
             default:
                 throw new \Exception($databaseConfig->dbtype . ' not supported yet');
         }
@@ -61,13 +71,13 @@ class Locator {
                 throw new \Exception($databaseConfig->dbtype . ' not supported yet');
         }
     }
-    
+
     public function getCallbackHandler(): CallbackHandler {
         return new CallbackHandler($this);
     }
 
     public function getUserHandler(): UserHandler {
-        return new UserHandler($this->getDAO());
+        return new UserHandler($this);
     }
 
     public function getMailSender(): MailSender {
diff --git a/classes/OAuth2RequestHandler.php b/classes/OAuth2RequestHandler.php
index 4d801f64edaa8fe2a7a8a3525a252a6298e1c02e..aa95981059a57aa6bac7446d4033957652eba073 100644
--- a/classes/OAuth2RequestHandler.php
+++ b/classes/OAuth2RequestHandler.php
@@ -20,7 +20,7 @@ class OAuth2RequestHandler {
             throw new BadRequestException("Redirect URI is required");
         }
 
-        $client = $this->locator->getDAO()->getOAuth2ClientByClientId($params['client_id']);
+        $client = $this->locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($params['client_id']);
         if ($client === null) {
             throw new BadRequestException("Invalid client id: " . $params['client_id']);
         }
@@ -60,7 +60,7 @@ class OAuth2RequestHandler {
         $accessToken = new \RAP\AccessToken();
         $accessToken->code = base64_encode(bin2hex(openssl_random_pseudo_bytes(64)));
         $accessToken->token = base64_encode(bin2hex(openssl_random_pseudo_bytes(128)));
-        $accessToken->userId = $session->user->id;
+        $accessToken->userId = $session->getUser()->id;
         $accessToken->clientId = $session->getOAuth2Data()->clientId;
         $accessToken->redirectUri = $session->getOAuth2Data()->redirectUrl;
         $accessToken->scope = $session->getOAuth2Data()->scope;
@@ -128,7 +128,7 @@ class OAuth2RequestHandler {
         }
 
         $accessToken = $this->locator->getAccessTokenDAO()->getAccessToken($token);
-        $user = $this->locator->getDAO()->findUserById($accessToken->userId);
+        $user = $this->locator->getUserDAO()->findUserById($accessToken->userId);
 
         $result = [];
         $result['exp'] = $this->getExpiresIn($accessToken);
diff --git a/classes/UserHandler.php b/classes/UserHandler.php
index ec0002910d5c447a683dd73558934ab1d9f03003..b98ccdc314024cfb4df33581c0c57d1d2481ddb2 100644
--- a/classes/UserHandler.php
+++ b/classes/UserHandler.php
@@ -31,8 +31,8 @@ class UserHandler {
 
     private $dao;
 
-    public function __construct(DAO $dao) {
-        $this->dao = $dao;
+    public function __construct(Locator $locator) {
+        $this->dao = $locator->getUserDAO();
     }
 
     /**
diff --git a/classes/X509Data.php b/classes/X509Data.php
index ddfb4028bd44088b4c043b8f4b8127bc3cfd2148..3b8a931378138c4863bca14fa28e617ea3257a76 100644
--- a/classes/X509Data.php
+++ b/classes/X509Data.php
@@ -203,9 +203,7 @@ class X509Data {
         return $parsedData;
     }
 
-    public function toUser() {
-
-        $user = new User();
+    public function toIdentity() {
 
         $identity = new Identity(Identity::X509);
         $identity->email = $this->email;
@@ -214,9 +212,7 @@ class X509Data {
         $identity->typedId = $this->serialNumber;
         $identity->institution = $this->institution;
 
-        $user->addIdentity($identity);
-
-        return $user;
+        return $identity;
     }
 
 }
diff --git a/classes/datalayer/OAuth2ClientDAO.php b/classes/datalayer/OAuth2ClientDAO.php
new file mode 100644
index 0000000000000000000000000000000000000000..9ea29d8af5f66ac7fb1679989554464d6d3473f4
--- /dev/null
+++ b/classes/datalayer/OAuth2ClientDAO.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace RAP;
+
+/**
+ * CRUD methods for OAuth2Clients (used by admin interface).
+ */
+interface OAuth2ClientDAO {
+
+    function getOAuth2Clients(): array;
+
+    function createOAuth2Client($client): OAuth2Client;
+
+    function updateOAuth2Client($client): OAuth2Client;
+
+    function deleteOAuth2Client($clientId);
+
+    /**
+     * Retrieve the client from the configured client id (the one associated to
+     * the secret, not the database id).
+     */
+    function getOAuth2ClientByClientId($clientId): ?OAuth2Client;
+}
diff --git a/classes/datalayer/DAO.php b/classes/datalayer/UserDAO.php
similarity index 84%
rename from classes/datalayer/DAO.php
rename to classes/datalayer/UserDAO.php
index 4de0886db83f7d9859c8b09d15a6664e0e065577..e37603ee7cd81881cf5bc2921984592e77c311fb 100644
--- a/classes/datalayer/DAO.php
+++ b/classes/datalayer/UserDAO.php
@@ -24,11 +24,7 @@
 
 namespace RAP;
 
-/**
- * Data Access Object interface for accessing the RAP database.
- * Current implementations: RAP\MySQLDAO
- */
-interface DAO {
+interface UserDAO {
 
     /**
      * Create a new identity.
@@ -98,20 +94,4 @@ interface DAO {
      */
     function deleteJoinRequest($token);
 
-    /**
-     * CRUD methods for OAuth2Clients (used by admin interface).
-     */
-    function getOAuth2Clients();
-
-    function createOAuth2Client($client): OAuth2Client;
-
-    function updateOAuth2Client($client): OAuth2Client;
-
-    function deleteOAuth2Client($clientId);
-
-    /**
-     * Retrieve the client from the configured client id (the one associated to
-     * the secret, not the database id).
-     */
-    function getOAuth2ClientByClientId($clientId): ?OAuth2Client;
 }
diff --git a/classes/datalayer/mysql/MySQLOAuth2ClientDAO.php b/classes/datalayer/mysql/MySQLOAuth2ClientDAO.php
new file mode 100644
index 0000000000000000000000000000000000000000..20cc7a45d299337ad053508fce086f8df65b15cf
--- /dev/null
+++ b/classes/datalayer/mysql/MySQLOAuth2ClientDAO.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace RAP;
+
+class MySQLOAuth2ClientDAO extends BaseMySQLDAO implements OAuth2ClientDAO {
+
+    public function __construct($config) {
+        parent::__construct($config);
+    }
+
+    function getOAuth2Clients(): array {
+        $dbh = $this->getDBHandler();
+
+        // Load clients info
+        $queryClient = "SELECT id, title, icon, client, secret, redirect_url, scope FROM oauth2_client";
+        $stmtClients = $dbh->prepare($queryClient);
+        $stmtClients->execute();
+
+        $clientsMap = [];
+
+        foreach ($stmtClients->fetchAll() as $row) {
+            $client = new OAuth2Client();
+            $client->id = $row['id'];
+            $client->title = $row['title'];
+            $client->icon = $row['icon'];
+            $client->client = $row['client'];
+            $client->secret = $row['secret'];
+            $client->redirectUrl = $row['redirect_url'];
+            $client->scope = $row['scope'];
+            $clientsMap[$client->id] = $client;
+        }
+
+        // Load authentication methods info
+        $queryAuthNMethods = "SELECT client_id, auth_method FROM oauth2_client_auth_methods";
+
+        $stmtAuthNMethods = $dbh->prepare($queryAuthNMethods);
+        $stmtAuthNMethods->execute();
+
+        foreach ($stmtAuthNMethods->fetchAll() as $row) {
+            $id = $row['client_id'];
+            array_push($clientsMap[$id]->authMethods, $row['auth_method']);
+        }
+
+        $clients = [];
+        foreach ($clientsMap as $id => $client) {
+            array_push($clients, $client);
+        }
+
+        return $clients;
+    }
+
+    function createOAuth2Client($client): OAuth2Client {
+        $dbh = $this->getDBHandler();
+
+        try {
+            $dbh->beginTransaction();
+
+            $stmt = $dbh->prepare("INSERT INTO `oauth2_client`(`title`, `icon`, `client`, `secret`, `redirect_url`, `scope`)"
+                    . " VALUES(:title, :icon, :client, :secret, :redirect_url, :scope)");
+
+            $stmt->bindParam(':title', $client->title);
+            $stmt->bindParam(':icon', $client->icon);
+            $stmt->bindParam(':client', $client->client);
+            $stmt->bindParam(':secret', $client->secret);
+            $stmt->bindParam(':redirect_url', $client->redirectUrl);
+            $stmt->bindParam(':scope', $client->scope);
+
+            $stmt->execute();
+
+            $client->id = $dbh->lastInsertId();
+
+            foreach ($client->authMethods as $method) {
+                $stmt = $dbh->prepare("INSERT INTO `oauth2_client_auth_methods`(`client_id`, `auth_method`)"
+                        . " VALUES(:client_id, :auth_method)");
+
+                $stmt->bindParam(':client_id', $client->id);
+                $stmt->bindParam(':auth_method', $method);
+
+                $stmt->execute();
+            }
+
+            $dbh->commit();
+        } catch (Exception $ex) {
+            $dbh->rollBack();
+            throw $ex;
+        }
+
+        return $client;
+    }
+
+    function updateOAuth2Client($client): OAuth2Client {
+        $dbh = $this->getDBHandler();
+
+        try {
+            $dbh->beginTransaction();
+
+            $stmt = $dbh->prepare("UPDATE `oauth2_client` SET `title` = :title, `icon` = :icon, "
+                    . " `client` = :client, `secret` = :secret, `redirect_url` = :redirect_url, `scope` = :scope "
+                    . " WHERE id = :id");
+
+            $stmt->bindParam(':title', $client->title);
+            $stmt->bindParam(':icon', $client->icon);
+            $stmt->bindParam(':client', $client->client);
+            $stmt->bindParam(':secret', $client->secret);
+            $stmt->bindParam(':redirect_url', $client->redirectUrl);
+            $stmt->bindParam(':scope', $client->scope);
+            $stmt->bindParam(':id', $client->id);
+
+            $stmt->execute();
+
+            // Delete old authentication methods
+            $stmt = $dbh->prepare("DELETE FROM oauth2_client_auth_methods WHERE client_id = :id");
+            $stmt->bindParam(':id', $client->id);
+
+            $stmt->execute();
+
+            // Re-add authentication methods
+            foreach ($client->authMethods as $method) {
+                $stmt = $dbh->prepare("INSERT INTO `oauth2_client_auth_methods`(`client_id`, `auth_method`)"
+                        . " VALUES(:client_id, :auth_method)");
+
+                $stmt->bindParam(':client_id', $client->id);
+                $stmt->bindParam(':auth_method', $method);
+
+                $stmt->execute();
+            }
+
+            $dbh->commit();
+        } catch (Exception $ex) {
+            $dbh->rollBack();
+            throw $ex;
+        }
+
+        return $client;
+    }
+
+    function deleteOAuth2Client($clientId) {
+        $dbh = $this->getDBHandler();
+        try {
+            $dbh->beginTransaction();
+
+            $stmt = $dbh->prepare("DELETE FROM `oauth2_client_auth_methods` WHERE client_id = :id");
+            $stmt->bindParam(':id', $clientId);
+            $stmt->execute();
+
+            $stmt = $dbh->prepare("DELETE FROM `oauth2_client` WHERE id = :id");
+            $stmt->bindParam(':id', $clientId);
+            $stmt->execute();
+
+            $dbh->commit();
+        } catch (Exception $ex) {
+            $dbh->rollBack();
+            throw $ex;
+        }
+    }
+
+    function getOAuth2ClientByClientId($clientId): ?OAuth2Client {
+        $dbh = $this->getDBHandler();
+
+        // Load clients info
+        $queryClient = "SELECT id, title, icon, client, secret, redirect_url, scope FROM oauth2_client WHERE client = :client";
+        $stmtClient = $dbh->prepare($queryClient);
+        $stmtClient->bindParam(':client', $clientId);
+        $stmtClient->execute();
+
+        $result = $stmtClient->fetchAll();
+
+        if (count($result) === 0) {
+            return null;
+        }
+        if (count($result) > 1) {
+            throw new Exception("Found multiple clients associated to the same client id!");
+        }
+
+        $row = $result[0];
+
+        $client = new OAuth2Client();
+        $client->id = $row['id'];
+        $client->title = $row['title'];
+        $client->icon = $row['icon'];
+        $client->client = $row['client'];
+        $client->secret = $row['secret'];
+        $client->redirectUrl = $row['redirect_url'];
+        $client->scope = $row['scope'];
+
+        // Load authentication methods info
+        $queryAuthNMethods = "SELECT auth_method FROM oauth2_client_auth_methods WHERE client_id = :id";
+
+        $stmtAuthNMethods = $dbh->prepare($queryAuthNMethods);
+        $stmtAuthNMethods->bindParam(':id', $client->id);
+        $stmtAuthNMethods->execute();
+
+        foreach ($stmtAuthNMethods->fetchAll() as $row) {
+            array_push($client->authMethods, $row['auth_method']);
+        }
+
+        return $client;
+    }
+
+}
diff --git a/classes/MySQLDAO.php b/classes/datalayer/mysql/MySQLUserDAO.php
similarity index 58%
rename from classes/MySQLDAO.php
rename to classes/datalayer/mysql/MySQLUserDAO.php
index 7208695b7467cc5b9c46f6921556dcb8a3d3e218..19a02b5625db1ba4d85a0a647fa3291dd974357f 100644
--- a/classes/MySQLDAO.php
+++ b/classes/datalayer/mysql/MySQLUserDAO.php
@@ -27,7 +27,7 @@ namespace RAP;
 /**
  * MySQL implementation of the DAO interface. See comments on the DAO interface.
  */
-class MySQLDAO extends BaseMySQLDAO implements DAO {
+class MySQLUserDAO extends BaseMySQLDAO implements UserDAO {
 
     public function __construct(Locator $locator) {
         parent::__construct($locator);
@@ -277,193 +277,4 @@ class MySQLDAO extends BaseMySQLDAO implements DAO {
         $stmt->execute();
     }
 
-    function getOAuth2Clients() {
-        $dbh = $this->getDBHandler();
-
-        // Load clients info
-        $queryClient = "SELECT id, title, icon, client, secret, redirect_url, scope FROM oauth2_client";
-        $stmtClients = $dbh->prepare($queryClient);
-        $stmtClients->execute();
-
-        $clientsMap = [];
-
-        foreach ($stmtClients->fetchAll() as $row) {
-            $client = new OAuth2Client();
-            $client->id = $row['id'];
-            $client->title = $row['title'];
-            $client->icon = $row['icon'];
-            $client->client = $row['client'];
-            $client->secret = $row['secret'];
-            $client->redirectUrl = $row['redirect_url'];
-            $client->scope = $row['scope'];
-            $clientsMap[$client->id] = $client;
-        }
-
-        // Load authentication methods info
-        $queryAuthNMethods = "SELECT client_id, auth_method FROM oauth2_client_auth_methods";
-
-        $stmtAuthNMethods = $dbh->prepare($queryAuthNMethods);
-        $stmtAuthNMethods->execute();
-
-        foreach ($stmtAuthNMethods->fetchAll() as $row) {
-            $id = $row['client_id'];
-            array_push($clientsMap[$id]->authMethods, $row['auth_method']);
-        }
-
-        $clients = [];
-        foreach ($clientsMap as $id => $client) {
-            array_push($clients, $client);
-        }
-
-        return $clients;
-    }
-
-    function createOAuth2Client($client): OAuth2Client {
-        $dbh = $this->getDBHandler();
-
-        try {
-            $dbh->beginTransaction();
-
-            $stmt = $dbh->prepare("INSERT INTO `oauth2_client`(`title`, `icon`, `client`, `secret`, `redirect_url`, `scope`)"
-                    . " VALUES(:title, :icon, :client, :secret, :redirect_url, :scope)");
-
-            $stmt->bindParam(':title', $client->title);
-            $stmt->bindParam(':icon', $client->icon);
-            $stmt->bindParam(':client', $client->client);
-            $stmt->bindParam(':secret', $client->secret);
-            $stmt->bindParam(':redirect_url', $client->redirectUrl);
-            $stmt->bindParam(':scope', $client->scope);
-
-            $stmt->execute();
-
-            $client->id = $dbh->lastInsertId();
-
-            foreach ($client->authMethods as $method) {
-                $stmt = $dbh->prepare("INSERT INTO `oauth2_client_auth_methods`(`client_id`, `auth_method`)"
-                        . " VALUES(:client_id, :auth_method)");
-
-                $stmt->bindParam(':client_id', $client->id);
-                $stmt->bindParam(':auth_method', $method);
-
-                $stmt->execute();
-            }
-
-            $dbh->commit();
-        } catch (Exception $ex) {
-            $dbh->rollBack();
-            throw $ex;
-        }
-
-        return $client;
-    }
-
-    function updateOAuth2Client($client): OAuth2Client {
-        $dbh = $this->getDBHandler();
-
-        try {
-            $dbh->beginTransaction();
-
-            $stmt = $dbh->prepare("UPDATE `oauth2_client` SET `title` = :title, `icon` = :icon, "
-                    . " `client` = :client, `secret` = :secret, `redirect_url` = :redirect_url, `scope` = :scope "
-                    . " WHERE id = :id");
-
-            $stmt->bindParam(':title', $client->title);
-            $stmt->bindParam(':icon', $client->icon);
-            $stmt->bindParam(':client', $client->client);
-            $stmt->bindParam(':secret', $client->secret);
-            $stmt->bindParam(':redirect_url', $client->redirectUrl);
-            $stmt->bindParam(':scope', $client->scope);
-            $stmt->bindParam(':id', $client->id);
-
-            $stmt->execute();
-
-            // Delete old authentication methods
-            $stmt = $dbh->prepare("DELETE FROM oauth2_client_auth_methods WHERE client_id = :id");
-            $stmt->bindParam(':id', $client->id);
-
-            $stmt->execute();
-
-            // Re-add authentication methods
-            foreach ($client->authMethods as $method) {
-                $stmt = $dbh->prepare("INSERT INTO `oauth2_client_auth_methods`(`client_id`, `auth_method`)"
-                        . " VALUES(:client_id, :auth_method)");
-
-                $stmt->bindParam(':client_id', $client->id);
-                $stmt->bindParam(':auth_method', $method);
-
-                $stmt->execute();
-            }
-
-            $dbh->commit();
-        } catch (Exception $ex) {
-            $dbh->rollBack();
-            throw $ex;
-        }
-
-        return $client;
-    }
-
-    function deleteOAuth2Client($clientId) {
-        $dbh = $this->getDBHandler();
-        try {
-            $dbh->beginTransaction();
-
-            $stmt = $dbh->prepare("DELETE FROM `oauth2_client_auth_methods` WHERE client_id = :id");
-            $stmt->bindParam(':id', $clientId);
-            $stmt->execute();
-
-            $stmt = $dbh->prepare("DELETE FROM `oauth2_client` WHERE id = :id");
-            $stmt->bindParam(':id', $clientId);
-            $stmt->execute();
-
-            $dbh->commit();
-        } catch (Exception $ex) {
-            $dbh->rollBack();
-            throw $ex;
-        }
-    }
-
-    function getOAuth2ClientByClientId($clientId): ?OAuth2Client {
-        $dbh = $this->getDBHandler();
-
-        // Load clients info
-        $queryClient = "SELECT id, title, icon, client, secret, redirect_url, scope FROM oauth2_client WHERE client = :client";
-        $stmtClient = $dbh->prepare($queryClient);
-        $stmtClient->bindParam(':client', $clientId);
-        $stmtClient->execute();
-
-        $result = $stmtClient->fetchAll();
-
-        if (count($result) === 0) {
-            return null;
-        }
-        if (count($result) > 1) {
-            throw new Exception("Found multiple clients associated to the same client id!");
-        }
-
-        $row = $result[0];
-
-        $client = new OAuth2Client();
-        $client->id = $row['id'];
-        $client->title = $row['title'];
-        $client->icon = $row['icon'];
-        $client->client = $row['client'];
-        $client->secret = $row['secret'];
-        $client->redirectUrl = $row['redirect_url'];
-        $client->scope = $row['scope'];
-
-        // Load authentication methods info
-        $queryAuthNMethods = "SELECT auth_method FROM oauth2_client_auth_methods WHERE client_id = :id";
-
-        $stmtAuthNMethods = $dbh->prepare($queryAuthNMethods);
-        $stmtAuthNMethods->bindParam(':id', $client->id);
-        $stmtAuthNMethods->execute();
-
-        foreach ($stmtAuthNMethods->fetchAll() as $row) {
-            array_push($client->authMethods, $row['auth_method']);
-        }
-
-        return $client;
-    }
-
 }
diff --git a/classes/login/FacebookLogin.php b/classes/login/FacebookLogin.php
new file mode 100644
index 0000000000000000000000000000000000000000..3b3b9e6779ed99420fcc92cd480dbd0abb614328
--- /dev/null
+++ b/classes/login/FacebookLogin.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace RAP;
+
+class FacebookLogin extends LoginHandler {
+
+    public function __construct(Locator $locator) {
+        parent::__construct($locator, Identity::FACEBOOK);
+    }
+
+    public function login() {
+
+        // Retrieve Facebook configuration
+        $Facebook = $this->locator->config->authenticationMethods->Facebook;
+
+        $fb = new \Facebook\Facebook([
+            'app_id' => $Facebook->id,
+            'app_secret' => $Facebook->secret,
+            'default_graph_version' => $Facebook->version,
+        ]);
+
+        $helper = $fb->getRedirectLoginHelper();
+
+        $permissions = ['email']; // Optional permissions: we need user email
+
+        $loginUrl = $helper->getLoginUrl($Facebook->callback, $permissions);
+
+        header("Location: $loginUrl");
+    }
+
+    public function retrieveToken() {
+        // Retrieve Facebook configuration
+        $Facebook = $this->locator->config->authenticationMethods->Facebook;
+
+        $fb = new \Facebook\Facebook([
+            'app_id' => $Facebook->id,
+            'app_secret' => $Facebook->secret,
+            'default_graph_version' => $Facebook->version,
+        ]);
+
+        $helper = $fb->getRedirectLoginHelper();
+        if (isset($_GET['state'])) {
+            $helper->getPersistentDataHandler()->set('state', $_GET['state']);
+        }
+
+        try {
+            // obtaining current URL without query string
+            $url = "https://$_SERVER[HTTP_HOST]" . strtok($_SERVER["REQUEST_URI"], '?');
+            $accessToken = $helper->getAccessToken($url);
+        } catch (Facebook\Exceptions\FacebookResponseException $e) {
+            // When Graph returns an error
+            http_response_code(500);
+            die('Graph returned an error: ' . $e->getMessage());
+        } catch (Facebook\Exceptions\FacebookSDKException $e) {
+            // When validation fails or other local issues
+            http_response_code(500);
+            die('Facebook SDK returned an error: ' . $e->getMessage());
+        }
+        if (!isset($accessToken)) {
+            if ($helper->getError()) {
+                $errorMessage = "Error: " . $helper->getError() . "<br>";
+                $errorMessage = $errorMessage . "Error Code: " . $helper->getErrorCode() . "<br>";
+                $errorMessage = $errorMessage . "Error Reason: " . $helper->getErrorReason() . "<br>";
+                $errorMessage = $errorMessage . "Error Description: " . $helper->getErrorDescription();
+            } else {
+                $errorMessage = "Bad request";
+            }
+
+            http_response_code(500);
+            die($errorMessage);
+        }
+
+        try {
+            // Returns a `Facebook\FacebookResponse` object
+            $response = $fb->get('/me?fields=id,first_name,last_name,email', $accessToken);
+        } catch (Facebook\Exceptions\FacebookResponseException $e) {
+            echo 'Graph returned an error: ' . $e->getMessage();
+            exit;
+        } catch (Facebook\Exceptions\FacebookSDKException $e) {
+            echo 'Facebook SDK returned an error: ' . $e->getMessage();
+            exit;
+        }
+
+        $_SESSION['fb_access_token'] = (string) $accessToken;
+
+        $fbUser = $response->getGraphUser();
+
+        $typedId = $fbUser["id"];
+
+        return $this->onIdentityDataReceived($typedId, function($identity) use($fbUser) {
+                    $identity->email = $fbUser["email"];
+                    $identity->name = $fbUser["first_name"];
+                    $identity->surname = $fbUser["last_name"];
+                });
+    }
+
+}
diff --git a/classes/social/GoogleLogin.php b/classes/login/GoogleLogin.php
similarity index 65%
rename from classes/social/GoogleLogin.php
rename to classes/login/GoogleLogin.php
index f2174d092a40fef77e5648783b5032d7d218feaa..82079aae58bb820a21216e121b8cff8007ed5a89 100644
--- a/classes/social/GoogleLogin.php
+++ b/classes/login/GoogleLogin.php
@@ -2,15 +2,13 @@
 
 namespace RAP;
 
-class GoogleLogin {
-
-    protected $locator;
+class GoogleLogin extends LoginHandler {
 
     public function __construct(Locator $locator) {
-        $this->locator = $locator;
+        parent::__construct($locator, Identity::GOOGLE);
     }
 
-    public function call() {
+    public function login() {
         // Retrieve Google configuration
 
         $Google = $this->locator->config->authenticationMethods->Google;
@@ -58,40 +56,19 @@ class GoogleLogin {
 
             $typedId = explode('/', $res->getResourceName())[1];
 
-            // Search if the user is already registered into RAP using the Google ID.
-            $user = $this->locator->getUserHandler()->findUserByIdentity(Identity::GOOGLE, $typedId);
-
-            $session = $this->locator->getSession();
-
-            if ($user === null) {
-                // Create new user
-                $user = new \RAP\User();
-
-                $identity = new Identity(Identity::GOOGLE);
-                $identity->email = $emailAddresses[0];
-                $identity->name = $name;
-                $identity->surname = $surname;
-                $identity->typedId = $typedId;
-
-                $user->addIdentity($identity);
-
-                $session->userToLogin = $user;
-                $session->save();
-
-                header('Location: ' . $this->locator->getBasePath() . '/tou-check');
-                die();
-            }
-
-            $this->locator->getAuditLogger()->info("LOGIN,Google," . $user->id);            
-            $this->locator->getCallbackHandler()->manageLoginRedirect($user, $session);
-
-            die();
+            return $this->onIdentityDataReceived($typedId, function($identity) use($emailAddresses, $name, $surname) {
+                        $identity->email = $emailAddresses[0];
+                        $identity->name = $name;
+                        $identity->surname = $surname;
+                    });
         } else {
             // Redirect to Google authorization URL for obtaining an access token
             $authUrl = $client->createAuthUrl();
             header('Location: ' . $authUrl);
             die();
         }
+
+        return null;
     }
 
 }
diff --git a/classes/login/LinkedInLogin.php b/classes/login/LinkedInLogin.php
new file mode 100644
index 0000000000000000000000000000000000000000..7f686ed2efc9bc040da6d8b52315d70d0dd3d3e2
--- /dev/null
+++ b/classes/login/LinkedInLogin.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace RAP;
+
+class LinkedInLogin extends LoginHandler {
+
+    public function __construct(Locator $locator) {
+        parent::__construct($locator, Identity::FACEBOOK);
+    }
+
+    public function login() {
+        // Retrieve LinkedIn configuration
+        $LinkedIn = $this->locator->config->authenticationMethods->LinkedIn;
+
+        $url = "https://www.linkedin.com/oauth/v2/authorization?response_type=code";
+        $url .= "&client_id=" . $LinkedIn->id;
+        $url .= "&redirect_uri=" . $LinkedIn->callback;
+        $url .= "&state=789654123";
+        $url .= "&scope=r_basicprofile r_emailaddress";
+
+        header("Location: $url");
+    }
+
+    public function retrieveToken() {
+        // Retrieve LinkedIn configuration
+        $LinkedIn = $this->locator->config->authenticationMethods->LinkedIn;
+
+        if (!isset($_REQUEST['code'])) {
+            die("Unable to get LinkedIn client code");
+        }
+
+        //create array of data to be posted to get AccessToken
+        $post_data = array(
+            'grant_type' => "authorization_code",
+            'code' => $_REQUEST['code'],
+            'redirect_uri' => $LinkedIn->callback,
+            'client_id' => $LinkedIn->id,
+            'client_secret' => $LinkedIn->secret
+        );
+
+        //traverse array and prepare data for posting (key1=value1)
+        foreach ($post_data as $key => $value) {
+            $post_items[] = $key . '=' . $value;
+        }
+
+        //create the final string to be posted
+        $post_string = implode('&', $post_items);
+
+        //create cURL connection
+        $conn1 = curl_init('https://www.linkedin.com/oauth/v2/accessToken');
+
+        //set options
+        curl_setopt($conn1, CURLOPT_CONNECTTIMEOUT, 30);
+        curl_setopt($conn1, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($conn1, CURLOPT_SSL_VERIFYPEER, true);
+        curl_setopt($conn1, CURLOPT_FOLLOWLOCATION, 1);
+
+        //set data to be posted
+        curl_setopt($conn1, CURLOPT_POSTFIELDS, $post_string);
+
+        //perform our request
+        $result1 = curl_exec($conn1);
+        $info1 = curl_getinfo($conn1);
+
+        if ($info1['http_code'] === 200) {
+            $my_token = json_decode($result1, TRUE);
+            $access_token = $my_token['access_token'];
+            $expires_in = $my_token['expires_in'];
+            curl_close($conn1);
+        } else {
+            //show information regarding the error
+            $errorMessage = "Error: LinkedIn server response code: " . $info1['http_code'] . " - ";
+            $errorMessage .= curl_error($conn1);
+            curl_close($conn1);
+            http_response_code(500);
+            die($errorMessage);
+        }
+
+        // Call to API
+        $conn2 = curl_init();
+        curl_setopt($conn2, CURLOPT_URL, "https://api.linkedin.com/v1/people/~:(first-name,last-name,email-address,id)?format=json");
+        curl_setopt($conn2, CURLOPT_HTTPHEADER, array(
+            'Authorization: Bearer ' . $access_token
+        ));
+
+        curl_setopt($conn2, CURLOPT_RETURNTRANSFER, true);
+        $result = curl_exec($conn2);
+        $info2 = curl_getinfo($conn2);
+
+        if ($info2['http_code'] === 200) {
+            $data = json_decode($result, TRUE);
+
+            curl_close($conn2);
+
+            if (isset($data['errorCode'])) {
+                $errorMessage = $data['message'];
+                die($errorMessage);
+            }
+
+            $typedId = $data['id'];
+
+            return $this->onIdentityDataReceived($typedId, function($identity) use($data) {
+                $identity->email = $data['emailAddress'];
+                $identity->name = $data['firstName'];
+                $identity->surname = $data['lastName'];
+            });
+        } else {
+            //show information regarding the error
+            $errorMessage = "Error: LinkedIn server response code: " . $info2['http_code'] . " - ";
+            $errorMessage = $errorMessage . curl_error($conn2);
+            curl_close($conn2);
+            die($errorMessage);
+        }
+
+        return null;
+    }
+
+}
diff --git a/classes/login/LoginHandler.php b/classes/login/LoginHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..245c9285c69c7eb39ad09540d3bbf2e98a87637e
--- /dev/null
+++ b/classes/login/LoginHandler.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace RAP;
+
+class LoginHandler {
+
+    protected $locator;
+    private $identityType;
+
+    public function __construct(Locator $locator, string $identityType) {
+        $this->locator = $locator;
+        $this->identityType = $identityType;
+    }
+
+    public function onIdentityDataReceived(string $typedId, \Closure $fillIdentityData): string {
+
+        $user = $this->locator->getUserHandler()->findUserByIdentity($this->identityType, $typedId);
+
+        if ($user === null) {
+            return $this->handleNewUser($typedId, $fillIdentityData);
+        }
+
+        return $this->getAfterLoginRedirect($user);
+    }
+
+    protected function handleNewUser(string $typedId, \Closure $fillIdentityData): string {
+
+        $session = $this->locator->getSession();
+
+        // Create new user
+        $user = new \RAP\User();
+
+        $identity = new Identity($this->identityType);
+        $identity->typedId = $typedId;
+        $fillIdentityData($identity);
+
+        $user->addIdentity($identity);
+
+        $session->setUser($user);
+
+        // Redirect to Term of Use acceptance page
+        return $this->locator->getBasePath() . '/tou-check';
+    }
+
+    public function getAfterLoginRedirect(User $user): string {
+
+        $session = $this->locator->getSession();
+        $this->locator->getAuditLogger()->info("LOGIN," . $this->identityType . "," . $user->id);
+
+        if ($session->getOAuth2Data() !== null) {
+            $session->setUser($user);
+            $redirectUrl = $this->locator->getOAuth2RequestHandler()->getCodeResponseUrl();
+            session_destroy();
+            return $redirectUrl;
+        }
+
+        if ($session->getAction() !== null) {
+            $session->setUser($user);
+            $action = $session->getAction();
+            if ($action === 'account' || $action === 'join' || $action === 'admin') {
+                return $this->locator->getBasePath() . '/' . $action;
+            }
+        }
+
+        throw new \Exception("Unable to find a proper redirect");
+    }
+
+}
diff --git a/classes/login/ShibbolethLogin.php b/classes/login/ShibbolethLogin.php
new file mode 100644
index 0000000000000000000000000000000000000000..2dc425c5e5adb20eb24c8bfda71a4ace9804f38d
--- /dev/null
+++ b/classes/login/ShibbolethLogin.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace RAP;
+
+class ShibbolethLogin extends LoginHandler {
+
+    public function __construct(Locator $locator) {
+        parent::__construct($locator, Identity::EDU_GAIN);
+    }
+
+    public function login() {
+        if (isset($_SERVER['Shib-Session-ID'])) {
+
+            // Retrieving eduPersonPrincipalName (eppn)
+            $eppn = $_SERVER['eppn'];
+
+            // Search if the user is already registered into RAP using the eppn.
+            // The persistent id should be a more appropriate identifier, however at IA2
+            // we need to import all INAF user into RAP, even if they will never register,
+            // and in that case we know only their eppn.
+
+            $this->onIdentityDataReceived($eppn, function($identity) use($eppn) {
+                $identity->email = $_SERVER['mail'];
+                $identity->name = $_SERVER['givenName'];
+                $identity->surname = $_SERVER['sn'];
+                $identity->eppn = $eppn;
+            });
+        } else {
+            http_response_code(500);
+            die("Shib-Session-ID not found!");
+        }
+    }
+
+}
diff --git a/classes/login/X509Login.php b/classes/login/X509Login.php
new file mode 100644
index 0000000000000000000000000000000000000000..87173dcce6ba4e35a0d9316c7636a880faec2184
--- /dev/null
+++ b/classes/login/X509Login.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace RAP;
+
+class X509Login extends LoginHandler {
+
+    private $x509Data;
+
+    public function __construct(Locator $locator) {
+        parent::__construct($locator, Identity::X509);
+    }
+
+    public function login(): string {
+        if (isset($_SERVER['SSL_CLIENT_VERIFY']) && isset($_SERVER['SSL_CLIENT_V_REMAIN']) &&
+                $_SERVER['SSL_CLIENT_VERIFY'] === 'SUCCESS' && $_SERVER['SSL_CLIENT_V_REMAIN'] > 0) {
+
+            $x509Data = RAP\X509Data::parse($_SERVER);
+            $this->x509Data = $x509Data;
+
+            return $this->onIdentityDataReceived($x509Data->serialNumber, function($identity) use ($x509Data) {
+                        $this->fillIdentity($identity, $x509Data);
+                    });
+        } else {
+            http_response_code(500);
+            die("Unable to verify client certificate");
+        }
+
+        return null;
+    }
+
+    public function afterNameSurnameSelection($x509Data) {
+        $redirect = $this->onIdentityDataReceived($x509Data->serialNumber, function($identity) use ($x509Data) {
+            $this->fillIdentity($identity, $x509Data);
+        });
+
+        $session = $this->locator->getSession();
+        $session->setX509DataToRegister(null);
+
+        return $redirect;
+    }
+
+    private function fillIdentity($identity, $x509Data) {
+        $identity->email = $x509Data->email;
+        $identity->name = $x509Data->name;
+        $identity->surname = $x509Data->surname;
+        $identity->institution = $x509Data->institution;
+    }
+
+    /**
+     * We want to extract name and surname from the X.509 certificate, however X.509
+     * puts name and surname together (inside the CN field).
+     * If name and surname are single words it is possible to retrieve them splitting
+     * on the space character, otherwise the user has to choose the correct combination.
+     * In that case partial X.509 data is temporarily stored into the user session and
+     * the page views/x509-name-surname.php is shown to the user before completing the
+     * registration, in order to allow him/her selecting the correct name and surname.
+     */
+    protected function handleNewUser(string $typedId, \Closure $fillIdentityData): string {
+
+        $session = $this->locator->getSession();
+
+        if ($this->x509Data->name === null) {
+            $session->setX509DataToRegister($this->x509Data);
+            return $this->locator->getBasePath() . '/x509-name-surname';
+        } else {
+            return parent::handleNewUser($typedId, $fillIdentityData);
+        }
+    }
+
+}
diff --git a/classes/AuthenticationMethods.php b/classes/model/AuthenticationMethods.php
similarity index 100%
rename from classes/AuthenticationMethods.php
rename to classes/model/AuthenticationMethods.php
diff --git a/classes/Identity.php b/classes/model/Identity.php
similarity index 100%
rename from classes/Identity.php
rename to classes/model/Identity.php
diff --git a/classes/model/InternalClient.php b/classes/model/InternalClient.php
index dfff4a31f8ff8c1ca346ae155bde1f09cfe314b3..36a81aa85f08e73f0844591ace4ae368964afc65 100644
--- a/classes/model/InternalClient.php
+++ b/classes/model/InternalClient.php
@@ -4,8 +4,11 @@ namespace RAP;
 
 class InternalClient extends RAPClient {
 
-    public function __construct() {
+    public $action;
+
+    public function __construct(string $action) {
         $this->authMethods = AuthenticationMethods::getAllMethods();
+        $this->action = $action;
     }
 
     public function getIconBasePath() {
diff --git a/classes/model/SessionData.php b/classes/model/SessionData.php
index ee0e0bc000ce46c56e58dafbecc0d28c37a4871b..1473ce824695b2bc5e6ec34f6c7f28977bae4a1c 100644
--- a/classes/model/SessionData.php
+++ b/classes/model/SessionData.php
@@ -32,20 +32,18 @@ class SessionData {
 
     const KEY = "SessionData";
 
-    public $user;
-    public $userSearchResults;
-    public $x509DataToRegister;
-    // user which is going to perform the login (we need to store this in the
-    // session because we need to check the Terms of Use user consensus, so we
-    // redirect to another page after retrieving user data.
-    public $userToLogin;
-    public $oauth2Data;
+    private $user;
+    private $x509DataToRegister;
+    private $oauth2Data;
+    private $action;
 
-    /**
-     * Store the data into the $_SESSION PHP variable
-     */
-    public function save() {
-        $_SESSION[SessionData::KEY] = $this;
+    public function setUser(?User $user): void {
+        $this->user = $user;
+        $this->save();
+    }
+
+    public function getUser(): ?User {
+        return $this->user;
     }
 
     /**
@@ -54,13 +52,23 @@ class SessionData {
      * the database.
      * @param int $identityId
      */
-    public function updatePrimaryIdentity($identityId) {
+    public function updatePrimaryIdentity($identityId): void {
         foreach ($this->user->identities as $identity) {
             $identity->primary = ($identity->id === $identityId);
         }
+        $this->save();
+    }
+
+    public function setX509DataToRegister(?X509Data $x509DataToRegister): void {
+        $this->x509DataToRegister = $x509DataToRegister;
+        $this->save();
+    }
+
+    public function getX509DataToRegister(): ?X509Data {
+        return $this->x509DataToRegister;
     }
 
-    public function setOAuth2Data(?OAuth2Data $oauth2Data) {
+    public function setOAuth2Data(?OAuth2Data $oauth2Data): void {
         $this->oauth2Data = $oauth2Data;
         $this->save();
     }
@@ -69,4 +77,20 @@ class SessionData {
         return $this->oauth2Data;
     }
 
+    public function setAction(?string $action): void {
+        $this->action = $action;
+        $this->save();
+    }
+
+    public function getAction(): ?string {
+        return $this->action;
+    }
+
+    /**
+     * Store the data into the $_SESSION PHP variable
+     */
+    public function save() {
+        $_SESSION[SessionData::KEY] = $this;
+    }
+
 }
diff --git a/config-example.json b/config-example.json
index 36e73d850e28a665f4f4fd97bcca19645b900a01..92f9edd77142e67dddafcb3445562328ee1ecc80 100644
--- a/config-example.json
+++ b/config-example.json
@@ -32,7 +32,7 @@
             "callback": "/auth/social/linkedin_token.php"
         },
         "X.509": {},
-        "DirectIdP": {
+        "LocalIdP": {
             "url": "https://sso.ia2.inaf.it/Shibboleth.sso/Login?entityID=https://sso.ia2.inaf.it/idp/shibboleth&target=https://sso.ia2.inaf.it/rap-ia2/auth/saml2/aai.php",
             "logo": "img/ia2-logo-60x60.png",
             "logo_alt": "IA2 logo",
diff --git a/include/admin.php b/include/admin.php
index 4479012f37a57599f24f2b8a3f76ed534f56352f..b8814ce96dca381611b7684c1805f945c072cdd2 100644
--- a/include/admin.php
+++ b/include/admin.php
@@ -10,7 +10,7 @@ function checkUser() {
     startSession();
 
     global $session;
-    if ($session->user === null) {
+    if ($session->getUser() === null) {
         http_response_code(401);
         die("You must be registered to perform this action");
     }
diff --git a/include/front-controller.php b/include/front-controller.php
index 818c1167f369ceaab858b4495d73796889d9f28f..ae907a1b8a5bd04e1c5907bcd1b93248571286c4 100644
--- a/include/front-controller.php
+++ b/include/front-controller.php
@@ -27,11 +27,24 @@ Flight::route('/', function() {
     global $locator;
 
     $action = Flight::request()->query['action'];
+    $locator->getSession()->setAction($action);
 
     switch ($action) {
         case "oaut2client":
             $clientId = $locator->getSession()->getOAuth2Data()->clientId;
-            $client = $locator->getDAO()->getOAuth2ClientByClientId($clientId);
+            $client = $locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($clientId);
+            $authPageModel = new \RAP\AuthPageModel($locator, $client);
+            renderMainPage($authPageModel);
+            break;
+        case "account":
+            $client = new \RAP\InternalClient('account');
+            $client->icon = 'account-manager.png';
+            $client->title = 'Account Management';
+            $authPageModel = new \RAP\AuthPageModel($locator, $client);
+            renderMainPage($authPageModel);
+            break;
+        case "admin":
+            $client = new \RAP\InternalClient('admin');
             $authPageModel = new \RAP\AuthPageModel($locator, $client);
             renderMainPage($authPageModel);
             break;
@@ -90,7 +103,7 @@ Flight::route('POST /auth/oauth2/check_token', function() {
     global $locator;
 
     $token = filter_input(INPUT_POST, 'token', FILTER_SANITIZE_STRING);
-    
+
     $requestHandler = new \RAP\OAuth2RequestHandler($locator);
     $result = $requestHandler->handleCheckTokenRequest($token);
 
@@ -125,28 +138,57 @@ Flight::route('/auth/social/google', function() {
     session_start();
     global $locator;
     $googleLogin = new \RAP\GoogleLogin($locator);
-    $googleLogin->call();
+    $redirect = $googleLogin->login();
+    if ($redirect !== null) {
+        Flight::redirect($redirect);
+    }
+});
+
+Flight::route('/auth/social/facebook', function() {
+    session_start();
+    global $locator;
+    $facebookLogin = new \RAP\FacebookLogin($locator);
+    $facebookLogin->login();
 });
 
-Flight::route('/facebook', function() {
-    sendAuthRedirect('/auth/social/facebook_login.php');
+Flight::route('/auth/social/facebook/token', function() {
+    session_start();
+    global $locator;
+    $facebookLogin = new \RAP\FacebookLogin($locator);
+    $facebookLogin->retrieveToken();
 });
 
-Flight::route('/linkedIn', function() {
-    sendAuthRedirect('/auth/social/linkedin_login.php');
+Flight::route('/auth/social/linkedIn', function() {
+    session_start();
+    global $locator;
+    $linkedInLogin = new \RAP\LinkedInLogin($locator);
+    $linkedInLogin->login();
 });
 
-Flight::route('/eduGAIN', function() {
-    sendAuthRedirect('/auth/saml2/aai.php');
+Flight::route('/auth/social/linkedIn/token', function() {
+    session_start();
+    global $locator;
+    $linkedInLogin = new \RAP\LinkedInLogin($locator);
+    $linkedInLogin->retrieveToken();
 });
 
-Flight::route('/x509', function() {
-    sendAuthRedirect('/auth/x509/certlogin.php');
+Flight::route('/auth/eduGAIN', function() {
+    session_start();
+    global $locator;
+    $shibbolethLogin = new \RAP\ShibbolethLogin($locator);
+    $shibbolethLogin->login();
+});
+
+Flight::route('/auth/x509', function() {
+    session_start();
+    global $locator;
+    $x509Login = new \RAP\X509Login($locator);
+    $x509Login->login();
 });
 
 Flight::route('/local', function() {
     global $AUTHENTICATION_METHODS;
-    sendAuthRedirect($AUTHENTICATION_METHODS['DirectIdP']['url']);
+    sendAuthRedirect($AUTHENTICATION_METHODS['LocalIdP']['url']);
 });
 
 /**
@@ -158,11 +200,11 @@ Flight::route('GET /x509-name-surname', function() {
     startSession();
     global $session, $BASE_PATH, $VERSION;
 
-    if ($session->x509DataToRegister !== null && $session->x509DataToRegister->name === null) {
+    if ($session->getX509DataToRegister() !== null && $session->getX509DataToRegister()->name === null) {
         Flight::render('x509-name-surname.php', array('title' => 'Select name and surname',
             'version' => $VERSION,
-            'fullName' => $session->x509DataToRegister->fullName,
-            'candidateNames' => $session->x509DataToRegister->candidateNames));
+            'fullName' => $session->getX509DataToRegister()->fullName,
+            'candidateNames' => $session->getX509DataToRegister()->candidateNames));
     } else {
         // Redirect to index
         header("Location: " . $BASE_PATH);
@@ -172,63 +214,79 @@ Flight::route('GET /x509-name-surname', function() {
 
 /**
  * Complete the X.509 registration selecting the correct name and surname specified
- * by the user.
+ * by the user (special case of composite names).
  */
 Flight::route('POST /submit-x509-name', function() {
 
+    session_start();
+
     $selectedNameIndex = Flight::request()->data['selected-name'];
 
-    error_log('index=' . $selectedNameIndex);
+    global $locator;
+    $session = $locator->getSession();
 
-    startSession();
-    global $session, $BASE_PATH;
-
-    if ($session->x509DataToRegister !== null) {
-        $session->x509DataToRegister->selectCandidateName($selectedNameIndex);
-        $session->userToLogin = $session->x509DataToRegister->toUser();
-        $session->x509DataToRegister = null;
-        $session->save();
-        header("Location: " . $BASE_PATH . '/tou-check');
-        die();
+    if ($session->getX509DataToRegister() !== null) {
+        $session->getX509DataToRegister()->selectCandidateName($selectedNameIndex);
+        $loginHandler = new \RAP\X509Login($locator);
+        $redirect = $loginHandler->afterNameSurnameSelection($session->getX509DataToRegister());
+        Flight::redirect($redirect);
     } else {
         die('X.509 data not returned');
     }
 });
 
+/**
+ * Display Term of Use acceptance page.
+ */
 Flight::route('GET /tou-check', function() {
 
     session_start();
     global $locator;
 
-    if ($locator->getSession()->userToLogin === null) {
+    if ($locator->getSession()->getUser() === null) {
         die("User data not retrieved.");
     } else {
         Flight::render('tou-check.php', array('title' => 'Terms of Use acceptance',
-            'user' => $locator->getSession()->userToLogin,
+            'user' => $locator->getSession()->getUser(),
             'version' => $locator->getVersion(),
             'registration_url' => $locator->getBasePath() . '/register'));
     }
 });
 
+/**
+ * Stores the user data into the database after he/she accepted the Terms of Use.
+ */
 Flight::route('GET /register', function() {
 
     session_start();
     global $locator;
 
-    if ($locator->getSession()->userToLogin === null) {
+    if ($locator->getSession()->user === null) {
         die("User data not retrieved.");
     } else {
 
-        $session = $locator->getSession();
-        
-        $user = $session->userToLogin;
+        $user = $locator->getSession()->user;
+
         $locator->getUserHandler()->saveUser($user);
 
-        $session->userToLogin = null;
-        $session->save();
+        $loginHandler = new \RAP\LoginHandler($locator, $user->identities[0]->type);
+        Flight::redirect($loginHandler->getAfterLoginRedirect($user));
+    }
+});
+
+/**
+ * Shows Account Management page.
+ */
+Flight::route('GET /account', function () {
+
+    session_start();
+    global $locator;
 
-        $locator->getAuditLogger()->info("LOGIN," . $user->identities[0]->type . "," . $user->id);
-        $locator->getCallbackHandler()->manageLoginRedirect($user, $session);
+    if ($locator->getSession()->getUser() === null) {
+        Flight::redirect('/');
+    } else {
+        Flight::render('account-management.php', array('title' => 'RAP Account Management',
+            'version' => $locator->getVersion(), 'session' => $locator->getSession()));
     }
 });
 
diff --git a/include/gui-backend.php b/include/gui-backend.php
index 5cc8dcf023202197c9d9239846bf59b80db13a34..0c311193dae02ca84dcd2ca00ce3f9f56292b7c5 100644
--- a/include/gui-backend.php
+++ b/include/gui-backend.php
@@ -10,31 +10,12 @@ function checkSession() {
     startSession();
 
     global $session;
-    if ($session->user === null) {
+    if ($session->getUser() === null) {
         http_response_code(401);
         die("You must be registered to perform this action");
     }
 }
 
-/**
- * This is called when an user search for other users into the join modal dialog.
- */
-Flight::route('GET /user', function() {
-
-    checkSession();
-    global $session;
-
-    $searchText = Flight::request()->query['search'];
-    $session->searchUser($searchText);
-
-    $jsRes = [];
-    foreach ($session->userSearchResults as $searchResult) {
-        array_push($jsRes, $searchResult->userDisplayText);
-    }
-
-    Flight::json($jsRes);
-});
-
 Flight::route('POST /join', function() {
 
     checkSession();
@@ -69,6 +50,6 @@ Flight::route('POST /primary-identity', function() {
     $session->updatePrimaryIdentity($identityId);
 
     // Following variable is used to render user-data
-    $user = $session->user;
+    $user = $session->getUser();
     include 'user-data.php';
 });
diff --git a/include/init.php b/include/init.php
index e0c98813b73b7cacc6dbc691e3838487d65e2bc0..de9808cdb95cc9798dc81d7ee64f9c945cc11aea 100644
--- a/include/init.php
+++ b/include/init.php
@@ -36,7 +36,7 @@ spl_autoload_register(function ($class) {
 
         $fileName = substr($class, $len, strlen($class) - $len);
 
-        $classDirectories = ['/', '/social/', '/exceptions/', '/datalayer/', '/model/'];
+        $classDirectories = ['/', '/login/', '/exceptions/', '/datalayer/', '/model/'];
         foreach ($classDirectories as $directory) {
             $classpath = ROOT . '/classes' . $directory . $fileName . '.php';
             if (file_exists($classpath)) {
diff --git a/index.php b/index.php
index 653a6ef0f2dad64ea7a895d6b0e45afd6918c2e0..485f948484f111466c27fd32bd362fc971b1f4b2 100644
--- a/index.php
+++ b/index.php
@@ -29,12 +29,16 @@ include './include/gui-backend.php';
 include './include/rest-web-service.php';
 
 // Error handling
-Flight::map('error', function(Exception $ex) {
+Flight::map('error', function(\Exception $ex) {
     if ($ex instanceof \RAP\BadRequestException) {
         http_response_code(400);
         echo "Bad request: " . $ex->message;
     } else {
-        echo $ex->getTraceAsString();
+        if ($ex->getMessage() !== null) {
+            echo $ex->getMessage();
+        } else {
+            echo $ex->getTraceAsString();
+        }
     }
 });
 
diff --git a/tests/IdTokenBuilderTest.php b/tests/IdTokenBuilderTest.php
index aa79a567ea97486baa3583169eec7eb4fef9b385..67a2596bf4f98a63bf97d4ebff53f5d5efc6a151 100644
--- a/tests/IdTokenBuilderTest.php
+++ b/tests/IdTokenBuilderTest.php
@@ -27,8 +27,8 @@ final class IdTokenBuilderTest extends TestCase {
         $identity->institution = "INAF";
         $user->addIdentity($identity);
 
-        $daoStub = $this->createMock(\RAP\DAO::class);
-        $locatorStub->method('getDAO')->willReturn($daoStub);
+        $daoStub = $this->createMock(\RAP\UserDAO::class);
+        $locatorStub->method('getUserDAO')->willReturn($daoStub);
         $daoStub->method('findUserById')->willReturn($user);
 
         $locatorStub->config = json_decode('{"jwtIssuer": "issuer"}');
diff --git a/tests/OAuth2RequestHandlerTest.php b/tests/OAuth2RequestHandlerTest.php
index 8095f1b26cd0edfcf851b892dee178b320c65883..216c4d17c1674b0732e0ccb54535ecde3b51db0f 100644
--- a/tests/OAuth2RequestHandlerTest.php
+++ b/tests/OAuth2RequestHandlerTest.php
@@ -30,11 +30,11 @@ final class OAuth2RequestHandlerTest extends TestCase {
             "scope" => "email%20profile"
         ];
 
-        $daoStub = $this->createMock(\RAP\DAO::class);
+        $daoStub = $this->createMock(\RAP\OAuth2ClientDAO::class);
         $daoStub->method('getOAuth2ClientByClientId')->willReturn(new \RAP\OAuth2Client());
 
         $locatorStub = $this->createMock(\RAP\Locator::class);
-        $locatorStub->method('getDAO')->willReturn($daoStub);
+        $locatorStub->method('getOAuth2ClientDAO')->willReturn($daoStub);
 
         $requestHandler = new \RAP\OAuth2RequestHandler($locatorStub);
         $requestHandler->handleAuthorizeRequest($params);
@@ -50,7 +50,7 @@ final class OAuth2RequestHandlerTest extends TestCase {
             "scope" => "email%20profile"
         ];
 
-        $daoStub = $this->createMock(\RAP\DAO::class);
+        $daoStub = $this->createMock(\RAP\OAuth2ClientDAO::class);
         $client = new \RAP\OAuth2Client();
         $client->redirectUrl = "redirect_uri";
         $daoStub->method('getOAuth2ClientByClientId')->willReturn($client);
@@ -58,7 +58,7 @@ final class OAuth2RequestHandlerTest extends TestCase {
         $sessionStub = $this->createMock(\RAP\SessionData::class);
 
         $locatorStub = $this->createMock(\RAP\Locator::class);
-        $locatorStub->method('getDAO')->willReturn($daoStub);
+        $locatorStub->method('getOAuth2ClientDAO')->willReturn($daoStub);
         $locatorStub->method('getSession')->willReturn($sessionStub);
 
         $sessionStub->expects($this->once())
diff --git a/views/account-management.php b/views/account-management.php
index d985159160428c526fe0ca167bd94ef5c2896d61..f6b28308f6dc13c209a16c9535c915806576f7b4 100644
--- a/views/account-management.php
+++ b/views/account-management.php
@@ -20,7 +20,7 @@ include 'include/header.php';
             </div>
             <div class="panel-body">
                 <?php
-                $user = $session->user;
+                $user = $session->getUser();
                 include 'include/user-data.php';
                 ?>
             </div>
diff --git a/views/services-list.php b/views/services-list.php
index f74215ce00e839b8c09ca32526d3d97efacf8e05..bc441d1b58de1d346b41defcf4591ce24f6e872b 100644
--- a/views/services-list.php
+++ b/views/services-list.php
@@ -20,10 +20,7 @@ include 'include/header.php';
             </form>
         </li>
         <li>
-            <form action="<?php echo $action; ?>" method="POST">
-                <input name="callback" type="hidden" value="<?php echo $action; ?>" />
-                <input type="submit" class="btn btn-link" value="RAP Account Management" />
-            </form>
+            <a href="?action=account">RAP Account Management</a>
         </li>
         <li>
             <form action="<?php echo $action; ?>" method="POST">