keycloak-aplcache

Details

diff --git a/forms/account-api/src/main/java/org/keycloak/account/AccountProvider.java b/forms/account-api/src/main/java/org/keycloak/account/AccountProvider.java
index bc6920b..f5bcbb2 100755
--- a/forms/account-api/src/main/java/org/keycloak/account/AccountProvider.java
+++ b/forms/account-api/src/main/java/org/keycloak/account/AccountProvider.java
@@ -1,52 +1,55 @@
 package org.keycloak.account;
 
-import org.apache.http.client.methods.HttpHead;
-import org.keycloak.events.Event;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.provider.Provider;
+import java.util.List;
 
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import java.util.List;
+
+import org.keycloak.events.Event;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.FormMessage;
+import org.keycloak.provider.Provider;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public interface AccountProvider extends Provider {
 
-    AccountProvider setUriInfo(UriInfo uriInfo);
+	AccountProvider setUriInfo(UriInfo uriInfo);
+
+	AccountProvider setHttpHeaders(HttpHeaders httpHeaders);
 
-    AccountProvider setHttpHeaders(HttpHeaders httpHeaders);
+	Response createResponse(AccountPages page);
 
-    Response createResponse(AccountPages page);
+	AccountProvider setError(String message, Object... parameters);
 
-    AccountProvider setError(String message, Object ... parameters);
+	AccountProvider setErrors(List<FormMessage> messages);
 
-    AccountProvider setSuccess(String message, Object ... parameters);
+	AccountProvider setSuccess(String message, Object... parameters);
 
-    AccountProvider setWarning(String message, Object ... parameters);
+	AccountProvider setWarning(String message, Object... parameters);
 
-    AccountProvider setUser(UserModel user);
+	AccountProvider setUser(UserModel user);
 
-    AccountProvider setProfileFormData(MultivaluedMap<String, String> formData);
+	AccountProvider setProfileFormData(MultivaluedMap<String, String> formData);
 
-    AccountProvider setStatus(Response.Status status);
+	AccountProvider setStatus(Response.Status status);
 
-    AccountProvider setRealm(RealmModel realm);
+	AccountProvider setRealm(RealmModel realm);
 
-    AccountProvider setReferrer(String[] referrer);
+	AccountProvider setReferrer(String[] referrer);
 
-    AccountProvider setEvents(List<Event> events);
+	AccountProvider setEvents(List<Event> events);
 
-    AccountProvider setSessions(List<UserSessionModel> sessions);
+	AccountProvider setSessions(List<UserSessionModel> sessions);
 
-    AccountProvider setPasswordSet(boolean passwordSet);
+	AccountProvider setPasswordSet(boolean passwordSet);
 
-    AccountProvider setStateChecker(String stateChecker);
+	AccountProvider setStateChecker(String stateChecker);
 
-    AccountProvider setFeatures(boolean social, boolean events, boolean passwordUpdateSupported);
+	AccountProvider setFeatures(boolean social, boolean events, boolean passwordUpdateSupported);
 }
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
index 4e0c007..2828531 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
@@ -1,25 +1,54 @@
 package org.keycloak.account.freemarker;
 
+import java.io.IOException;
+import java.net.URI;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
 import org.jboss.logging.Logger;
 import org.keycloak.account.AccountPages;
 import org.keycloak.account.AccountProvider;
-import org.keycloak.account.freemarker.model.*;
+import org.keycloak.account.freemarker.model.AccountBean;
+import org.keycloak.account.freemarker.model.AccountFederatedIdentityBean;
+import org.keycloak.account.freemarker.model.FeaturesBean;
+import org.keycloak.account.freemarker.model.LogBean;
+import org.keycloak.account.freemarker.model.PasswordBean;
+import org.keycloak.account.freemarker.model.RealmBean;
+import org.keycloak.account.freemarker.model.ReferrerBean;
+import org.keycloak.account.freemarker.model.SessionsBean;
+import org.keycloak.account.freemarker.model.TotpBean;
+import org.keycloak.account.freemarker.model.UrlBean;
 import org.keycloak.events.Event;
