diff --git a/README.md b/README.md
index b5fe36890ac97d64fc55681dcc380131404275e3..16c6594469748a623eeea243e307e90f6dba1c51 100644
--- a/README.md
+++ b/README.md
@@ -10,16 +10,12 @@
 * `mongoose`: Mongoose MongoDB ODM
 * `body-parser`: Node.js body parsing middleware
 * `morgan`: HTTP request logger middleware for node.js
+* `connect-mongodb-session`: MongoDB session store for connect/express built by MongoDB
 
 ### To use
 * `joi`: Object schema description language and validator for JavaScript objects.
 
 ## Release
 
-### Next: v0.1.0
-* [ ] Authentication and session
+### TODO
 * [ ] Validate mongoose models
-* [ ] Make mongoose unique index work
-
-### Next: v0.2.0
-* [ ] Message handler
diff --git a/index.js b/index.js
index d307bc5d409eec01a6c3049a08337f6eff3090c6..b6be466f1a06db7e2de8c36bc97fee93ea0e504c 100644
--- a/index.js
+++ b/index.js
@@ -1,28 +1,65 @@
 const express = require('express');
 const session = require('express-session');
 const bodyParser = require('body-parser');
-const User = require('./models/user');
 const mongoose = require('mongoose');
 const bcrypt = require('bcrypt');
 const morgan = require('morgan');
+const mongoDBStore = require('connect-mongodb-session')(session);
 
 const config = require('./config.json');
 
+const User = require('./models/user');
+const Notification = require('./models/notification');
+
 // Utils
-const render = (req, res, view, options) => res.render(view, {
-  ...options,
-  user: req.session.user,
-  nextUrl: req.url,
-});
+const render = (req, res, view, options) => {
+  // Load notifications
+  if (req.session.user) {
+      res.render(view, {
+        ...options,
+        user: req.session.user,
+        nextUrl: req.url,
+        notifications: req.session.user.notifications
+      });
+      req.session.user.notifications
+        .filter(notification => !notification.persistant)
+        .forEach(notification => notification.remove());
+      req.session.user.save();
+  } else {
+    return res.render(view, {
+      ...options,
+      user: req.session.user,
+      nextUrl: req.url,
+      notifications: []
+    });
+  }
+};
+const warn = (req, res, title, content) => {
+  req.session.user.notifications.push({ title, content, color: "warning" });
+  req.session.user.save();
+};
+const error = (req, res, title, content) => {
+  req.session.user.notifications.push({ title, content, color: "error" });
+  req.user.session.save();
+  return res.redirect('/'); // TODO redirect to error route or previous
+}
 
 // Configuration
 const app = express();
+const store = new mongoDBStore({
+  uri: 'mongodb://localhost/rolegame',
+  collection: 'sessions'
+});
+store.on('error', function(error) {
+  console.error(error);
+});
 app.set('view engine', 'pug');
 app.use(morgan('tiny'));
 app.use(session({
   secret: config.secret,
   resave: false,
-  saveUninitialized: false
+  saveUninitialized: false,
+  store
 }));
 
 // Middlewares
@@ -36,6 +73,17 @@ app.use((req, res, next) => {
     return res.redirect('/signup');
   }
 });
