keycloak-uncached

Merge pull request #1551 from stianst/master KEYCLOAK-1761

8/20/2015 12:19:47 PM

Changes

Details

diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index c576a5d..e49842b 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -289,10 +289,9 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
             }
             JsonWebToken token = jws.readJsonContent(JsonWebToken.class);
 
-            String aud = token.getAudience();
             String iss = token.getIssuer();
 
-            if (aud != null && !aud.equals(getConfig().getClientId())) {
+            if (!token.hasAudience(getConfig().getClientId())) {
                 throw new IdentityBrokerException("Wrong audience from token.");
             }
 
diff --git a/core/src/main/java/org/keycloak/json/StringOrArrayDeserializer.java b/core/src/main/java/org/keycloak/json/StringOrArrayDeserializer.java
new file mode 100644
index 0000000..00b8173
--- /dev/null
+++ b/core/src/main/java/org/keycloak/json/StringOrArrayDeserializer.java
@@ -0,0 +1,30 @@
+package org.keycloak.json;
+
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public class StringOrArrayDeserializer extends JsonDeserializer<Object> {
+
+    @Override
+    public Object deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+        JsonNode jsonNode = jsonParser.readValueAsTree();
+        if (jsonNode.isArray()) {
+            ArrayList<String> a = new ArrayList<>(1);
+            Iterator<JsonNode> itr = jsonNode.iterator();
+            while (itr.hasNext()) {
+                a.add(itr.next().getTextValue());
+            }
+            return a.toArray(new String[a.size()]);
+        } else {
+            return new String[] { jsonNode.getTextValue() };
+        }
+    }
+
+}
diff --git a/core/src/main/java/org/keycloak/json/StringOrArraySerializer.java b/core/src/main/java/org/keycloak/json/StringOrArraySerializer.java
new file mode 100644
index 0000000..f9b3547
--- /dev/null
+++ b/core/src/main/java/org/keycloak/json/StringOrArraySerializer.java
@@ -0,0 +1,25 @@
+package org.keycloak.json;
+
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.map.JsonSerializer;
+import org.codehaus.jackson.map.SerializerProvider;
+
+import java.io.IOException;
+
+public class StringOrArraySerializer extends JsonSerializer<Object> {
+    @Override
+    public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+        String[] array = (String[]) o;
+        if (array == null) {
+            jsonGenerator.writeNull();
+        } else if (array.length == 1) {
+            jsonGenerator.writeString(array[0]);
+        } else {
+            jsonGenerator.writeStartArray();
+            for (String s : array) {
+                jsonGenerator.writeString(s);
+            }
+            jsonGenerator.writeEndArray();
+        }
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/AccessToken.java b/core/src/main/java/org/keycloak/representations/AccessToken.java
index 1380c2f..092298e 100755
--- a/core/src/main/java/org/keycloak/representations/AccessToken.java
+++ b/core/src/main/java/org/keycloak/representations/AccessToken.java
@@ -164,11 +164,6 @@ public class AccessToken extends IDToken {
     }
 
     @Override
-    public AccessToken audience(String audience) {
-        return (AccessToken) super.audience(audience);
-    }
-
-    @Override
     public AccessToken subject(String subject) {
         return (AccessToken) super.subject(subject);
     }
diff --git a/core/src/main/java/org/keycloak/representations/IDToken.java b/core/src/main/java/org/keycloak/representations/IDToken.java
index 42f7679..499180b 100755
--- a/core/src/main/java/org/keycloak/representations/IDToken.java
+++ b/core/src/main/java/org/keycloak/representations/IDToken.java
@@ -1,12 +1,6 @@
 package org.keycloak.representations;
 
-import org.codehaus.jackson.annotate.JsonAnyGetter;
-import org.codehaus.jackson.annotate.JsonAnySetter;
 import org.codehaus.jackson.annotate.JsonProperty;
-import org.codehaus.jackson.annotate.JsonUnwrapped;
-
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
diff --git a/core/src/main/java/org/keycloak/representations/JsonWebToken.java b/core/src/main/java/org/keycloak/representations/JsonWebToken.java
index f714d4f..b547fca 100755
--- a/core/src/main/java/org/keycloak/representations/JsonWebToken.java
+++ b/core/src/main/java/org/keycloak/representations/JsonWebToken.java
@@ -4,6 +4,10 @@ import org.codehaus.jackson.annotate.JsonAnyGetter;
 import org.codehaus.jackson.annotate.JsonAnySetter;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.keycloak.json.StringOrArrayDeserializer;
+import org.keycloak.json.StringOrArraySerializer;
 import org.keycloak.util.Time;
 
 import java.io.Serializable;
@@ -26,14 +30,16 @@ public class JsonWebToken implements Serializable {
     @JsonProperty("iss")
     protected String issuer;
     @JsonProperty("aud")
-    protected String audience;
+    @JsonSerialize(using = StringOrArraySerializer.class)
+    @JsonDeserialize(using = StringOrArrayDeserializer.class)
+    protected String[] audience;
     @JsonProperty("sub")
     protected String subject;
     @JsonProperty("typ")
     protected String type;
     @JsonProperty("azp")
     public String issuedFor;
-    protected Map<String, Object> otherClaims = new HashMap<String, Object>();
+    protected Map<String, Object> otherClaims = new HashMap<>();
 
     public String getId() {
         return id;
@@ -72,7 +78,6 @@ public class JsonWebToken implements Serializable {
     @JsonIgnore
     public boolean isNotBefore() {
         return Time.currentTime() >= notBefore;
-
     }
 
     /**
@@ -113,12 +118,21 @@ public class JsonWebToken implements Serializable {
         return this;
     }
 
-
-    public String getAudience() {
+    @JsonIgnore
+    public String[] getAudience() {
         return audience;
     }
 
-    public JsonWebToken audience(String audience) {
+    public boolean hasAudience(String audience) {
+        for (String a : this.audience) {
+            if (a.equals(audience)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public JsonWebToken audience(String... audience) {
         this.audience = audience;
         return this;
     }
diff --git a/core/src/main/java/org/keycloak/util/JsonSerialization.java b/core/src/main/java/org/keycloak/util/JsonSerialization.java
index ff080de..a1a93ba 100755
--- a/core/src/main/java/org/keycloak/util/JsonSerialization.java
+++ b/core/src/main/java/org/keycloak/util/JsonSerialization.java
@@ -3,7 +3,6 @@ package org.keycloak.util;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.SerializationConfig;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
-import org.codehaus.jackson.type.TypeReference;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/core/src/test/java/org/keycloak/jose/JsonWebTokenTest.java b/core/src/test/java/org/keycloak/jose/JsonWebTokenTest.java
new file mode 100644
index 0000000..dbe0ecb
--- /dev/null
+++ b/core/src/test/java/org/keycloak/jose/JsonWebTokenTest.java
@@ -0,0 +1,44 @@
+package org.keycloak.jose;
+
+import org.junit.Test;
+import org.keycloak.representations.JsonWebToken;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+/**
+ * Created by st on 20.08.15.
+ */
+public class JsonWebTokenTest {
+
+    @Test
+    public void testAudSingle() throws IOException {
+        String single = "{ \"aud\": \"test\" }";
+        JsonWebToken s = JsonSerialization.readValue(single, JsonWebToken.class);
+        assertArrayEquals(new String[] { "test" }, s.getAudience());
+    }
+
+    @Test
+    public void testAudArray() throws IOException {
+        String single = "{ \"aud\": [\"test\"] }";
+        JsonWebToken s = JsonSerialization.readValue(single, JsonWebToken.class);
+        assertArrayEquals(new String[]{"test"}, s.getAudience());
+    }
+
+    @Test
+    public void test() throws IOException {
+        JsonWebToken jsonWebToken = new JsonWebToken();
+        jsonWebToken.audience("test");
+        assertTrue(JsonSerialization.writeValueAsPrettyString(jsonWebToken).contains("\"aud\" : \"test\""));
+    }
+
+    @Test
+    public void testArray() throws IOException {
+        JsonWebToken jsonWebToken = new JsonWebToken();
+        jsonWebToken.audience("test", "test2");
+        assertTrue(JsonSerialization.writeValueAsPrettyString(jsonWebToken).contains("\"aud\" : [ \"test\", \"test2\" ]"));
+    }
+
+}
diff --git a/docbook/reference/en/en-US/modules/javascript-adapter.xml b/docbook/reference/en/en-US/modules/javascript-adapter.xml
index 996f29a..2e9f941 100755
--- a/docbook/reference/en/en-US/modules/javascript-adapter.xml
+++ b/docbook/reference/en/en-US/modules/javascript-adapter.xml
@@ -177,6 +177,7 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
                 <listitem>resourceAccess - the resource roles assocaited with the token</listitem>
                 <listitem>refreshToken - the base64 encoded token that can be used to retrieve a new token</listitem>
                 <listitem>refreshTokenParsed - the parsed refresh token</listitem>
+                <listitem>timeSkew - estimated skew between local time and Keycloak server in seconds</listitem>
             </itemizedlist>
         </section>
 
diff --git a/docbook/reference/en/en-US/modules/jboss-adapter.xml b/docbook/reference/en/en-US/modules/jboss-adapter.xml
index 6349c45..71e599d 100755
--- a/docbook/reference/en/en-US/modules/jboss-adapter.xml
+++ b/docbook/reference/en/en-US/modules/jboss-adapter.xml
@@ -167,6 +167,9 @@ public class CustomerService {
         <auth-constraint>
             <role-name>admin</role-name>
         </auth-constraint>
+        <user-data-constraint>
+            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+        </user-data-constraint>
     </security-constraint>
     <security-constraint>
         <web-resource-collection>
@@ -176,12 +179,6 @@ public class CustomerService {
         <auth-constraint>
             <role-name>user</role-name>
         </auth-constraint>
-    </security-constraint>
-
-    <security-constraint>
-        <web-resource-collection>
-            <url-pattern>/*</url-pattern>
-        </web-resource-collection>
         <user-data-constraint>
             <transport-guarantee>CONFIDENTIAL</transport-guarantee>
         </user-data-constraint>
@@ -235,7 +232,7 @@ public class CustomerService {
 </programlisting>
 </para>
         <para>
-            The <literal>security-deployment</literal> <literal>name</literal> attribute identifies the WAR you want
+            The <literal>secure-deployment</literal> <literal>name</literal> attribute identifies the WAR you want
             to secure.  Its value is the <literal>module-name</literal> defined in <literal>web.xml</literal> with
             <literal>.war</literal> appended.  The rest of the configuration corresponds pretty much one to one
             with the <literal>keycloak.json</literal> configuration options defined in <link linkend='adapter-config'>general adapter configuration</link>.
diff --git a/docbook/reference/en/en-US/modules/jetty9-adapter.xml b/docbook/reference/en/en-US/modules/jetty9-adapter.xml
index 30e3878..dea36fd 100755
--- a/docbook/reference/en/en-US/modules/jetty9-adapter.xml
+++ b/docbook/reference/en/en-US/modules/jetty9-adapter.xml
@@ -132,12 +132,6 @@ $ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak
         <auth-constraint>
             <role-name>user</role-name>
         </auth-constraint>
-    </security-constraint>
-
-    <security-constraint>
-        <web-resource-collection>
-            <url-pattern>/*</url-pattern>
-        </web-resource-collection>
         <user-data-constraint>
             <transport-guarantee>CONFIDENTIAL</transport-guarantee>
         </user-data-constraint>
diff --git a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
index a49d432..10027b9 100755
--- a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
+++ b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml
@@ -104,6 +104,14 @@
                     impacts on performance.
                 </para>
             </simplesect>
+            <simplesect>
+                <title>Contact details removed from registration and account management</title>
+                <para>
+                    In the default theme we have now removed the contact details from the registration page and account management. The admin console now lists
+                    all the users attributes, not just contact specific attributes. The admin console also has the ability to add/remove attributes to a user.
+                    If you want to add contact details, please refer to the address theme included in the examples.
+                </para>
+            </simplesect>
         </section>
         <section>
             <title>Migrating to 1.3.0.Final</title>
diff --git a/examples/js-console/src/main/webapp/index.html b/examples/js-console/src/main/webapp/index.html
index 153053b..d85b63b 100644
--- a/examples/js-console/src/main/webapp/index.html
+++ b/examples/js-console/src/main/webapp/index.html
@@ -49,7 +49,7 @@
             if (refreshed) {
                 output(keycloak.tokenParsed);
             } else {
-                output('Token not refreshed, valid for ' + Math.round(keycloak.tokenParsed.exp - new Date().getTime() / 1000) + ' seconds');
+                output('Token not refreshed, valid for ' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
             }
         }).error(function() {
             output('Failed to refresh token');
@@ -62,11 +62,11 @@
             return;
         }
 
-        var o = 'Token Expires:\t\t' + new Date(keycloak.tokenParsed.exp * 1000).toLocaleString() + '\n';
-        o += 'Token Expires in:\t' + Math.round(keycloak.tokenParsed.exp - new Date().getTime() / 1000) + ' seconds\n';
+        var o = 'Token Expires:\t\t' + new Date((keycloak.tokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
+        o += 'Token Expires in:\t' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds\n';
 
-        o += 'Refresh Token Expires:\t' + new Date(keycloak.refreshTokenParsed.exp * 1000).toLocaleString() + '\n';
-        o += 'Refresh Expires in:\t' + Math.round(keycloak.refreshTokenParsed.exp - new Date().getTime() / 1000) + ' seconds';
+        o += 'Refresh Token Expires:\t' + new Date((keycloak.refreshTokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
+        o += 'Refresh Expires in:\t' + Math.round(keycloak.refreshTokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds';
         output(o);
     }
 
diff --git a/examples/themes/README.md b/examples/themes/README.md
index 69c922b..ba47b3c 100644
--- a/examples/themes/README.md
+++ b/examples/themes/README.md
@@ -26,6 +26,14 @@ Then open $KEYCLOAK_HOME/standalone/configuration/keycloak-server.json and regis
     }
 
 
+Address Theme
+-------------------
+
+Example theme that adds address fields to registration page, account management and admin console. To enable the theme open the admin console, select your realm, click on `Theme`. In the dropdown for `Login Theme` and `Account Theme` select `address`. Click `Save` and login to the realm to see the new theme in action.
+
+One thing to note is that to change the admin console for the master admin console (`/auth/admin`) you need to change the theme for the master realm. Changing the admin console theme for any other realms will only change the admin console for that specific realm (for example `/auth/admin/myrealm/console`).
+
+
 Sunrise Login Theme
 -------------------
 
diff --git a/examples/themes/src/main/resources/META-INF/keycloak-themes.json b/examples/themes/src/main/resources/META-INF/keycloak-themes.json
index 12ee694..aafb6e8 100644
--- a/examples/themes/src/main/resources/META-INF/keycloak-themes.json
+++ b/examples/themes/src/main/resources/META-INF/keycloak-themes.json
@@ -1,5 +1,8 @@
 {
     "themes": [{
+        "name" : "address",
+        "types": [ "admin", "account", "login" ]
+    }, {
         "name" : "logo-example",
         "types": [ "admin", "account", "login", "welcome" ]
     }, {
diff --git a/examples/themes/src/main/resources/theme/address/account/account.ftl b/examples/themes/src/main/resources/theme/address/account/account.ftl
new file mode 100755
index 0000000..d2a6af1
--- /dev/null
+++ b/examples/themes/src/main/resources/theme/address/account/account.ftl
@@ -0,0 +1,114 @@
+<#import "template.ftl" as layout>
+<@layout.mainLayout active='account' bodyClass='user'; section>
+
+    <div class="row">
+        <div class="col-md-10">
+            <h2>${msg("editAccountHtmlTtile")}</h2>
+        </div>
+        <div class="col-md-2 subtitle">
+            <span class="subtitle"><span class="required">*</span> ${msg("requiredFields")}</span>
+        </div>
+    </div>
+
+    <form action="${url.accountUrl}" class="form-horizontal" method="post">
+
+        <input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
+
+        <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> <#if realm.editUsernameAllowed><span class="required">*</span></#if>
+            </div>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="username" name="username" <#if !realm.editUsernameAllowed>disabled="disabled"</#if> value="${(account.username!'')?html}"/>
+            </div>
+        </div>
+
+        <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>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="email" name="email" autofocus value="${(account.email!'')?html}"/>
+            </div>
+        </div>
+
+        <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>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="firstName" name="firstName" value="${(account.firstName!'')?html}"/>
+            </div>
+        </div>
+
+        <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>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="lastName" name="lastName" value="${(account.lastName!'')?html}"/>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div class="col-sm-2 col-md-2">
+                <label for="user.attributes.street" class="control-label">${msg("street")}</label>
+            </div>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="user.attributes.street" name="user.attributes.street" value="${(account.attributes.street!'')?html}"/>
+            </div>
+        </div>
+        <div class="form-group">
+            <div class="col-sm-2 col-md-2">
+                <label for="user.attributes.locality" class="control-label">${msg("locality")}</label>
+            </div>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="user.attributes.locality" name="user.attributes.locality" value="${(account.attributes.locality!'')?html}"/>
+            </div>
+        </div>
+        <div class="form-group">
+            <div class="col-sm-2 col-md-2">
+                <label for="user.attributes.region" class="control-label">${msg("region")}</label>
+            </div>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="user.attributes.region" name="user.attributes.region" value="${(account.attributes.region!'')?html}"/>
+            </div>
+        </div>
+        <div class="form-group">
+            <div class="col-sm-2 col-md-2">
+                <label for="user.attributes.postal_code" class="control-label">${msg("postal_code")}</label>
+            </div>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="user.attributes.postal_code" name="user.attributes.postal_code" value="${(account.attributes.postal_code!'')?html}"/>
+            </div>
+        </div>
+        <div class="form-group">
+            <div class="col-sm-2 col-md-2">
+                <label for="user.attributes.country" class="control-label">${msg("country")}</label>
+            </div>
+
+            <div class="col-sm-10 col-md-10">
+                <input type="text" class="form-control" id="user.attributes.country" name="user.attributes.country" value="${(account.attributes.country!'')?html}"/>
+            </div>
+        </div>
+
+        <div class="form-group">
+            <div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
+                <div class="">
+                    <#if url.referrerURI??><a href="${url.referrerURI}">${msg("backToApplication")}/a></#if>
+                    <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Save">${msg("doSave")}</button>
+                    <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Cancel">${msg("doCancel")}</button>
+                </div>
+            </div>
+        </div>
+    </form>
+
+</@layout.mainLayout>
\ No newline at end of file
diff --git a/examples/themes/src/main/resources/theme/address/account/theme.properties b/examples/themes/src/main/resources/theme/address/account/theme.properties
new file mode 100644
index 0000000..512d633
--- /dev/null
+++ b/examples/themes/src/main/resources/theme/address/account/theme.properties
@@ -0,0 +1 @@
+parent=keycloak
\ No newline at end of file
diff --git a/examples/themes/src/main/resources/theme/address/admin/theme.properties b/examples/themes/src/main/resources/theme/address/admin/theme.properties
new file mode 100644
index 0000000..512d633
--- /dev/null
+++ b/examples/themes/src/main/resources/theme/address/admin/theme.properties
@@ -0,0 +1 @@
+parent=keycloak
\ No newline at end of file
diff --git a/examples/themes/src/main/resources/theme/address/login/login-update-profile.ftl b/examples/themes/src/main/resources/theme/address/login/login-update-profile.ftl
new file mode 100755
index 0000000..8be620d
--- /dev/null
+++ b/examples/themes/src/main/resources/theme/address/login/login-update-profile.ftl
@@ -0,0 +1,95 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout; section>
+    <#if section = "title">
+        ${msg("loginProfileTitle")}
+    <#elseif section = "header">
+        ${msg("loginProfileTitle")}
+    <#elseif section = "form">
+        <form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginUpdateProfileUrl}" method="post">
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('email',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" id="email" name="email" value="${(user.email!'')?html}" class="${properties.kcInputClass!}" />
+                </div>
+            </div>
+
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('firstName',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" id="firstName" name="firstName" value="${(user.firstName!'')?html}" class="${properties.kcInputClass!}" />
+                </div>
+            </div>
+
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('lastName',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" id="lastName" name="lastName" value="${(user.lastName!'')?html}" class="${properties.kcInputClass!}" />
+                </div>
+            </div>
+            
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.street" class="${properties.kcLabelClass!}">${msg("street")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.street" name="user.attributes.street" value="${(user.attributes.street!'')?html}"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.locality" class="${properties.kcLabelClass!}">${msg("locality")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.locality" name="user.attributes.locality" value="${(user.attributes.locality!'')?html}"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.region" class="${properties.kcLabelClass!}">${msg("region")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.region" name="user.attributes.region" value="${(user.attributes.region!'')?html}"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.postal_code" class="${properties.kcLabelClass!}">${msg("postal_code")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.postal_code" name="user.attributes.postal_code" value="${(user.attributes.postal_code!'')?html}"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.country" class="${properties.kcLabelClass!}">${msg("country")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.country" name="user.attributes.country" value="${(user.attributes.country!'')?html}"/>
+                </div>
+            </div>
+            
+
+            <div class="${properties.kcFormGroupClass!}">
+                <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+                    <div class="${properties.kcFormOptionsWrapperClass!}">
+                    </div>
+                </div>
+
+                <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+                    <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}" />
+                </div>
+            </div>
+        </form>
+    </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/examples/themes/src/main/resources/theme/address/login/register.ftl b/examples/themes/src/main/resources/theme/address/login/register.ftl
new file mode 100755
index 0000000..3247305
--- /dev/null
+++ b/examples/themes/src/main/resources/theme/address/login/register.ftl
@@ -0,0 +1,131 @@
+<#import "template.ftl" as layout>
+<@layout.registrationLayout; section>
+    <#if section = "title">
+        ${msg("registerWithTitle",(realm.name!''))}
+    <#elseif section = "header">
+         ${msg("registerWithTitleHtml",(realm.name!''))}
+    <#elseif section = "form">
+        <form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.registrationAction}" method="post">
+          <#if !realm.registrationEmailAsUsername>
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('username',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" id="username" class="${properties.kcInputClass!}" name="username" value="${(register.formData.username!'')?html}" />
+                </div>
+            </div>
+          </#if>
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('firstName',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" id="firstName" class="${properties.kcInputClass!}" name="firstName" value="${(register.formData.firstName!'')?html}" />
+                </div>
+            </div>
+
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('lastName',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" id="lastName" class="${properties.kcInputClass!}" name="lastName" value="${(register.formData.lastName!'')?html}" />
+                </div>
+            </div>
+
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('email',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" id="email" class="${properties.kcInputClass!}" name="email" value="${(register.formData.email!'')?html}" />
+                </div>
+            </div>
+
+            <#if passwordRequired>
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('password',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="password" id="password" class="${properties.kcInputClass!}" name="password" />
+                </div>
+            </div>
+
+            <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('password-confirm',properties.kcFormGroupErrorClass!)}">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
+                </div>
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="password" id="password-confirm" class="${properties.kcInputClass!}" name="password-confirm" />
+                </div>
+            </div>
+            </#if>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.street" class="${properties.kcLabelClass!}">${msg("street")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.street" name="user.attributes.street" value="${(register.formData['user.attributes.street']!'')?html}"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.locality" class="${properties.kcLabelClass!}">${msg("locality")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.locality" name="user.attributes.locality" value="${(register.formData['user.attributes.locality']!'')?html}"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.region" class="${properties.kcLabelClass!}">${msg("region")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.region" name="user.attributes.region" value="${(register.formData['user.attributes.region']!'')?html}"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.postal_code" class="${properties.kcLabelClass!}">${msg("postal_code")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.postal_code" name="user.attributes.postal_code" value="${(register.formData['user.attributes.postal_code']!'')?html}"/>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="${properties.kcLabelWrapperClass!}">
+                    <label for="user.attributes.country" class="${properties.kcLabelClass!}">${msg("country")}</label>
+                </div>
+
+                <div class="${properties.kcInputWrapperClass!}">
+                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.country" name="user.attributes.country" value="${(register.formData['user.attributes.country']!'')?html}"/>
+                </div>
+            </div>
+            <#if recaptchaRequired??>
+            <div class="form-group">
+                <div class="${properties.kcInputWrapperClass!}">
+                    <div class="g-recaptcha" data-size="compact" data-sitekey="${recaptchaSiteKey}"></div>
+                </div>
+            </div>
+            </#if>
+
+            <div class="${properties.kcFormGroupClass!}">
+                <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
+                    <div class="${properties.kcFormOptionsWrapperClass!}">
+                        <span><a href="${url.loginUrl}">${msg("backToLogin")}</a></span>
+                    </div>
+                </div>
+
+                <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
+                    <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doRegister")}"/>
+                </div>
+            </div>
+        </form>
+    </#if>
+</@layout.registrationLayout>
\ No newline at end of file
diff --git a/examples/themes/src/main/resources/theme/address/login/theme.properties b/examples/themes/src/main/resources/theme/address/login/theme.properties
new file mode 100644
index 0000000..512d633
--- /dev/null
+++ b/examples/themes/src/main/resources/theme/address/login/theme.properties
@@ -0,0 +1 @@
+parent=keycloak
\ No newline at end of file
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 d2a6af1..60a21b5 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
@@ -55,52 +55,6 @@
         </div>
 
         <div class="form-group">
-            <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.street" class="control-label">${msg("street")}</label>
-            </div>
-
-            <div class="col-sm-10 col-md-10">
-                <input type="text" class="form-control" id="user.attributes.street" name="user.attributes.street" value="${(account.attributes.street!'')?html}"/>
-            </div>
-        </div>
-        <div class="form-group">
-            <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.locality" class="control-label">${msg("locality")}</label>
-            </div>
-
-            <div class="col-sm-10 col-md-10">
-                <input type="text" class="form-control" id="user.attributes.locality" name="user.attributes.locality" value="${(account.attributes.locality!'')?html}"/>
-            </div>
-        </div>
-        <div class="form-group">
-            <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.region" class="control-label">${msg("region")}</label>
-            </div>
-
-            <div class="col-sm-10 col-md-10">
-                <input type="text" class="form-control" id="user.attributes.region" name="user.attributes.region" value="${(account.attributes.region!'')?html}"/>
-            </div>
-        </div>
-        <div class="form-group">
-            <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.postal_code" class="control-label">${msg("postal_code")}</label>
-            </div>
-
-            <div class="col-sm-10 col-md-10">
-                <input type="text" class="form-control" id="user.attributes.postal_code" name="user.attributes.postal_code" value="${(account.attributes.postal_code!'')?html}"/>
-            </div>
-        </div>
-        <div class="form-group">
-            <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.country" class="control-label">${msg("country")}</label>
-            </div>
-
-            <div class="col-sm-10 col-md-10">
-                <input type="text" class="form-control" id="user.attributes.country" name="user.attributes.country" value="${(account.attributes.country!'')?html}"/>
-            </div>
-        </div>
-
-        <div class="form-group">
             <div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
                 <div class="">
                     <#if url.referrerURI??><a href="${url.referrerURI}">${msg("backToApplication")}/a></#if>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 2a37bc0..d0fc402 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -345,6 +345,18 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'UserDetailCtrl'
         })
+        .when('/realms/:realm/users/:user/user-attributes', {
+            templateUrl : resourceUrl + '/partials/user-attributes.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                user : function(UserLoader) {
+                    return UserLoader();
+                }
+            },
+            controller : 'UserDetailCtrl'
+        })
         .when('/realms/:realm/users/:user/user-credentials', {
             templateUrl : resourceUrl + '/partials/user-credentials.html',
             resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 802b202..b693827 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -450,6 +450,15 @@ module.controller('UserDetailCtrl', function($scope, realm, user, BruteForceUser
     $scope.cancel = function() {
         $location.url("/realms/" + realm.realm + "/users");
     };
+
+    $scope.addAttribute = function() {
+        $scope.user.attributes[$scope.newAttribute.key] = $scope.newAttribute.value;
+        delete $scope.newAttribute;
+    }
+
+    $scope.removeAttribute = function(key) {
+        delete $scope.user.attributes[key];
+    }
 });
 
 module.controller('UserCredentialsCtrl', function($scope, realm, user, User, UserCredentials, Notifications, Dialog) {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html
new file mode 100755
index 0000000..542431d
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html
@@ -0,0 +1,45 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/users">Users</a></li>
+        <li>{{user.username}}</li>
+    </ol>
+
+    <kc-tabs-user></kc-tabs-user>
+
+    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageUsers">
+        <table class="table table-striped table-bordered">
+            <thead>
+            <tr>
+                <th>Key</th>
+                <th>Value</th>
+                <th>Actions</th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr ng-repeat="(key, value) in (user.attributes | filter:search)">
+                <td>{{key}}</td>
+                <td><input ng-model="user.attributes[key]" class="form-control" type="text" name="{{key}}" id="attribute-{{key}}" /></td>
+                <td class="kc-action-cell">
+                    <button class="btn btn-default btn-block btn-sm" data-ng-click="removeAttribute(key)">Delete</button>
+                </td>
+            </tr>
+            <tr>
+                <td><input ng-model="newAttribute.key" class="form-control" type="text" id="newAttributeKey" /></td>
+                <td><input ng-model="newAttribute.value" class="form-control" type="text" id="newAttributeValue" /></td>
+                <td class="kc-action-cell">
+                    <button class="btn btn-default btn-block btn-sm" data-ng-click="addAttribute()">Add</button>
+                </td>
+            </tr>
+            </tbody>
+        </table>
+
+        <div class="form-group" data-ng-show="access.manageUsers">
+            <div class="col-md-12">
+                <button kc-save  data-ng-disabled="!changed">Save</button>
+                <button kc-reset data-ng-disabled="!changed">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
index 006f83f..feedea5 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
@@ -123,8 +123,6 @@
 
         </fieldset>
 
-        <div data-ng-include data-src="resourceUrl + '/partials/user-attribute-entry.html'"></div>
-
         <div class="form-group">
             <div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageUsers">
                 <button kc-save data-ng-show="changed">Save</button>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
index e090fca..edc3a66 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
@@ -6,7 +6,8 @@
     <h1 data-ng-show="create">Add User</h1>
 
     <ul class="nav nav-tabs" data-ng-show="!create">
-        <li ng-class="{active: !path[4] && path[0] != 'create'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}">Attributes</a></li>
+        <li ng-class="{active: !path[4] && path[0] != 'create'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}">Details</a></li>
+        <li ng-class="{active: path[4] == 'user-attributes'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-attributes">Attributes</a></li>
         <li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">Credentials</a></li>
         <li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/role-mappings">Role Mappings</a></li>
         <li ng-class="{active: path[4] == 'consents'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/consents">Consents</a></li>
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 8be620d..f80909e 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
@@ -32,53 +32,6 @@
                     <input type="text" id="lastName" name="lastName" value="${(user.lastName!'')?html}" class="${properties.kcInputClass!}" />
                 </div>
             </div>
-            
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.street" class="${properties.kcLabelClass!}">${msg("street")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.street" name="user.attributes.street" value="${(user.attributes.street!'')?html}"/>
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.locality" class="${properties.kcLabelClass!}">${msg("locality")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.locality" name="user.attributes.locality" value="${(user.attributes.locality!'')?html}"/>
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.region" class="${properties.kcLabelClass!}">${msg("region")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.region" name="user.attributes.region" value="${(user.attributes.region!'')?html}"/>
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.postal_code" class="${properties.kcLabelClass!}">${msg("postal_code")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.postal_code" name="user.attributes.postal_code" value="${(user.attributes.postal_code!'')?html}"/>
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.country" class="${properties.kcLabelClass!}">${msg("country")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.country" name="user.attributes.country" value="${(user.attributes.country!'')?html}"/>
-                </div>
-            </div>
-            
 
             <div class="${properties.kcFormGroupClass!}">
                 <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
diff --git a/forms/common-themes/src/main/resources/theme/base/login/register.ftl b/forms/common-themes/src/main/resources/theme/base/login/register.ftl
index 3247305..d1593da 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/register.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/register.ftl
@@ -62,51 +62,7 @@
                 </div>
             </div>
             </#if>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.street" class="${properties.kcLabelClass!}">${msg("street")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.street" name="user.attributes.street" value="${(register.formData['user.attributes.street']!'')?html}"/>
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.locality" class="${properties.kcLabelClass!}">${msg("locality")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.locality" name="user.attributes.locality" value="${(register.formData['user.attributes.locality']!'')?html}"/>
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.region" class="${properties.kcLabelClass!}">${msg("region")}</label>
-                </div>
 
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.region" name="user.attributes.region" value="${(register.formData['user.attributes.region']!'')?html}"/>
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.postal_code" class="${properties.kcLabelClass!}">${msg("postal_code")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.postal_code" name="user.attributes.postal_code" value="${(register.formData['user.attributes.postal_code']!'')?html}"/>
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.country" class="${properties.kcLabelClass!}">${msg("country")}</label>
-                </div>
-
-                <div class="${properties.kcInputWrapperClass!}">
-                    <input type="text" class="${properties.kcInputClass!}"  id="user.attributes.country" name="user.attributes.country" value="${(register.formData['user.attributes.country']!'')?html}"/>
-                </div>
-            </div>
             <#if recaptchaRequired??>
             <div class="form-group">
                 <div class="${properties.kcInputWrapperClass!}">
diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js
index 798bfdd..46d3b18 100755
--- a/integration/js/src/main/resources/keycloak.js
+++ b/integration/js/src/main/resources/keycloak.js
@@ -261,7 +261,7 @@
                 throw 'Not authenticated';
             }
 
-            var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000);
+            var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew;
             if (minValidity) {
                 expiresIn -= minValidity;
             }
@@ -299,11 +299,18 @@
                             params += '&client_id=' + encodeURIComponent(kc.clientId);
                         }
 
+                        var timeLocal = new Date().getTime();
+
                         req.onreadystatechange = function () {
                             if (req.readyState == 4) {
                                 if (req.status == 200) {
+                                    timeLocal = (timeLocal + new Date().getTime()) / 2;
+
                                     var tokenResponse = JSON.parse(req.responseText);
                                     setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']);
+
+                                    kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
+
                                     kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
                                     for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
                                         p.setSuccess(true);
@@ -385,11 +392,18 @@
 
                 req.withCredentials = true;
 
+                var timeLocal = new Date().getTime();
+
                 req.onreadystatechange = function() {
                     if (req.readyState == 4) {
                         if (req.status == 200) {
+                            timeLocal = (timeLocal + new Date().getTime()) / 2;
+
                             var tokenResponse = JSON.parse(req.responseText);
                             setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']);
+
+                            kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
+
                             kc.onAuthSuccess && kc.onAuthSuccess();
                             promise && promise.setSuccess();
                         } else {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
index 5191ed0..3a98c8d 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
@@ -111,13 +111,9 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator {
             }
 
             // Validate other things
-            String audience = token.getAudience();
             String expectedAudience = Urls.realmIssuer(context.getUriInfo().getBaseUri(), realm.getName());
-            if (audience == null) {
-                throw new RuntimeException("Audience is null on JWT");
-            }
-            if (!audience.equals(expectedAudience)) {
-                throw new RuntimeException("Token audience doesn't match domain. Realm audience is '" + expectedAudience + "' but audience from token is '" + audience + "'");
+            if (!token.hasAudience(expectedAudience)) {
+                throw new RuntimeException("Token audience doesn't match domain. Realm audience is '" + expectedAudience + "' but audience from token is '" + token.getAudience() + "'");
             }
 
             if (!token.isActive()) {
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index c5a7f8f..15c5f38 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -190,8 +190,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
 
             if (authResult != null) {
                 AccessToken token = authResult.getToken();
-                String audience = token.getAudience();
-                ClientModel clientModel = this.realmModel.getClientByClientId(audience);
+                String[] audience = token.getAudience();
+                ClientModel clientModel = this.realmModel.getClientByClientId(audience[0]);
 
                 if (clientModel == null) {
                     return badRequest("Invalid client.");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 12ec029..95d644e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -562,7 +562,7 @@ public class AccountTest {
             loginPage.open();
             loginPage.clickRegister();
 
-            registerPage.register("view", "log", "view-log@localhost", "view-log", "password", "password", null);
+            registerPage.register("view", "log", "view-log@localhost", "view-log", "password", "password");
 
             expectedEvents.add(events.poll());
             expectedEvents.add(events.poll());
@@ -609,7 +609,7 @@ public class AccountTest {
         loginPage.open();
         loginPage.clickRegister();
 
-        registerPage.register("view", "sessions", "view-sessions@localhost", "view-sessions", "password", "password", null);
+        registerPage.register("view", "sessions", "view-sessions@localhost", "view-sessions", "password", "password");
 
         Event registerEvent = events.expectRegister("view-sessions", "view-sessions@localhost").assertEvent();
         String userId = registerEvent.getUserId();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
index 38760b9..a9c0c58 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
@@ -145,7 +145,7 @@ public class RequiredActionEmailVerificationTest {
     public void verifyEmailRegister() throws IOException, MessagingException {
         loginPage.open();
         loginPage.clickRegister();
-        registerPage.register("firstName", "lastName", "email@mail.com", "verifyEmail", "password", "password", null);
+        registerPage.register("firstName", "lastName", "email@mail.com", "verifyEmail", "password", "password");
 
         String userId = events.expectRegister("verifyEmail", "email@mail.com").assertEvent().getUserId();
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
index 341a440..4d16a1b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
@@ -121,7 +121,7 @@ public class RequiredActionMultipleActionsTest {
     }
 
     public String updateProfile(String sessionId) {
-        updateProfilePage.update("New first", "New last", "new@email.com", null);
+        updateProfilePage.update("New first", "New last", "new@email.com");
 
         AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_PROFILE);
         if (sessionId != null) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
index a4206aa..dab9428 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
@@ -25,21 +25,16 @@ import org.junit.Assert;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
-import org.keycloak.constants.KerberosConstants;
 import org.keycloak.events.Details;
 import org.keycloak.events.Event;
 import org.keycloak.events.EventType;
-import org.keycloak.models.ClientModel;
 import org.keycloak.models.OTPPolicy;
-import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredActionProviderModel;
-import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.HmacOTP;
 import org.keycloak.models.utils.TimeBasedOTP;
-import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
@@ -117,7 +112,7 @@ public class RequiredActionTotpSetupTest {
     public void setupTotpRegister() {
         loginPage.open();
         loginPage.clickRegister();
-        registerPage.register("firstName", "lastName", "email@mail.com", "setupTotp", "password", "password", null);
+        registerPage.register("firstName", "lastName", "email@mail.com", "setupTotp", "password", "password");
 
         String userId = events.expectRegister("setupTotp", "email@mail.com").assertEvent().getUserId();
 
@@ -170,7 +165,7 @@ public class RequiredActionTotpSetupTest {
         // Register new user
         loginPage.open();
         loginPage.clickRegister();
-        registerPage.register("firstName2", "lastName2", "email2@mail.com", "setupTotp2", "password2", "password2", null);
+        registerPage.register("firstName2", "lastName2", "email2@mail.com", "setupTotp2", "password2", "password2");
 
         String userId = events.expectRegister("setupTotp2", "email2@mail.com").assertEvent().getUserId();
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
index b61ee17..a7c84a0 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
@@ -87,7 +87,7 @@ public class RequiredActionUpdateProfileTest {
 
         updateProfilePage.assertCurrent();
 
-        updateProfilePage.update("New first", "New last", "new@email.com", "mystreet");
+        updateProfilePage.update("New first", "New last", "new@email.com");
 
         String sessionId = events.expectRequiredAction(EventType.UPDATE_PROFILE).assertEvent().getSessionId();
         events.expectRequiredAction(EventType.UPDATE_EMAIL).session(sessionId).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
@@ -101,7 +101,6 @@ public class RequiredActionUpdateProfileTest {
         Assert.assertEquals("New first", user.getFirstName());
         Assert.assertEquals("New last", user.getLastName());
         Assert.assertEquals("new@email.com", user.getEmail());
-        Assert.assertEquals("mystreet", user.getAttributesAsListValues().get("street").get(0));
     }
 
     @Test
@@ -112,7 +111,7 @@ public class RequiredActionUpdateProfileTest {
 
         updateProfilePage.assertCurrent();
 
-        updateProfilePage.update("", "New last", "new@email.com", "mystreet");
+        updateProfilePage.update("", "New last", "new@email.com");
 
         updateProfilePage.assertCurrent();
 
@@ -120,7 +119,6 @@ public class RequiredActionUpdateProfileTest {
         Assert.assertEquals("", updateProfilePage.getFirstName());
         Assert.assertEquals("New last", updateProfilePage.getLastName());
         Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
-        Assert.assertEquals("mystreet", updateProfilePage.getAttributeStreet());
 
         Assert.assertEquals("Please specify first name.", updateProfilePage.getError());
 
@@ -135,7 +133,7 @@ public class RequiredActionUpdateProfileTest {
 
         updateProfilePage.assertCurrent();
 
-        updateProfilePage.update("New first", "", "new@email.com", null);
+        updateProfilePage.update("New first", "", "new@email.com");
 
         updateProfilePage.assertCurrent();
 
@@ -143,7 +141,6 @@ public class RequiredActionUpdateProfileTest {
         Assert.assertEquals("New first", updateProfilePage.getFirstName());
         Assert.assertEquals("", updateProfilePage.getLastName());
         Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
-        Assert.assertEquals("", updateProfilePage.getAttributeStreet());
 
         Assert.assertEquals("Please specify last name.", updateProfilePage.getError());
 
@@ -158,7 +155,7 @@ public class RequiredActionUpdateProfileTest {
 
         updateProfilePage.assertCurrent();
 
-        updateProfilePage.update("New first", "New last", "", "mystreet");
+        updateProfilePage.update("New first", "New last", "");
 
         updateProfilePage.assertCurrent();
 
@@ -166,7 +163,6 @@ public class RequiredActionUpdateProfileTest {
         Assert.assertEquals("New first", updateProfilePage.getFirstName());
         Assert.assertEquals("New last", updateProfilePage.getLastName());
         Assert.assertEquals("", updateProfilePage.getEmail());
-        Assert.assertEquals("mystreet", updateProfilePage.getAttributeStreet());
 
         Assert.assertEquals("Please specify email.", updateProfilePage.getError());
 
@@ -181,7 +177,7 @@ public class RequiredActionUpdateProfileTest {
 
         updateProfilePage.assertCurrent();
 
-        updateProfilePage.update("New first", "New last", "invalidemail", null);
+        updateProfilePage.update("New first", "New last", "invalidemail");
 
         updateProfilePage.assertCurrent();
 
@@ -189,7 +185,6 @@ public class RequiredActionUpdateProfileTest {
         Assert.assertEquals("New first", updateProfilePage.getFirstName());
         Assert.assertEquals("New last", updateProfilePage.getLastName());
         Assert.assertEquals("invalidemail", updateProfilePage.getEmail());
-        Assert.assertEquals("", updateProfilePage.getAttributeStreet());
 
         Assert.assertEquals("Invalid email address.", updateProfilePage.getError());
 
@@ -204,7 +199,7 @@ public class RequiredActionUpdateProfileTest {
 
         updateProfilePage.assertCurrent();
 
-        updateProfilePage.update("New first", "New last", "keycloak-user@localhost", null);
+        updateProfilePage.update("New first", "New last", "keycloak-user@localhost");
 
         updateProfilePage.assertCurrent();
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index e3dabc8..fb2b5df 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -48,6 +48,7 @@ import org.keycloak.testsuite.pages.VerifyEmailPage;
 import org.keycloak.testsuite.rule.GreenMailRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
 import org.openqa.selenium.By;
 import org.openqa.selenium.NoSuchElementException;
 import org.openqa.selenium.WebDriver;
@@ -451,7 +452,7 @@ public abstract class AbstractIdentityProviderTest {
         doAfterProviderAuthentication();
 
         this.updateProfilePage.assertCurrent();
-        this.updateProfilePage.update("Test", "User", "psilva@redhat.com", null);
+        this.updateProfilePage.update("Test", "User", "psilva@redhat.com");
 
         WebElement element = this.driver.findElement(By.className("kc-feedback-text"));
 
@@ -460,7 +461,7 @@ public abstract class AbstractIdentityProviderTest {
         assertEquals("Email already exists.", element.getText());
 
         this.updateProfilePage.assertCurrent();
-        this.updateProfilePage.update("Test", "User", "test-user@redhat.com", null);
+        this.updateProfilePage.update("Test", "User", "test-user@redhat.com");
 
         assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
 
@@ -724,7 +725,7 @@ public abstract class AbstractIdentityProviderTest {
 
             // update profile
             this.updateProfilePage.assertCurrent();
-            this.updateProfilePage.update(userFirstName, userLastName, userEmail, null);
+            this.updateProfilePage.update(userFirstName, userLastName, userEmail);
         }
 
     }
@@ -801,10 +802,9 @@ public abstract class AbstractIdentityProviderTest {
         UserSessionStatus sessionStatus = null;
 
         try {
-            ObjectMapper objectMapper = new ObjectMapper();
             String pageSource = this.driver.getPageSource();
 
-            sessionStatus = objectMapper.readValue(pageSource.getBytes(), UserSessionStatus.class);
+            sessionStatus = JsonSerialization.readValue(pageSource.getBytes(), UserSessionStatus.class);
         } catch (IOException ignore) {
             ignore.printStackTrace();
         }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
index 943adaf..3a86011 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationProvidersIntegrationTest.java
@@ -245,7 +245,6 @@ public class FederationProvidersIntegrationTest {
         Assert.assertEquals("John", profilePage.getFirstName());
         Assert.assertEquals("Doe", profilePage.getLastName());
         Assert.assertEquals("john@email.org", profilePage.getEmail());
-        Assert.assertEquals("1234", profilePage.getPostalCode());
     }
 
     @Test
@@ -296,12 +295,12 @@ public class FederationProvidersIntegrationTest {
         registerPage.assertCurrent();
 
         // check existing username
-        registerPage.register("firstName", "lastName", "email@mail.cz", "existing", "Password1", "Password1", null);
+        registerPage.register("firstName", "lastName", "email@mail.cz", "existing", "Password1", "Password1");
         registerPage.assertCurrent();
         Assert.assertEquals("Username already exists.", registerPage.getError());
 
         // Check existing email
-        registerPage.register("firstName", "lastName", "existing@email.org", "nonExisting", "Password1", "Password1", null);
+        registerPage.register("firstName", "lastName", "existing@email.org", "nonExisting", "Password1", "Password1");
         registerPage.assertCurrent();
         Assert.assertEquals("Email already exists.", registerPage.getError());
     }
@@ -312,7 +311,7 @@ public class FederationProvidersIntegrationTest {
         loginPage.clickRegister();
         registerPage.assertCurrent();
 
-        registerPage.register("firstName", "lastName", "email2@check.cz", "registerUserSuccess2", "Password1", "Password1", null);
+        registerPage.register("firstName", "lastName", "email2@check.cz", "registerUserSuccess2", "Password1", "Password1");
         Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
 
         KeycloakSession session = keycloakRule.startSession();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
index 63a754e..9c2852e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java
@@ -77,7 +77,7 @@ public class RegisterTest {
         loginPage.clickRegister();
         registerPage.assertCurrent();
 
-        registerPage.register("firstName", "lastName", "registerExistingUser@email", "test-user@localhost", "password", "password", "mystreet");
+        registerPage.register("firstName", "lastName", "registerExistingUser@email", "test-user@localhost", "password", "password");
 
         registerPage.assertCurrent();
         Assert.assertEquals("Username already exists.", registerPage.getError());
@@ -89,7 +89,6 @@ public class RegisterTest {
         Assert.assertEquals("", registerPage.getUsername());
         Assert.assertEquals("", registerPage.getPassword());
         Assert.assertEquals("", registerPage.getPasswordConfirm());
-        Assert.assertEquals("mystreet", registerPage.getAttributeStreet());
 
         events.expectRegister("test-user@localhost", "registerExistingUser@email")
                 .removeDetail(Details.EMAIL)
@@ -102,7 +101,7 @@ public class RegisterTest {
         loginPage.clickRegister();
         registerPage.assertCurrent();
 
-        registerPage.register("firstName", "lastName", "registerUserInvalidPasswordConfirm@email", "registerUserInvalidPasswordConfirm", "password", "invalid", null);
+        registerPage.register("firstName", "lastName", "registerUserInvalidPasswordConfirm@email", "registerUserInvalidPasswordConfirm", "password", "invalid");
 
         registerPage.assertCurrent();
         Assert.assertEquals("Password confirmation doesn't match.", registerPage.getError());
@@ -114,7 +113,6 @@ public class RegisterTest {
         Assert.assertEquals("registerUserInvalidPasswordConfirm", registerPage.getUsername());
         Assert.assertEquals("", registerPage.getPassword());
         Assert.assertEquals("", registerPage.getPasswordConfirm());
-        Assert.assertEquals("", registerPage.getAttributeStreet());
 
         events.expectRegister("registerUserInvalidPasswordConfirm", "registerUserInvalidPasswordConfirm@email")
                 .removeDetail(Details.USERNAME)
@@ -128,7 +126,7 @@ public class RegisterTest {
         loginPage.clickRegister();
         registerPage.assertCurrent();
 
-        registerPage.register("firstName", "lastName", "registerUserMissingPassword@email", "registerUserMissingPassword", null, null, null);
+        registerPage.register("firstName", "lastName", "registerUserMissingPassword@email", "registerUserMissingPassword", null, null);
 
         registerPage.assertCurrent();
         Assert.assertEquals("Please specify password.", registerPage.getError());
@@ -153,7 +151,7 @@ public class RegisterTest {
             loginPage.clickRegister();
             registerPage.assertCurrent();
 
-            registerPage.register("firstName", "lastName", "registerPasswordPolicy@email", "registerPasswordPolicy", "pass", "pass", null);
+            registerPage.register("firstName", "lastName", "registerPasswordPolicy@email", "registerPasswordPolicy", "pass", "pass");
 
             registerPage.assertCurrent();
             Assert.assertEquals("Invalid password: minimum length 8.", registerPage.getError());
@@ -163,7 +161,7 @@ public class RegisterTest {
                     .removeDetail(Details.EMAIL)
                     .user((String) null).error("invalid_registration").assertEvent();
 
-            registerPage.register("firstName", "lastName", "registerPasswordPolicy@email", "registerPasswordPolicy", "password", "password", null);
+            registerPage.register("firstName", "lastName", "registerPasswordPolicy@email", "registerPasswordPolicy", "password", "password");
             Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
 
             String userId = events.expectRegister("registerPasswordPolicy", "registerPasswordPolicy@email").assertEvent().getUserId();
@@ -185,7 +183,7 @@ public class RegisterTest {
         loginPage.clickRegister();
         registerPage.assertCurrent();
 
-        registerPage.register("firstName", "lastName", "registerUserMissingUsername@email", null, "password", "password", null);
+        registerPage.register("firstName", "lastName", "registerUserMissingUsername@email", null, "password", "password");
 
         registerPage.assertCurrent();
         Assert.assertEquals("Please specify username.", registerPage.getError());
@@ -202,14 +200,14 @@ public class RegisterTest {
         loginPage.clickRegister();
         registerPage.assertCurrent();
 
-        registerPage.register("firstName", "lastName", null, "registerUserMissingEmail", "password", "password", null);
+        registerPage.register("firstName", "lastName", null, "registerUserMissingEmail", "password", "password");
         registerPage.assertCurrent();
         Assert.assertEquals("Please specify email.", registerPage.getError());
         events.expectRegister("registerUserMissingEmail", null)
                 .removeDetail("email")
                 .error("invalid_registration").assertEvent();
 
-        registerPage.register("firstName", "lastName", "registerUserInvalidEmailemail", "registerUserInvalidEmail", "password", "password", null);
+        registerPage.register("firstName", "lastName", "registerUserInvalidEmailemail", "registerUserInvalidEmail", "password", "password");
         registerPage.assertCurrent();
         Assert.assertEquals("Invalid email address.", registerPage.getError());
         events.expectRegister("registerUserInvalidEmail", "registerUserInvalidEmailemail")
@@ -222,7 +220,7 @@ public class RegisterTest {
         loginPage.clickRegister();
         registerPage.assertCurrent();
 
-        registerPage.register("firstName", "lastName", "registerUserSuccess@email", "registerUserSuccess", "password", "password", "myStreet");
+        registerPage.register("firstName", "lastName", "registerUserSuccess@email", "registerUserSuccess", "password", "password");
 
         Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
 
@@ -239,7 +237,6 @@ public class RegisterTest {
         Assert.assertEquals("registerusersuccess@email", user.getEmail());
         Assert.assertEquals("firstName", user.getFirstName());
         Assert.assertEquals("lastName", user.getLastName());
-        Assert.assertEquals("myStreet", user.getAttribute("street").get(0));
     }
 
     protected UserModel getUser(String userId) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
index 18bc795..c013345 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java
@@ -47,9 +47,6 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
     @FindBy(id = "email")
     private WebElement emailInput;
 
-    @FindBy(id = "user.attributes.postal_code")
-    private WebElement postalCodeInput;
-
 
     @FindBy(id = "referrer")
     private WebElement backToApplicationLink;
@@ -107,10 +104,6 @@ public class AccountUpdateProfilePage extends AbstractAccountPage {
         return lastNameInput.getAttribute("value");
     }
 
-    public String getPostalCode() {
-        return postalCodeInput.getAttribute("value");
-    }
-
     public String getEmail() {
         return emailInput.getAttribute("value");
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
index 809150f..d67862c 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
@@ -38,25 +38,19 @@ public class LoginUpdateProfilePage extends AbstractPage {
     @FindBy(id = "email")
     private WebElement emailInput;
 
-    @FindBy(id = "user.attributes.street")
-    private WebElement attributeStreetInput;
-
     @FindBy(css = "input[type=\"submit\"]")
     private WebElement submitButton;
 
     @FindBy(className = "feedback-error")
     private WebElement loginErrorMessage;
 
-    public void update(String firstName, String lastName, String email, String attributeStreet) {
+    public void update(String firstName, String lastName, String email) {
         firstNameInput.clear();
         firstNameInput.sendKeys(firstName);
         lastNameInput.clear();
         lastNameInput.sendKeys(lastName);
         emailInput.clear();
         emailInput.sendKeys(email);
-        attributeStreetInput.clear();
-        if (attributeStreet != null)
-            attributeStreetInput.sendKeys(attributeStreet);
         submitButton.click();
     }
 
@@ -76,10 +70,6 @@ public class LoginUpdateProfilePage extends AbstractPage {
         return emailInput.getAttribute("value");
     }
 
-    public String getAttributeStreet() {
-        return attributeStreetInput.getAttribute("value");
-    }
-
     public boolean isCurrent() {
         return driver.getTitle().equals("Update Account Information");
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/RegisterPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/RegisterPage.java
index 0892b0f..456d0a8 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/RegisterPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/RegisterPage.java
@@ -50,9 +50,6 @@ public class RegisterPage extends AbstractPage {
 
     @FindBy(id = "password-confirm")
     private WebElement passwordConfirmInput;
-    
-    @FindBy(id = "user.attributes.street")
-    private WebElement attributeStreetInput;
 
     @FindBy(css = "input[type=\"submit\"]")
     private WebElement submitButton;
@@ -60,7 +57,7 @@ public class RegisterPage extends AbstractPage {
     @FindBy(className = "feedback-error")
     private WebElement loginErrorMessage;
 
-    public void register(String firstName, String lastName, String email, String username, String password, String passwordConfirm, String attributeStreet) {
+    public void register(String firstName, String lastName, String email, String username, String password, String passwordConfirm) {
         firstNameInput.clear();
         if (firstName != null) {
             firstNameInput.sendKeys(firstName);
@@ -91,11 +88,6 @@ public class RegisterPage extends AbstractPage {
             passwordConfirmInput.sendKeys(passwordConfirm);
         }
 
-        attributeStreetInput.clear();
-        if (attributeStreet != null) {
-            attributeStreetInput.sendKeys(attributeStreet);
-        }
-
         submitButton.click();
     }
 
@@ -163,10 +155,6 @@ public class RegisterPage extends AbstractPage {
         return passwordConfirmInput.getAttribute("value");
     }
 
-    public String getAttributeStreet() {
-        return attributeStreetInput.getAttribute("value");
-    }
-
     public boolean isCurrent() {
         return driver.getTitle().equals("Register with test");
     }