-import org.keycloak.freemarker.*;
+import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
+import org.keycloak.freemarker.FreeMarkerException;
+import org.keycloak.freemarker.FreeMarkerUtil;
+import org.keycloak.freemarker.LocaleHelper;
+import org.keycloak.freemarker.Theme;
+import org.keycloak.freemarker.ThemeProvider;
+import org.keycloak.freemarker.beans.LocaleBean;
+import org.keycloak.freemarker.beans.MessageBean;
 import org.keycloak.freemarker.beans.MessageFormatterMethod;
+import org.keycloak.freemarker.beans.MessageType;
+import org.keycloak.freemarker.beans.MessagesPerFieldBean;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.FormMessage;
 import org.keycloak.services.resources.flows.Urls;
 
-import javax.ws.rs.core.*;
-import java.io.IOException;
-import java.net.URI;
-import java.text.MessageFormat;
-import java.util.*;
-import org.keycloak.freemarker.beans.LocaleBean;
-
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
@@ -43,13 +72,10 @@ public class FreeMarkerAccountProvider implements AccountProvider {
     private FreeMarkerUtil freeMarker;
     private HttpHeaders headers;
 
-    public static enum MessageType {SUCCESS, WARNING, ERROR}
-
     private UriInfo uriInfo;
 
-    private String message;
-    private Object[] parameters;
-    private MessageType messageType;
+    private List<FormMessage> messages = null;
+    private MessageType messageType = MessageType.ERROR;
 
     public FreeMarkerAccountProvider(KeycloakSession session, FreeMarkerUtil freeMarker) {
         this.session = session;
@@ -87,13 +113,13 @@ public class FreeMarkerAccountProvider implements AccountProvider {
         }
 
         Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, headers);
-        Properties messages;
+        Properties messagesBundle;
         try {
-            messages = theme.getMessages(locale);
-            attributes.put("msg", new MessageFormatterMethod(locale, messages));
+        	messagesBundle = theme.getMessages(locale);
+            attributes.put("msg", new MessageFormatterMethod(locale, messagesBundle));
         } catch (IOException e) {
             logger.warn("Failed to load messages", e);
-            messages = new Properties();
+            messagesBundle = new Properties();
         }
 
         URI baseUri = uriInfo.getBaseUri();
@@ -107,15 +133,19 @@ public class FreeMarkerAccountProvider implements AccountProvider {
             attributes.put("stateChecker", stateChecker);
         }
 
-        if (message != null) {
-            String formattedMessage;
-            if(messages.containsKey(message)){
-                formattedMessage = new MessageFormat(messages.getProperty(message),locale).format(parameters);
-            }else{
-                formattedMessage = message;
+        MessagesPerFieldBean messagesPerField = new MessagesPerFieldBean();
+        if (messages != null) {
+            MessageBean wholeMessage = new MessageBean(null, messageType);
+            for (FormMessage message : this.messages) {
+                String formattedMessageText = formatMessage(message, messagesBundle, locale);
+                if (formattedMessageText != null) {
+                    wholeMessage.appendSummaryLine(formattedMessageText);
+                    messagesPerField.addMessage(message.getField(), formattedMessageText, messageType);
+                }
             }
-            attributes.put("message", new MessageBean(formattedMessage, messageType));
+            attributes.put("message", wholeMessage);
         }
+        attributes.put("messagesPerField", messagesPerField);
 
         if (referrer != null) {
             attributes.put("referrer", new ReferrerBean(referrer));
@@ -134,7 +164,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
                     b = UriBuilder.fromUri(baseQueryUri).path(uriInfo.getPath());
                     break;
             }
-            attributes.put("locale", new LocaleBean(realm, locale, b, messages));
+            attributes.put("locale", new LocaleBean(realm, locale, b, messagesBundle));
         }
 
         attributes.put("features", new FeaturesBean(identityProviderEnabled, eventsEnabled, passwordUpdateSupported));
@@ -173,28 +203,47 @@ public class FreeMarkerAccountProvider implements AccountProvider {
         this.passwordSet = passwordSet;
         return this;
     }
+    
+	protected void setMessage(MessageType type, String message, Object... parameters) {
+		messageType = type;
+		messages = new ArrayList<>();
+		messages.add(new FormMessage(null, message, parameters));
+	}
+
+	protected String formatMessage(FormMessage message, Properties messagesBundle, Locale locale) {
+		if (message == null)
+			return null;
+		if (messagesBundle.containsKey(message.getMessage())) {
+			return new MessageFormat(messagesBundle.getProperty(message.getMessage()), locale)
+					.format(message.getParameters());
+		} else {
+			return message.getMessage();
+		}
+	}
+  
+  @Override
+	public AccountProvider setErrors(List<FormMessage> messages) {
+		this.messageType = MessageType.ERROR;
+		this.messages = new ArrayList<>(messages);
+		return this;
+	}
+
 
     @Override
     public AccountProvider setError(String message, Object ... parameters) {
-        this.message = message;
-        this.parameters = parameters;
-        this.messageType = MessageType.ERROR;
+    	  setMessage(MessageType.ERROR, message, parameters);
         return this;
     }
 
     @Override
     public AccountProvider setSuccess(String message, Object ... parameters) {
-        this.message = message;
-        this.parameters = parameters;
-        this.messageType = MessageType.SUCCESS;
+    	  setMessage(MessageType.SUCCESS, message, parameters);
         return this;
     }
 
     @Override
     public AccountProvider setWarning(String message, Object ... parameters) {
-        this.message = message;
-        this.parameters = parameters;
-        this.messageType = MessageType.WARNING;
+    	  setMessage(MessageType.WARNING, message, parameters);
         return this;
     }
 
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageType.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageType.java
new file mode 100644
index 0000000..a404c7e
--- /dev/null
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageType.java
@@ -0,0 +1,17 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @authors tag. All rights reserved.
+ */
+package org.keycloak.freemarker.beans;
+
+/**
+ * Enum with types of messages.
+ *
+ * @author Vlastimil Elias (velias at redhat dot com)
+ */
+public enum MessageType {
+	
+	SUCCESS, WARNING, ERROR
+
+}
diff --git a/forms/common-themes/src/main/resources/theme/base/account/account.ftl b/forms/common-themes/src/main/resources/theme/base/account/account.ftl
index a41d769..7c349df 100755
--- a/forms/common-themes/src/main/resources/theme/base/account/account.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/account/account.ftl
@@ -14,7 +14,7 @@
 
         <input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
 
-        <div class="form-group">
+        <div class="form-group ${messagesPerField.printIfExists('username','has-error')}">
             <div class="col-sm-2 col-md-2">
                 <label for="username" class="control-label">${msg("username")}</label>
             </div>
@@ -24,7 +24,7 @@
             </div>
         </div>
 
-        <div class="form-group">
+        <div class="form-group ${messagesPerField.printIfExists('email','has-error')}">
             <div class="col-sm-2 col-md-2">
             <label for="email" class="control-label">${msg("email")}</label> <span class="required">*</span>
             </div>
@@ -34,7 +34,7 @@
             </div>
         </div>
 
-        <div class="form-group">
+        <div class="form-group ${messagesPerField.printIfExists('firstName','has-error')}">
             <div class="col-sm-2 col-md-2">
                 <label for="firstName" class="control-label">${msg("firstName")}</label> <span class="required">*</span>
             </div>
@@ -44,7 +44,7 @@
             </div>
         </div>
 
-        <div class="form-group">
+        <div class="form-group ${messagesPerField.printIfExists('lastName','has-error')}">
             <div class="col-sm-2 col-md-2">
                 <label for="lastName" class="control-label">${msg("lastName")}</label> <span class="required">*</span>
             </div>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
index 56b5cbe..a534073 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
@@ -6,7 +6,7 @@
         ${msg("loginProfileTitle")}
     <#elseif section = "form">
         <form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginUpdateProfileUrl}" method="post">
-            <div class="${properties.kcFormGroupClass!}">
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('email',properties.kcFormGroupErrorClass!)}">
                 <div class="${properties.kcLabelWrapperClass!}">
                     <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
                 </div>
