diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f59063d5d0b1ebe43aca487389a59cf2b2acdcf..9df2fb214c117a471077517a176e2d51a16f2146 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## Unreleased
+
+- Fix login welcome message
+- Clarify "set new password" button (#94)
+- Show error messages when login fails, for example when a wrong password was
+  entered (#96)
+
 ## [0.5.1]
 
 - Fix bug of missing "Monitoring" app access when creating a new user.
diff --git a/backend/web/login/login.py b/backend/web/login/login.py
index 8ea982ca1e1454ab95d11a85e8755800d4b842f7..3601a00e1aea650423e87c18f1812cdedf3e77f0 100644
--- a/backend/web/login/login.py
+++ b/backend/web/login/login.py
@@ -118,7 +118,13 @@ def login():
     identity = get_auth()
 
     if identity:
-        return render_template("loggedin.html", api_url=KRATOS_PUBLIC_URL, id=id)
+        if 'name' in identity['traits']:
+            # Add a space in front of the "name" so the template can put it
+            # between "Welcome" and the comma
+            name = " " + identity['traits']['name']
+        else:
+            name = ""
+        return render_template("loggedin.html", api_url=KRATOS_PUBLIC_URL, name=name)
 
     flow = request.args.get("flow")
 
diff --git a/backend/web/static/base.js b/backend/web/static/base.js
index 44247607f2dbd6bb6733df7f0a42c5cbe328f753..0e142ed6e01a4ccd417899ea44262e60d715e8fc 100644
--- a/backend/web/static/base.js
+++ b/backend/web/static/base.js
@@ -16,257 +16,244 @@
 
 */
 
-
 // Check if an auth flow is configured and redirect to auth page in that
 // case.
 function check_flow_auth() {
-    var state = Cookies.get('flow_state');
-    var url = Cookies.get('auth_url');
+  var state = Cookies.get('flow_state');
+  var url = Cookies.get('auth_url');
 
-    if (state == 'auth') {
-        Cookies.set('flow_state','');
-        window.location.href = url;
-    }
+  if (state == 'auth') {
+    Cookies.set('flow_state', '');
+    window.location.href = url;
+  }
 }
 
 // Check if there if the flow is expired, if so, reset the cookie
 function check_flow_expired() {
-    var state = Cookies.get('flow_state');
+  var state = Cookies.get('flow_state');
 
-    if (state == 'flow_expired') {
-        Cookies.set('flow_state','');
-        $("#contentFlowExpired").show();
-    }
+  if (state == 'flow_expired') {
+    Cookies.set('flow_state', '');
+    $('#contentFlowExpired').show();
+  }
 }
 
-
-
 // The script executed on login flows
 function flow_login() {
-
-    var flow = $.urlParam('flow');
-    var uri = api_url + 'self-service/login/flows?id=' + flow;
-
-    // Query the Kratos backend to know what fields to render for the
-    // current flow
-    $.ajax({
-        type: "GET",
-        url: uri,
-        success: function(data) {
-
-            // Render login form (group: password)
-            var html = render_form(data, 'password');
-            $("#contentLogin").html(html);
-
-        },
-        complete: function(obj) {
-
-            // If we get a 410, the flow is expired, need to refresh the flow
-            if (obj.status == 410) {
-                Cookies.set('flow_state','flow_expired');
-                // If we call the page without arguments, we get a new flow
-                window.location.href = 'login';
-            }
-        }
-    });
+  var flow = $.urlParam('flow');
+  var uri = api_url + 'self-service/login/flows?id=' + flow;
+
+  // Query the Kratos backend to know what fields to render for the
+  // current flow
+  $.ajax({
+    type: 'GET',
+    url: uri,
+    success: function (data) {
+      // Render login form (group: password)
+      var form_html = render_form(data, 'password');
+      $('#contentLogin').html(form_html);
+
+      var messages_html = render_messages(data);
+      $('#contentMessages').html(messages_html);
+    },
+    complete: function (obj) {
+      // If we get a 410, the flow is expired, need to refresh the flow
+      if (obj.status == 410) {
+        Cookies.set('flow_state', 'flow_expired');
+        // If we call the page without arguments, we get a new flow
+        window.location.href = 'login';
+      }
+    },
+  });
 }
 
-// This is called after a POST on settings. It tells if the save was 
+// This is called after a POST on settings. It tells if the save was
 // successful and display / handles based on that outcome
 function flow_settings_validate() {
-
-    var flow = $.urlParam('flow');
-    var uri = api_url + 'self-service/settings/flows?id=' + flow;
-
-    $.ajax( {
-        type: "GET",
-        url: uri,
-        success: function(data) {
-
-            // We had success. We save that fact in our flow_state
-            // cookie and regenerate a new flow
-            if (data.state == 'success') {
-                Cookies.set('flow_state', 'settings_saved');
-
-                // Redirect to generate new flow ID
-                window.location.href = 'settings';
-            }
-            else {
-                // There was an error, Kratos does not specify what is
-                // wrong. So we just show the general error message and
-                // let the user figure it out. We can re-use the flow-id
-                $("#contentProfileSaveFailed").show();
-
-                // For now, this code assumes that only the password can fail
-                // validation. Other forms might need to be added in the future.
-                html = render_form(data, 'password')
-                $("#contentPassword").html(html)
-            }
-        }
-    });
+  var flow = $.urlParam('flow');
+  var uri = api_url + 'self-service/settings/flows?id=' + flow;
+
+  $.ajax({
+    type: 'GET',
+    url: uri,
+    success: function (data) {
+      // We had success. We save that fact in our flow_state
+      // cookie and regenerate a new flow
+      if (data.state == 'success') {
+        Cookies.set('flow_state', 'settings_saved');
+
+        // Redirect to generate new flow ID
+        window.location.href = 'settings';
+      } else {
+        // There was an error, Kratos does not specify what is
+        // wrong. So we just show the general error message and
+        // let the user figure it out. We can re-use the flow-id
+        $('#contentProfileSaveFailed').show();
+
+        // For now, this code assumes that only the password can fail
+        // validation. Other forms might need to be added in the future.
+        html = render_form(data, 'password');
+        $('#contentPassword').html(html);
+      }
+    },
+  });
 }
 
-
 // Render the settings flow, this is where users can change their personal
 // settings, like name and password. The form contents are defined by Kratos
 function flow_settings() {
-
-    // Get the details from the current flow from kratos
-    var flow = $.urlParam('flow');
-    var uri = api_url + 'self-service/settings/flows?id=' + flow;
-    $.ajax({
-        type: "GET",
-        url: uri,
-        success: function(data) {
-
-            var state = Cookies.get('flow_state')
-
-            // If we have confirmation the settings are saved, show the
-            // notification
-            if (state == 'settings_saved') {
-                $("#contentProfileSaved").show();
-                Cookies.set('flow_state', 'settings');
-            }
-
-            // Hide prfile section if we are in recovery state
-            // so the user is not confused by other fields. The user
-            // probably want to setup a password only first.
-            if (state == 'recovery') {
-                $("#contentProfile").hide();
-            }
-
-
-            // Render the password & profile form based on the fields we got
-            // from the API
-            var html = render_form(data, 'password');
-            $("#contentPassword").html(html);
-
-            html = render_form(data, 'profile');
-            $("#contentProfile").html(html);
-
-            // If the submit button is hit, execute the POST with Ajax. 
-            $("#formpassword").submit(function(e) {
-
-                // avoid to execute the actual submit of the form.
-                e.preventDefault(); 
-
-                var form = $(this);
-                var url = form.attr('action');
-
-                $.ajax({
-                    type: "POST",
-                    url: url,
-                    data: form.serialize(), 
-                    complete: function(obj) {
-                        // Validate the settings
-                        flow_settings_validate();
-                    },
-                });
-            });
-
-
-
-        },
-        complete: function(obj) {
-
-            // If we get a 410, the flow is expired, need to refresh the flow
-            if (obj.status == 410) {
-                Cookies.set('flow_state','flow_expired');
-                window.location.href = 'settings';
-            }
-
-        }
-    });
-
+  // Get the details from the current flow from kratos
+  var flow = $.urlParam('flow');
+  var uri = api_url + 'self-service/settings/flows?id=' + flow;
+  $.ajax({
+    type: 'GET',
+    url: uri,
+    success: function (data) {
+      var state = Cookies.get('flow_state');
+
+      // If we have confirmation the settings are saved, show the
+      // notification
+      if (state == 'settings_saved') {
+        $('#contentProfileSaved').show();
+        Cookies.set('flow_state', 'settings');
+      }
+
+      // Hide prfile section if we are in recovery state
+      // so the user is not confused by other fields. The user
+      // probably want to setup a password only first.
+      if (state == 'recovery') {
+        $('#contentProfile').hide();
+      }
+
+      // Render the password & profile form based on the fields we got
+      // from the API
+      var html = render_form(data, 'password');
+      $('#contentPassword').html(html);
+
+      html = render_form(data, 'profile');
+      $('#contentProfile').html(html);
+
+      // If the submit button is hit, execute the POST with Ajax.
+      $('#formpassword').submit(function (e) {
+        // avoid to execute the actual submit of the form.
+        e.preventDefault();
+
+        var form = $(this);
+        var url = form.attr('action');
+
+        $.ajax({
+          type: 'POST',
+          url: url,
+          data: form.serialize(),
+          complete: function (obj) {
+            // Validate the settings
+            flow_settings_validate();
+          },
+        });
+      });
+    },
+    complete: function (obj) {
+      // If we get a 410, the flow is expired, need to refresh the flow
+      if (obj.status == 410) {
+        Cookies.set('flow_state', 'flow_expired');
+        window.location.href = 'settings';
+      }
+    },
+  });
 }
 
 function flow_recover() {
-    var flow = $.urlParam('flow');
-    var uri = api_url + 'self-service/recovery/flows?id=' + flow;
-
-    $.ajax( {
-        type: "GET",
-        url: uri,
-        success: function(data) {
-
-            // Render the recover form, method 'link'
-            var html = render_form(data, 'link');
-            $("#contentRecover").html(html);
-
-            // Do form post as an AJAX call
-            $("#formlink").submit(function(e) {
-
-                // avoid to execute the actual submit of the form.
-                e.preventDefault(); 
-
-                var form = $(this);
-                var url = form.attr('action');
-
-                // keep stat we are in recovery
-                Cookies.set('flow_state', 'recovery');
-                $.ajax({
-                    type: "POST",
-                    url: url,
-                    data: form.serialize(), // serializes the form's elements.
-                    success: function(data)
-                    {
-
-                        // Show the request is sent out
-                        $("#contentRecover").hide();
-                        $("#contentRecoverRequested").show();
-                    }
-                });
-            });
-
-
-        },
-        complete: function(obj) {
-
-            // If we get a 410, the flow is expired, need to refresh the flow
-            if (obj.status == 410) {
-                Cookies.set('flow_state','flow_expired');
-                window.location.href = 'recovery';
-
-            }
-        }
-    });
+  var flow = $.urlParam('flow');
+  var uri = api_url + 'self-service/recovery/flows?id=' + flow;
+
+  $.ajax({
+    type: 'GET',
+    url: uri,
+    success: function (data) {
+      // Render the recover form, method 'link'
+      var html = render_form(data, 'link');
+      $('#contentRecover').html(html);
+
+      // Do form post as an AJAX call
+      $('#formlink').submit(function (e) {
+        // avoid to execute the actual submit of the form.
+        e.preventDefault();
+
+        var form = $(this);
+        var url = form.attr('action');
+
+        // keep stat we are in recovery
+        Cookies.set('flow_state', 'recovery');
+        $.ajax({
+          type: 'POST',
+          url: url,
+          data: form.serialize(), // serializes the form's elements.
+          success: function (data) {
+            // Show the request is sent out
+            $('#contentRecover').hide();
+            $('#contentRecoverRequested').show();
+          },
+        });
+      });
+    },
+    complete: function (obj) {
+      // If we get a 410, the flow is expired, need to refresh the flow
+      if (obj.status == 410) {
+        Cookies.set('flow_state', 'flow_expired');
+        window.location.href = 'recovery';
+      }
+    },
+  });
 }
 
 // Based on Kratos UI data and a group name, get the full form for that group.
-// kratos groups elements which belongs together in a group and should be posted 
+// kratos groups elements which belongs together in a group and should be posted
 // at once. The elements in the default group should be part of all other
 // groups.
 //
 // data: data object as returned form the API
 // group: group to render.
-function render_form(data, group) { 
-
-    // Create form
-    var action = data.ui.action;
-    var method = data.ui.method;
-    var form = "<form id='form"+group+"' method='"+method+"' action='"+action+"'>";
-
-    for (const node of data.ui.nodes) {
-
-        var name = node.attributes.name;
-        var type = node.attributes.type;
-        var value = node.attributes.value;
-        var messages = node.messages
-
-        if (node.group == 'default' || node.group == group) {
-            var elm = getFormElement(type, name, value, messages);
-            form += elm;
-        }
+function render_form(data, group) {
+  // Create form
+  var action = data.ui.action;
+  var method = data.ui.method;
+  var form = "<form id='form" + group + "' method='" + method + "' action='" + action + "'>";
+
+  for (const node of data.ui.nodes) {
+    var name = node.attributes.name;
+    var type = node.attributes.type;
+    var value = node.attributes.value;
+    var messages = node.messages;
+
+    if (node.group == 'default' || node.group == group) {
+      var elm = getFormElement(type, name, value, messages);
+      form += elm;
     }
-    form += "</form>";
-    return form;
+  }
+  form += '</form>';
+  return form;
+}
 
+// Check if there are any general messages to show to the user and render them
+function render_messages(data) {
+  var messages = data.ui.messages;
+  if (messages == []) {
+    return '';
+  }
+  var html = '<ul>';
+  messages.forEach((message) => {
+    html += '<li>';
+    html += message.text;
+    html += '</li>';
+  });
+  html += '</ul>';
+  return html;
 }
 
 // Return form element based on name, including help text (sub), placeholder etc.
 // Kratos give us form names and types and specifies what to render. However
 // it does not provide labels or translations. This function returns a HTML
-// form element based on the fields provided by Kratos with proper names and 
+// form element based on the fields provided by Kratos with proper names and
 // labels
 // type: input type, usual "input", "hidden" or "submit". But bootstrap types
 //                   like "email" are also supported
@@ -274,98 +261,80 @@ function render_form(data, group) {
 // value: If there is already a value known, show it
 // messages: error messages related to the field
 function getFormElement(type, name, value, messages) {
-    console.log("Getting form element", type, name, value, messages)
-
-    if (value == undefined) {
-            value = '';
-    }
-
-    if (typeof(messages) == "undefined") {
-        messages = []
-    }
-
-    if (name == 'email' || name == 'traits.email') {
-        return getFormInput(
-                'email',
-                name,
-                value,
-                'E-mail address',
-                'Please enter your e-mail address here',
-                'Please provide your e-mail address. We will send a recovery ' +
-                'link to that e-mail address.',
-                messages,
-                );
-    }
-
-    if (name == 'traits.username') {
-        return getFormInput(
-                'name',
-                name,
-                value,
-                'Username',
-                'Please provide an username',
-                null,
-                messages,
-                );
-    }
-
-    if (name == 'traits.name') {
-        return getFormInput(
-                'name',
-                name,
-                value,
-                'Full name',
-                'Please provide your full name',
-                null,
-                messages,
-                );
-    }
-
-
-    if (name == 'identifier') {
-        return getFormInput(
-                'email',
-                name,
-                value,
-                'E-mail address',
-                'Please provide your e-mail address to log in',
-                null,
-                messages,
-                );
-    }
-
-    if (name == 'password') {
-        return getFormInput(
-                'password',
-                name,
-                value,
-                'Password',
-                'Please provide your password',
-                null,
-                messages,
-                );
-    }
-
-
-    if (type == 'hidden' || name == 'traits.uuid') {
-
-        return `
-            <input type="hidden" class="form-control" id="`+name+`"
-            name="`+name+`" value='`+value+`'>`;
-    }
-
-    if (type == 'submit') {
-
-        return `<div class="form-group">
-            <input type="hidden" name="`+name+`" value="`+value+`">
+  console.log('Getting form element', type, name, value, messages);
+
+  if (value == undefined) {
+    value = '';
+  }
+
+  if (typeof messages == 'undefined') {
+    messages = [];
+  }
+
+  if (name == 'email' || name == 'traits.email') {
+    return getFormInput(
+      'email',
+      name,
+      value,
+      'E-mail address',
+      'Please enter your e-mail address here',
+      'Please provide your e-mail address. We will send a recovery link to that e-mail address.',
+      messages,
+    );
+  }
+
+  if (name == 'traits.username') {
+    return getFormInput('name', name, value, 'Username', 'Please provide an username', null, messages);
+  }
+
+  if (name == 'traits.name') {
+    return getFormInput('name', name, value, 'Full name', 'Please provide your full name', null, messages);
+  }
+
+  if (name == 'identifier') {
+    return getFormInput(
+      'email',
+      name,
+      value,
+      'E-mail address',
+      'Please provide your e-mail address to log in',
+      null,
+      messages,
+    );
+  }
+
+  if (name == 'password') {
+    return getFormInput('password', name, value, 'Password', 'Please provide your password', null, messages);
+  }
+
+  if (type == 'hidden' || name == 'traits.uuid') {
+    return (
+      `
+            <input type="hidden" class="form-control" id="` +
+      name +
+      `"
+            name="` +
+      name +
+      `" value='` +
+      value +
+      `'>`
+    );
+  }
+
+  if (type == 'submit') {
+    return (
+      `<div class="form-group">
+            <input type="hidden" name="` +
+      name +
+      `" value="` +
+      value +
+      `">
              <button type="submit" class="btn btn-primary">Go!</button>
-            </div>`;
-    }
-
-
-    return getFormInput('input', name, value, name, null,null, messages);
-
+            </div>`
+    );
+  }
 
+  return getFormInput('input', name, value, name, null, null, messages);
 }
 
 // Usually called by getFormElement, generic function to generate an
@@ -378,56 +347,58 @@ function getFormElement(type, name, value, messages) {
 // param help: Additional help text, displayed below the field in small font
 // param messages: Message about failed input
 function getFormInput(type, name, value, label, placeHolder, help, messages) {
-    if (typeof(help) == "undefined" || help == null) {
-        help = ""
+  if (typeof help == 'undefined' || help == null) {
+    help = '';
+  }
+  console.log('Messages: ', messages);
+
+  // Id field for help element
+  var nameHelp = name + 'Help';
+
+  var element = '<div class="form-group">';
+  element += '<label for="' + name + '">' + label + '</label>';
+  element += '<input type="' + type + '" class="form-control" id="' + name + '" name="' + name + '" ';
+
+  // messages get appended to help info
+  if (messages.length) {
+    for (message in messages) {
+      console.log('adding message', messages[message]);
+      help += messages[message]['text'];
     }
-    console.log("Messages: ", messages);
-
-    // Id field for help element
-    var nameHelp = name + "Help";
-
-    var element = '<div class="form-group">';
-    element += '<label for="'+name+'">'+label+'</label>';
-    element += '<input type="'+type+'" class="form-control" id="'+name+'" name="'+name+'" ';
-
-    // messages get appended to help info
-    if (messages.length) {
-        for (message in messages) {
-            console.log("adding message", messages[message])
-            help += messages[message]['text']
-        }
-    }
-
-    // If we are a password field, add a eye icon to reveal password
-    if (value) {
-        element += 'value="'+value+'" ';
-    }
-    if (help) {
-        element += 'aria-describedby="' + nameHelp +'" ';
-    }
-    if (placeHolder) {
-        element += 'placeholder="'+placeHolder+'" ';
-    }
-    element += ">";
-
-    if (help) {
-        element +=
-        `<small id="`+nameHelp+`" class="form-text text-muted">` + help + `
+  }
+
+  // If we are a password field, add a eye icon to reveal password
+  if (value) {
+    element += 'value="' + value + '" ';
+  }
+  if (help) {
+    element += 'aria-describedby="' + nameHelp + '" ';
+  }
+  if (placeHolder) {
+    element += 'placeholder="' + placeHolder + '" ';
+  }
+  element += '>';
+
+  if (help) {
+    element +=
+      `<small id="` +
+      nameHelp +
+      `" class="form-text text-muted">` +
+      help +
+      `
         </small>`;
-    }
+  }
 
-    element += '</div>';
+  element += '</div>';
 
-    return element;
+  return element;
 }
 
-
-
 // $.urlParam get parameters from the URI. Example: id =  $.urlParam('id');
-$.urlParam = function(name) {
-    var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
-    if (results==null) {
-        return null;
-    }
-    return decodeURI(results[1]) || 0;
+$.urlParam = function (name) {
+  var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href);
+  if (results == null) {
+    return null;
+  }
+  return decodeURI(results[1]) || 0;
 };
diff --git a/backend/web/templates/loggedin.html b/backend/web/templates/loggedin.html
index 530e0dad19038b17e128aa2747077d07d5fc0623..aee728efd7af9818830128570bcc6a95f5ab4f1a 100644
--- a/backend/web/templates/loggedin.html
+++ b/backend/web/templates/loggedin.html
@@ -1,5 +1,3 @@
-
-
 {% extends 'base.html' %}
 
 {% block content %}
@@ -17,11 +15,9 @@
 
 
 
-    <div id="contentMessages"></div>
-    <div id="contentWelcome">Welcome {{ id['name'] }},<br/><br/>
-    You are already logged in.
-    
-
+<div id="contentMessages"></div>
+<div id="contentWelcome">
+    Welcome{{ name }}, you are logged in.
 </div>
 
 
diff --git a/backend/web/templates/login.html b/backend/web/templates/login.html
index b715367a98e700f0e498ea3daaa24d17baee6a7a..844eef72ff644be2ae5fcebed74eb3d89016dbe6 100644
--- a/backend/web/templates/login.html
+++ b/backend/web/templates/login.html
@@ -19,7 +19,7 @@
     <div id="contentMessages"></div>
     <div id="contentLogin"></div>
     <div id="contentHelp">
-        <a href='recovery'>Forget password?</a> | <a href='https://stackspin.net'>About stackspin</a>
+        <a href='recovery'>Set new password</a> | <a href='https://stackspin.net'>About stackspin</a>
     </div>
 
 {% endblock %}