diff --git a/.gitignore b/.gitignore
index ca46cded4f38e74bc74aa61ca19405b48d59a3c0..5287a346274b7fb0fcba96b863751747a91784a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
 vendor
 composer.lock
 nbproject
-
+logs
diff --git a/classes/DAO.php b/classes/DAO.php
index 72255197bcacb800e7f63a0bdf8934272f3069b7..ccbabd8da1301d281064c2621212f34ff0d22d89 100644
--- a/classes/DAO.php
+++ b/classes/DAO.php
@@ -24,61 +24,52 @@
 
 namespace RAP;
 
-use PDO;
+abstract class DAO {
 
-class DAO {
+    public abstract function getDBHandler();
 
-    private static function getDBHandler() {
-        global $PDO;
-        return new PDO($PDO['connection_string'], $PDO['user'], $PDO['password']);
-    }
-
-    public static function insertLogin($user) {
-
-        global $log;
-
-        $token = bin2hex(openssl_random_pseudo_bytes(16)); // http://stackoverflow.com/a/18890309/771431
-
-        $dbh = DAO::getDBHandler();
-
-        $stmt = $dbh->prepare("INSERT INTO token (token, data) VALUES(:token, :data)");
+    public abstract function insertTokenData($token, $data);
 
-        $params = array(
-            ':token' => $token,
-            ':data' => json_encode($user)
-        );
+    public abstract function findTokenData($token);
 
-        if ($stmt->execute($params)) {
-            return $token;
-        } else {
-            $log->error($stmt->errorInfo()[2]);
-            throw new \Exception("SQL error while storing user token");
-        }
-    }
+    public abstract function deleteToken($token);
 
-    public static function getTokenData($token) {
+    /**
+     * Return the new identity ID.
+     */
+    public abstract function insertIdentity(Identity $identity, $userId);
 
-        $dbh = DAO::getDBHandler();
+    /**
+     * Return the new user ID.
+     */
+    public abstract function createUser();
 
-        $stmt = $dbh->prepare("SELECT data FROM token WHERE token = :token AND CURRENT_TIMESTAMP < TIMESTAMPADD(MINUTE,1,creation_time)");
-        $stmt->bindParam(':token', $token);
+    public abstract function findUserById($userId);
+    
+    /**
+     * 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
+     * @param type $dbIdentifier identifier of the database (used only for local identities)
+     */
+    public abstract function findUserByIdentity($type, $identifier, $dbIdentifier);
 
-        $stmt->execute();
+    public abstract function addEmailToUser($email, $userId);
 
-        foreach ($stmt->fetchAll() as $row) {
-            return $row['data'];
-        }
+    public $config;
 
-        return null;
+    public function __construct($config) {
+        $this->config = $config;
     }
 
-    public static function deleteToken($token) {
-
-        $dbh = DAO::getDBHandler();
-
-        $stmt = $dbh->prepare("DELETE FROM token WHERE token = :token");
-        $stmt->bindParam(':token', $token);
-        $stmt->execute();
+    public static function get() {
+        $config = parse_ini_file(ROOT . '/config.ini', true);
+        switch ($config['dbtype']) {
+            case 'MySQL':
+                return new MySQLDAO($config);
+            default:
+                throw new \Exception($config['dbtype'] . ' not supported yet');
+        }
     }
 
 }
diff --git a/classes/Identity.php b/classes/Identity.php
new file mode 100644
index 0000000000000000000000000000000000000000..e792c17a3649ea5d46dc73ad0b8363e494f02e63
--- /dev/null
+++ b/classes/Identity.php
@@ -0,0 +1,108 @@
+<?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;
+
+class Identity {
+
+    const EDU_GAIN = "eduGAIN";
+    const X509 = "X.509";
+    const GOOGLE = "Google";
+    const FACEBOOK = "Facebook";
+    const LINKEDIN = "LinkedIn";
+    const LOCAL = "Local";
+
+    private static $ALLOWED_TYPES = [Identity::EDU_GAIN, Identity::X509, Identity::GOOGLE, Identity::FACEBOOK, Identity::LINKEDIN, Identity::LOCAL];
+
+    /**
+     * Identity id in the database. Mandatory field.
+     */
+    public $id;
+
+    /**
+     * One of the types specified above. Mandatory field.
+     */
+    private $type;
+
+    /**
+     * Data related to specific account type (shibboleth persistent id, facebook id, etc, ...). Mandatory field.
+     */
+    public $typedId;
+
+    /**
+     * Primary email related to this identity. Mandatory field.
+     * User can have additional email addresses. These are stored into User class.
+     */
+    public $email;
+
+    /**
+     * Unique identifier for local user database (specified in configuration file).
+     */
+    public $localDBId;
+
+    /**
+     * First name
+     */
+    public $name;
+
+    /**
+     * Last name / Family name
+     */
+    public $surname;
+
+    /**
+     * Institution / Organization. Not mandatory.
+     */
+    public $institution;
+
+    /**
+     * For local identities.
+     */
+    public $username;
+
+    /**
+     * For eduGAIN identities.
+     */
+    public $eppn;
+
+    public function __construct($userType) {
+        $isAllowedType = false;
+        foreach (Identity::$ALLOWED_TYPES as $type) {
+            if ($userType === $type) {
+                $isAllowedType = true;
+                break;
+            }
+        }
+        if (!$isAllowedType) {
+            throw new \Exception($userType . " is not a supported user type");
+        }
+
+        $this->type = $userType;
+    }
+
+    public function getType() {
+        return $this->type;
+    }
+
+}
diff --git a/classes/MySQLDAO.php b/classes/MySQLDAO.php
new file mode 100644
index 0000000000000000000000000000000000000000..e484c927b3e5d72fb5abf7ac955907ba40a7eb9b
--- /dev/null
+++ b/classes/MySQLDAO.php
@@ -0,0 +1,186 @@
+<?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;
+
+use PDO;
+
+class MySQLDAO extends DAO {
+
+    public function getDBHandler() {
+        $connectionString = "mysql:host=" . $this->config['hostname'] . ";dbname=" . $this->config['dbname'];
+        return new PDO($connectionString, $this->config['username'], $this->config['password']);
+    }
+
+    public function insertTokenData($token, $data) {
+
+        global $log;
+
+        $dbh = $this->getDBHandler();
+        $stmt = $dbh->prepare("INSERT INTO token (token, data) VALUES(:token, :data)");
+
+        $params = array(
+            ':token' => $token,
+            ':data' => $data
+        );
+
+        if ($stmt->execute($params)) {
+            return $token;
+        } else {
+            $log->error($stmt->errorInfo()[2]);
+            throw new \Exception("SQL error while storing user token");
+        }
+    }
+
+    public function findTokenData($token) {
+
+        $dbh = $this->getDBHandler();
+
+        $stmt = $dbh->prepare("SELECT data FROM token WHERE token = :token AND CURRENT_TIMESTAMP < TIMESTAMPADD(MINUTE,1,creation_time)");
+        $stmt->bindParam(':token', $token);
+
+        $stmt->execute();
+
+        foreach ($stmt->fetchAll() as $row) {
+            return $row['data'];
+        }
+
+        return null;
+    }
+
+    public function deleteToken($token) {
+
+        $dbh = $this->getDBHandler();
+
+        $stmt = $dbh->prepare("DELETE FROM token WHERE token = :token");
+        $stmt->bindParam(':token', $token);
+        $stmt->execute();
+    }
+
+    public function insertIdentity(Identity $identity, $userId) {
+
+        $dbh = $this->getDBHandler();
+
+        $stmt = $dbh->prepare("INSERT INTO identity(`user_id`, `type`, `email`, `name`, `surname`, `institution`, `username`, `local_db_id`, `typed_id`, `eppn`)"
+                . " VALUES(:user_id, :type, :email, :name, :surname, :institution, :username, :local_db_id, :typed_id, :eppn)");
+
+        $stmt->bindParam(':user_id', $userId);
+        $stmt->bindParam(':type', $identity->getType());
+        $stmt->bindParam(':email', $identity->email);
+        $stmt->bindParam(':name', $identity->name);
+        $stmt->bindParam(':surname', $identity->surname);
+        $stmt->bindParam(':institution', $identity->institution);
+        $stmt->bindParam(':username', $identity->username);
+        $stmt->bindParam(':local_db_id', $identity->localDBId);
+        $stmt->bindParam(':typed_id', $identity->typedId);
+        $stmt->bindParam(':eppn', $identity->eppn);
+
+        $stmt->execute();
+
+        return $dbh->lastInsertId();
+    }
+
+    public function createUser() {
+
+        $dbh = $this->getDBHandler();
+
+        $stmt = $dbh->prepare("INSERT INTO user() VALUES()");
+        $stmt->execute();
+
+        return $dbh->lastInsertId();
+    }
+
+    public function findUserById($userId) {
+
+        $dbh = $this->getDBHandler();
+
+        $stmt = $dbh->prepare("SELECT `id`, `type`, `typed_id`, `email`, `local_db_id`, `name`, `surname`, `institution`, `username`, `eppn`"
+                . " FROM identity WHERE user_id = :user_id");
+
+        $stmt->bindParam(':user_id', $userId);
+        $stmt->execute();
+
+        $user = new User();
+        $user->id = $userId;
+
+        foreach ($stmt->fetchAll() as $row) {
+            $identity = new Identity($row['type']);
+            $identity->id = $row['id'];
+            $identity->typedId = $row['typed_id'];
+            $identity->email = $row['email'];
+            $identity->localDBId = $row['local_db_id'];
+            $identity->name = $row['name'];
+            $identity->surname = $row['surname'];
+            $identity->institution = $row['institution'];
+            $identity->username = $row['username'];
+            $identity->eppn = $row['eppn'];
+            $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 findUserByIdentity($type, $identifier, $dbIdentifier) {
+
+        $dbh = $this->getDBHandler();
+
+        $stmt = $dbh->prepare("SELECT user_id FROM identity WHERE type = :type AND typed_id = :typed_id AND local_db_id = :local_db_id");
+        $stmt->bindParam(':type', $type);
+        $stmt->bindParam(':typed_id', $identifier);
+        $stmt->bindParam(':local_db_id', $dbIdentifier);
+
+        $stmt->execute();
+
+        $result = $stmt->fetchAll();
+
+        if (count($result) === 0) {
+            return null;
+        }
+        if (count($result) > 1) {
+            throw new Exception("Found multiple users associated to the same identity!");
+        }
+
+        $userId = $result[0]['user_id'];
+        return $this->findUserById($userId);
+    }
+
+    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();
+    }
+
+}
diff --git a/classes/SessionData.php b/classes/SessionData.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6d807741a67a3433cfad618e6c567d136e87590
--- /dev/null
+++ b/classes/SessionData.php
@@ -0,0 +1,44 @@
+<?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;
+
+class SessionData {
+
+    public $callback;
+    public $user;
+
+    public function save() {
+        $_SESSION['SessionData'] = $this;
+    }
+
+    public static function get() {
+
+        if (!isset($_SESSION['SessionData'])) {
+            $session = new SessionData();
+            $session->save();
+        }
+        return $_SESSION['SessionData'];
+    }
+}
diff --git a/classes/TokenHandler.php b/classes/TokenHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..c096a01710e9f52969e21bb8f25a311cd34804fc
--- /dev/null
+++ b/classes/TokenHandler.php
@@ -0,0 +1,42 @@
+<?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;
+
+class TokenHandler {
+
+    public static function createNewToken($data) {
+        $token = bin2hex(openssl_random_pseudo_bytes(16)); // http://stackoverflow.com/a/18890309/771431
+        DAO::get()->insertTokenData($token, $data);
+    }
+
+    public static function deleteToken($token) {
+        DAO::get()->deleteToken($token);
+    }
+
+    public static function getUserData($token) {
+        return DAO::get()->findTokenData($token);
+    }
+
+}
diff --git a/classes/User.php b/classes/User.php
new file mode 100644
index 0000000000000000000000000000000000000000..220c6f088c4a28f7e851ff708f712f072e37d2c3
--- /dev/null
+++ b/classes/User.php
@@ -0,0 +1,46 @@
+<?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;
+
+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);
+    }
+
+}
diff --git a/classes/UserHandler.php b/classes/UserHandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..bb3bb3eceb9efa616702bcfb4682df5e08a233ba
--- /dev/null
+++ b/classes/UserHandler.php
@@ -0,0 +1,55 @@
+<?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;
+
+class UserHandler {
+
+    public static function saveUser(User $user) {
+
+        $dao = DAO::get();
+
+        if ($user->id === null) {
+            $user->id = $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)) {
+                
+            }
+        }
+    }
+
+    public static function findUserByIdentity($type, $identifier, $dbIdentifier) {
+
+        return DAO::get()->findUserByIdentity($type, $identifier, $dbIdentifier);
+    }
+
+}
diff --git a/config.ini b/config.ini
new file mode 100644
index 0000000000000000000000000000000000000000..2065065c528b35849d8c65f91530a3e4de6b91f8
--- /dev/null
+++ b/config.ini
@@ -0,0 +1,8 @@
+
+;connection_string = mysql:host=localhost;dbname=rap"
+dbtype = MySQL
+hostname = localhost
+port = 3306
+username = rap
+password = ***REMOVED***
+dbname = rap
diff --git a/img/eduGain-200.png b/img/eduGain-200.png
new file mode 100755
index 0000000000000000000000000000000000000000..5235aa8fcd435d54d4e4bec1374f6aa151a0c3d4
Binary files /dev/null and b/img/eduGain-200.png differ
diff --git a/img/facebook-60.png b/img/facebook-60.png
new file mode 100755
index 0000000000000000000000000000000000000000..a1badec4e1c2c5186f25c51590890bfa96cb183b
Binary files /dev/null and b/img/facebook-60.png differ
diff --git a/img/google-60.png b/img/google-60.png
new file mode 100755
index 0000000000000000000000000000000000000000..fe63b937d17bc06b18ee1a3f401231dfd9989d32
Binary files /dev/null and b/img/google-60.png differ
diff --git a/img/linkedin-60.png b/img/linkedin-60.png
new file mode 100755
index 0000000000000000000000000000000000000000..0d8772a6e85a1e0d6c5eb314a1a07b0dc0283740
Binary files /dev/null and b/img/linkedin-60.png differ
diff --git a/img/x509-200.png b/img/x509-200.png
new file mode 100755
index 0000000000000000000000000000000000000000..e1921f8ec165cb5cf527ef374e6f372466f69a2e
Binary files /dev/null and b/img/x509-200.png differ
diff --git a/include/footer.php b/include/footer.php
new file mode 100644
index 0000000000000000000000000000000000000000..9943ff0f851844f5daaccd7e1adca02a193936dc
--- /dev/null
+++ b/include/footer.php
@@ -0,0 +1,3 @@
+</div>
+</body>
+</html>
diff --git a/include/header.php b/include/header.php
new file mode 100644
index 0000000000000000000000000000000000000000..92c810d6804db1bcd19937e8f5a83fd149166a48
--- /dev/null
+++ b/include/header.php
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title><?php echo $title; ?></title>
+        <!-- jQuery and Bootstrap -->
+        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" />
+        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous" />
+        <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>
+    </head>
+    <body>
+        <div class="container">
diff --git a/include/init.php b/include/init.php
index 1bfefedc717e87d27ac854be36bcc7a8a2875427..e6124cf334fb2bd854a60655bfc38b845626d3c6 100644
--- a/include/init.php
+++ b/include/init.php
@@ -39,3 +39,6 @@ include ROOT . '/config.php';
 
 $log = new Monolog\Logger('mainLogger');
 $log->pushHandler(new Monolog\Handler\StreamHandler($LOG_PATH, $LOG_LEVEL));