@@ -15,7 +15,7 @@
                 </div>
             </div>
 
-            <div class="${properties.kcFormGroupClass!}">
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('firstName',properties.kcFormGroupErrorClass!)}">
                 <div class="${properties.kcLabelWrapperClass!}">
                     <label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
                 </div>
@@ -24,7 +24,7 @@
                 </div>
             </div>
 
-            <div class="${properties.kcFormGroupClass!}">
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('lastName',properties.kcFormGroupErrorClass!)}">
                 <div class="${properties.kcLabelWrapperClass!}">
                     <label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
                 </div>
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
index ac73087..c3a71d9 100755
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginFormsProvider.java
@@ -13,6 +13,7 @@ import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.FormMessage;
 import org.keycloak.provider.Provider;
 
 /**
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 22fa3f7..0dd6342 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -1,27 +1,50 @@
 package org.keycloak.login.freemarker;
 
+import java.io.IOException;
+import java.net.URI;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
-import org.keycloak.freemarker.*;
+import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
+import org.keycloak.freemarker.FreeMarkerException;
+import org.keycloak.freemarker.FreeMarkerUtil;
+import org.keycloak.freemarker.LocaleHelper;
+import org.keycloak.freemarker.Theme;
+import org.keycloak.freemarker.ThemeProvider;
 import org.keycloak.freemarker.beans.AdvancedMessageFormatterMethod;
+import org.keycloak.freemarker.beans.LocaleBean;
+import org.keycloak.freemarker.beans.MessageBean;
 import org.keycloak.freemarker.beans.MessageFormatterMethod;
-import org.keycloak.login.FormMessage;
+import org.keycloak.freemarker.beans.MessageType;
+import org.keycloak.freemarker.beans.MessagesPerFieldBean;
 import org.keycloak.login.LoginFormsPages;
 import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.login.freemarker.model.ClientBean;
 import org.keycloak.login.freemarker.model.CodeBean;
-import org.keycloak.freemarker.beans.LocaleBean;
+import org.keycloak.login.freemarker.model.IdentityProviderBean;
 import org.keycloak.login.freemarker.model.LoginBean;
-import org.keycloak.login.freemarker.model.MessageBean;
-import org.keycloak.login.freemarker.model.MessagesPerFieldBean;
 import org.keycloak.login.freemarker.model.OAuthGrantBean;
 import org.keycloak.login.freemarker.model.ProfileBean;
 import org.keycloak.login.freemarker.model.RealmBean;
 import org.keycloak.login.freemarker.model.RegisterBean;
-import org.keycloak.login.freemarker.model.IdentityProviderBean;
 import org.keycloak.login.freemarker.model.TotpBean;
 import org.keycloak.login.freemarker.model.UrlBean;
 import org.keycloak.models.ClientModel;
@@ -30,17 +53,10 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.FormMessage;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.flows.Urls;
 
-import javax.ws.rs.core.*;
-
-import java.io.IOException;
-import java.net.URI;
-import java.text.MessageFormat;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
@@ -48,8 +64,6 @@ import java.util.concurrent.TimeUnit;
 
     private static final Logger logger = Logger.getLogger(FreeMarkerLoginFormsProvider.class);
 
-    public static enum MessageType {SUCCESS, WARNING, ERROR}
-
     private String accessCode;
     private Response.Status status;
     private List<RoleModel> realmRolesRequested;
@@ -191,7 +205,7 @@ import java.util.concurrent.TimeUnit;
         if (messages != null) {
             MessageBean wholeMessage = new MessageBean(null, messageType);
             for (FormMessage message : this.messages) {
-                String formattedMessageText = formatMessageMessage(message, messagesBundle, locale);
+                String formattedMessageText = formatMessage(message, messagesBundle, locale);
                 if (formattedMessageText != null) {
                     wholeMessage.appendSummaryLine(formattedMessageText);
                     messagesPerField.addMessage(message.getField(), formattedMessageText, messageType);
@@ -323,7 +337,7 @@ import java.util.concurrent.TimeUnit;
         return null;
     }
 
-    protected String formatMessageMessage(FormMessage message, Properties messagesBundle, Locale locale) {
+    protected String formatMessage(FormMessage message, Properties messagesBundle, Locale locale) {
         if (message == null)
             return null;
         if (messagesBundle.containsKey(message.getMessage())) {
@@ -332,22 +346,27 @@ import java.util.concurrent.TimeUnit;
             return message.getMessage();
         }
     }
+
+    @Override
     public FreeMarkerLoginFormsProvider setError(String message, Object... parameters) {
         setMessage(MessageType.ERROR, message, parameters);
         return this;
     }
 
+    @Override
     public LoginFormsProvider setErrors(List<FormMessage> messages) {
         this.messageType = MessageType.ERROR;
         this.messages = new ArrayList<>(messages);
         return this;
     }
 
+    @Override
     public FreeMarkerLoginFormsProvider setSuccess(String message, Object ... parameters) {
         setMessage(MessageType.SUCCESS, message, parameters);
         return this;
     }
 
+    @Override
     public FreeMarkerLoginFormsProvider setWarning(String message, Object ... parameters) {
         setMessage(MessageType.WARNING, message, parameters);
         return this;
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index f694661..fd50eda 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -34,6 +34,7 @@ import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventStoreProvider;
 import org.keycloak.events.EventType;
 import org.keycloak.models.*;
+import org.keycloak.models.utils.FormMessage;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.TimeBasedOTP;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
@@ -71,6 +72,7 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.core.Variant;
+
 import java.lang.reflect.Method;
 import java.net.URI;
 import java.util.HashSet;
@@ -405,10 +407,10 @@ public class AccountService {
 
         UserModel user = auth.getUser();
 
-        String error = Validation.validateUpdateProfileForm(formData);
-        if (error != null) {
+        List<FormMessage> errors = Validation.validateUpdateProfileForm(formData);
+        if (errors != null && !errors.isEmpty()) {
             setReferrerOnPage();
-            return account.setError(error).setProfileFormData(formData).createResponse(AccountPages.ACCOUNT);
+            return account.setErrors(errors).setProfileFormData(formData).createResponse(AccountPages.ACCOUNT);
         }
 
         try {
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 0a7e8b1..c941cc4 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -31,10 +31,10 @@ import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.jose.jws.JWSBuilder;
-import org.keycloak.login.FormMessage;
 import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.*;
 import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.models.utils.FormMessage;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.TimeBasedOTP;
 import org.keycloak.protocol.LoginProtocol;
@@ -618,9 +618,9 @@ public class LoginActionsService {
 
         initEvent(clientSession);
 
-        String error = Validation.validateUpdateProfileForm(formData);
-        if (error != null) {
-            return Flows.forms(session, realm, null, uriInfo, headers).setUser(user).setError(error)
+        List<FormMessage> errors = Validation.validateUpdateProfileForm(formData);
+        if (errors != null && !errors.isEmpty()) {
+            return Flows.forms(session, realm, null, uriInfo, headers).setUser(user).setErrors(errors)
                     .setClientSessionCode(accessCode.getCode())
                     .createResponse(RequiredAction.UPDATE_PROFILE);
         }
diff --git a/services/src/main/java/org/keycloak/services/validation/Validation.java b/services/src/main/java/org/keycloak/services/validation/Validation.java
index 5fb98f9..1a4392b 100755
--- a/services/src/main/java/org/keycloak/services/validation/Validation.java
+++ b/services/src/main/java/org/keycloak/services/validation/Validation.java
@@ -6,9 +6,9 @@ import java.util.regex.Pattern;
 
 import javax.ws.rs.core.MultivaluedMap;
 
-import org.keycloak.login.FormMessage;
 import org.keycloak.models.PasswordPolicy;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.FormMessage;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.messages.Messages;
 
@@ -67,24 +67,24 @@ public class Validation {
     }
 
 
-    public static String validateUpdateProfileForm(MultivaluedMap<String, String> formData) {
+    public static List<FormMessage> validateUpdateProfileForm(MultivaluedMap<String, String> formData) {
+        List<FormMessage> errors = new ArrayList<>();
+        
         if (isEmpty(formData.getFirst(FIELD_FIRST_NAME))) {
-            return Messages.MISSING_FIRST_NAME;
+            addError(errors, FIELD_FIRST_NAME, Messages.MISSING_FIRST_NAME);
         }
 
         if (isEmpty(formData.getFirst(FIELD_LAST_NAME))) {
-            return Messages.MISSING_LAST_NAME;
+            addError(errors, FIELD_LAST_NAME, Messages.MISSING_LAST_NAME);
         }
 
         if (isEmpty(formData.getFirst(FIELD_EMAIL))) {
-            return Messages.MISSING_EMAIL;
-        }
-
-        if (!isEmailValid(formData.getFirst(FIELD_EMAIL))) {
-            return Messages.INVALID_EMAIL;
+            addError(errors, FIELD_EMAIL, Messages.MISSING_EMAIL);
+        } else if (!isEmailValid(formData.getFirst(FIELD_EMAIL))) {
+            addError(errors, FIELD_EMAIL, Messages.INVALID_EMAIL);
         }
 
-        return null;
+        return errors;
     }
 
     public static boolean isEmpty(String s) {