+app.use((req, res, next) => {
+  if (req.session.user) {
+    User.findById(req.session.user._id, (err, user) => {
+      err ? error(req, res, 'Error fetching user', err) : null;
+      req.session.user = user;
+      next();
+    });
+  } else {
+    next();
+  }
+})
 
 app.get('/', (req, res) => {
   return render(req, res, 'home');
@@ -46,7 +94,7 @@ app.get('/signup', (req, res) => {
 app.post('/signup', (req, res) => {
   const passwordHash = bcrypt.hashSync(req.body.password, config.cryptRounds);
   User.create(req.body, (err, user) => {
-    err ? console.error(err) : null;
+    err ? error(req, res, 'Error creating user', err) : null;
     user.passwordHash = passwordHash;
     user.save();
     req.session.user = user;
@@ -54,16 +102,22 @@ app.post('/signup', (req, res) => {
   });
 });
 app.post('/login', (req, res) => {
-  User.findOne({ username: req.body.username }, (err, user) => {
-    err ? console.error(err) : null;
-    if (bcrypt.compareSync(req.body.password, user.passwordHash)) {
-      req.session.user = user;
-      return res.redirect(req.query.nextUrl || '/');
-    } else {
-      console.error("Bad authentication");
-      return res.redirect('/signup');
-    }
-  });
+  if (!req.body.username || !req.body.password) {
+    return res.redirect('/signup');
+  } else if (req.session.user) {
+    error(req, res, 'User already logged in', 'You must logout before log in.')
+  } else {
+    User.findOne({ username: req.body.username }, (err, user) => {
+      err ? error(req, res, 'Error fetching user', err) : null;
+      if (bcrypt.compareSync(req.body.password, user.passwordHash)) {
+        req.session.user = user;
+        return res.redirect(req.query.nextUrl || '/');
+      } else {
+        error(req, res, 'Bad credentials')
+        return res.redirect('/signup');
+      }
+    });
+  }
 });
 app.post('/logout', (req, res) => {
   req.session.destroy();
diff --git a/models/notification.js b/models/notification.js
new file mode 100644
index 0000000000000000000000000000000000000000..f411cff6edcfea0b65353c5916ded00bd8c93aee
--- /dev/null
+++ b/models/notification.js
@@ -0,0 +1,21 @@
+const mongoose = require('mongoose');
+
+const Notification = new mongoose.Schema({
+  title: {
+    type: String,
+    required: true,
+  },
+  content: {
+    type: String,
+  },
+  persistant: {
+    type: Boolean,
+    default: false,
+  },
+  color: {
+    type: String,
+    default: ""
+  }
+});
+
+module.exports = Notification;
\ No newline at end of file
diff --git a/models/user.js b/models/user.js
index fdb9dc2587a5453ea56b85e9a19e301f93f3c045..993d4d7ea91843c639a3ceb21a4680ca712b2280 100644
--- a/models/user.js
+++ b/models/user.js
@@ -1,4 +1,5 @@
 const mongoose = require('mongoose');
+const Notification = require('./notification');
 
 const User = new mongoose.Schema({
   firstName: {
@@ -7,11 +8,12 @@ const User = new mongoose.Schema({
   },
   lastName: {
     type: String,
+    required: true,
   },
   username: {
     unique: true,
     type: String,
-    lowercase: true
+    lowercase: true,
   },
   email: {
     type: String,
@@ -20,6 +22,7 @@ const User = new mongoose.Schema({
   passwordHash: {
     type: String,
   },
+  notifications: [Notification],
 });
 
 module.exports = mongoose.model('User', User);
\ No newline at end of file
diff --git a/package.json b/package.json
index e3b0f5265bcfe6b94893fabefb7e5623309b5744..e5449afe88dc0e2936b2eee22d25720e724c4002 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
   "dependencies": {
     "bcrypt": "^3.0.4",
     "body-parser": "^1.18.3",
+    "connect-mongodb-session": "^2.1.1",
     "eslint": "^5.13.0",
     "express": "^4.16.4",
     "express-session": "^1.15.6",
diff --git a/views/base.pug b/views/base.pug
index 38855d3c86dc03a07458dd4ec5681663723f9595..6be478781b22b3d1c969eb199770446e16f4af0d 100644
--- a/views/base.pug
+++ b/views/base.pug
@@ -7,6 +7,8 @@ html(lang="en")
     link(rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" integrity="sha256-9mbkOfVho3ZPXfM7W8sV2SndrGDuh7wuyLjtsWeTI1Q=" crossorigin="anonymous")
     title RoleGame
   body
+    script(src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E=" crossorigin="anonymous")
+    script(src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js" integrity="sha256-t8GepnyPmw9t+foMh3mKNvcorqNHamSKtKRxxpUEgFI=" crossorigin="anonymous")
     block navbar
       if !user
         form.ui.form(action="/login", method="post")
@@ -29,8 +31,8 @@ html(lang="en")
               .item
                 button.ui.icon.button.basic(type="submit")
                   i.power.off.icon
-    .ui.container
-      block main
-    block script
-      script(src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E=" crossorigin="anonymous")
-      script(src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js" integrity="sha256-t8GepnyPmw9t+foMh3mKNvcorqNHamSKtKRxxpUEgFI=" crossorigin="anonymous")
\ No newline at end of file
+    .ui.grid
+      .thirteen.wide.column
+        block main
+      .three.wide.column
+        include notifications.pug
diff --git a/views/notifications.pug b/views/notifications.pug
new file mode 100644
index 0000000000000000000000000000000000000000..bf6c0d7b23c3ce6a5ec99b73d9d0361ab1932480
--- /dev/null
+++ b/views/notifications.pug
@@ -0,0 +1,10 @@
+each notification in notifications
+  .ui.message.tiny(class=notification.color)
+    i.close.icon
+    .header= notification.title
+    p= notification.content
+
+script 
+  | $('.message .close').on('click', function() {
+  |   $(this).closest('.message').transition('fade');
+  | });
\ No newline at end of file