+
+session_start();
+$session = RAP\SessionData::get();
diff --git a/index.php b/index.php
index f1521219932ad13e3b9f2a52aff2eece9c996484..8241bc3ffe5b8cc06cb4b91d4b73845a5d01e16d 100644
--- a/index.php
+++ b/index.php
@@ -27,18 +27,28 @@ include './include/init.php';
 /**
  * REST Web Service using http://flightphp.com/
  */
-Flight::route('/', function() {
+Flight::route('/demo', function() {
     $callback = (isset($_SERVER['HTTPS']) ? "https" : "http") . '://' . $_SERVER['HTTP_HOST'] . "/rap-service/user-info";
     Flight::render('demo.php', array('callback' => $callback));
 });
 
-Flight::route('POST /google', function() {
+Flight::route('/', function() {
+    global $session;
+    Flight::render('index.php', array('title' => 'RAP', 'session' => $session));
+});
+
+Flight::route('GET /logout', function() {
+    session_destroy();
+    Flight::redirect('/');
+});
+
+Flight::route('/google', function() {
+    global $session;
+
     $callback = Flight::request()->data['callback'];
-    if (!isset($callback)) {
-        throw new Exception("Callback URL not set!");
-    }
-    session_start();
-    $_SESSION['rap_callback'] = $callback;
+    $session->callback = isset($callback) ? $callback : null;
+    $session->save();
+
     Flight::redirect('/oauth2/google_token.php');
 });
 
@@ -68,4 +78,17 @@ Flight::route('GET /user-info', function() {
     echo $userData;
 });
 
+Flight::route('GET /user/@userId', function($userId) {
+
+    $user = RAP\DAO::get()->findUserById($userId);
+    if ($user !== null) {
+        global $log;
+        $log->debug(count($user->identities));
+        echo json_encode($user);
+    } else {
+        http_response_code(404);
+        die("User not found");
+    }
+});
+
 Flight::start();
diff --git a/oauth2/google_token.php b/oauth2/google_token.php
index b0d58007e8917d81575da1dcde3c18728f6bb75d..553bbb03666660be22538b21baedd354312dce6c 100644
--- a/oauth2/google_token.php
+++ b/oauth2/google_token.php
@@ -24,14 +24,6 @@
 
 include '../include/init.php';
 
-session_start();
-
-$callback = $_SESSION['rap_callback'];
-if (!isset($callback)) {
-    http_response_code(422);
-    die("Callback URL not set!");
-}
-
 $client = new Google_Client(array(
     'client_id' => $Google['id'],
     'client_secret' => $Google['secret'],
@@ -77,17 +69,36 @@ if ($client->getAccessToken()) {
         array_push($emailAddresses, $addr->value);
     }
 
-    // Creating user object
-    $user = array(
-        "type" => "Google",
-        "name" => $name,
-        "surname" => $surname,
-        "emailAddresses" => $emailAddresses,
-        "typed_id" => explode('/', $res->getResourceName())[1]
-    );
-
-    $token = RAP\DAO::insertLogin($user);
-    header('Location: ' . $callback . '?token=' . $token);
+    $typedId = explode('/', $res->getResourceName())[1];
+
+    $user = RAP\UserHandler::findUserByIdentity(RAP\Identity::GOOGLE, $typedId, null);
+
+    if ($user === null) {
+        $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);
+
+        RAP\UserHandler::saveUser($user);
+    }
+
+    if (isset($session->callback) && $session->callback !== null) {
+        // External login using token
+        $token = RAP\TokenHandler::createNewToken($user->id);
+        header('Location: ' . $session->callback . '?token=' . $token);
+    } else {
+        // Login in session
+        $session->user = $user;
+        $session->save();
+        // Return to index
+        header('Location: ' . $BASE_PATH);
+    }
+
     die();
 } else {
     // Redirect to Google authorization URL for obtaining an access token
diff --git a/setup-database.sql b/setup-database.sql
deleted file mode 100644
index 9630ab493016fd8e0ceb00270bcabe2a2b31c9ee..0000000000000000000000000000000000000000
--- a/setup-database.sql
+++ /dev/null
@@ -1,14 +0,0 @@
-CREATE TABLE `token` (
-  `id` bigint(20) NOT NULL AUTO_INCREMENT,
-  `token` varchar(255) DEFAULT NULL,
-  `data` text,
-  `creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE EVENT tokens_cleanup
-    ON SCHEDULE
-      EVERY 1 MINUTE
-    COMMENT 'Remove expired tokens'
-    DO
-      DELETE FROM token WHERE CURRENT_TIMESTAMP > TIMESTAMPADD(MINUTE,1,creation_time);
diff --git a/sql/setup-database.sql b/sql/setup-database.sql
new file mode 100644
index 0000000000000000000000000000000000000000..5ea15e11748c60a6028e6f7f272b2f1ae2a4a909
--- /dev/null
+++ b/sql/setup-database.sql
@@ -0,0 +1,43 @@
+CREATE TABLE `user` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `identity` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `user_id` bigint(20) NOT NULL,
+  `type` varchar(50) NOT NULL,
+  `typed_id` varchar(255) NOT NULL,
+  `email` varchar(255) NOT NULL,
+  `name` varchar(255) DEFAULT NULL,
+  `surname` varchar(255) DEFAULT NULL,
+  `institution` varchar(255) DEFAULT NULL,
+  `username` varchar(255) DEFAULT NULL,
+  `local_db_id` varchar(255) DEFAULT NULL,
+  `eppn` varchar(255) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  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;
+
+CREATE TABLE `token` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `token` varchar(255) NOT NULL,
+  `data` text,
+  `creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE EVENT tokens_cleanup
+    ON SCHEDULE
+      EVERY 1 MINUTE
+    COMMENT 'Remove expired tokens'
+    DO
+      DELETE FROM token WHERE CURRENT_TIMESTAMP > TIMESTAMPADD(MINUTE,1,creation_time);
diff --git a/views/index.php b/views/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..52a446c8e5a0ad43994a137df6ba0ff5033440f7
--- /dev/null
+++ b/views/index.php
@@ -0,0 +1,16 @@
+<?php
+include 'include/header.php';
+?>
+
+<?php if ($session->user === null) { ?>
+    <a href="google">
+        <img src="img/google-60.png" alt="Google Logo" />
+    </a>
+<?php } else { ?>
+    <?php echo json_encode($session->user); ?>
+    <a href="logout">Logout</a>
+<?php } ?>
+
+<?php
+include 'include/footer.php';
+