diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index 44458fd..7f0e0a0 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -23,6 +23,7 @@ import org.keycloak.json.StringListMapDeserializer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
@@ -157,7 +158,7 @@ public class UserRepresentation {
public UserRepresentation singleAttribute(String name, String value) {
if (this.attributes == null) attributes = new HashMap<>();
- attributes.put(name, Arrays.asList(value));
+ attributes.put(name, (value == null ? new ArrayList<String>() : Arrays.asList(value)));
return this;
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
index 5acde8b..560a490 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
@@ -86,7 +86,7 @@ public class OIDCAttributeMapperHelper {
}
private static Object convertToType(String type, Object attributeValue) {
- if (type == null) return attributeValue;
+ if (type == null || attributeValue == null) return attributeValue;
switch (type) {
case "boolean":
Boolean booleanObject = getBoolean(attributeValue);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java
index b8dd51f..ad16c2d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java
@@ -52,6 +52,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isEmptyString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -123,7 +124,6 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
user.singleAttribute("formatted", "6 Foo Street");
user.singleAttribute("phone", "617-777-6666");
-
List<String> departments = Arrays.asList("finance", "development");
user.getAttributes().put("departments", departments);
userResource.update(user);
@@ -242,6 +242,62 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
events.clear();
}
+ @Test
+ public void testNullOrEmptyTokenMapping() throws Exception {
+ {
+ UserResource userResource = findUserByUsernameId(adminClient.realm("test"), "test-user@localhost");
+ UserRepresentation user = userResource.toRepresentation();
+
+ user.singleAttribute("empty", "");
+ user.singleAttribute("null", null);
+ userResource.update(user);
+
+ ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
+ app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, "", true, true, false)).close();
+ app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, "", true, true, false)).close();
+ }
+
+ {
+ OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password");
+
+ IDToken idToken = oauth.verifyIDToken(response.getIdToken());
+ Object empty = idToken.getOtherClaims().get("empty");
+ assertThat((empty == null ? null : (String) empty), isEmptyString());
+ Object nulll = idToken.getOtherClaims().get("null");
+ assertNull(nulll);
+
+ AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
+ oauth.openLogout();
+ }
+
+ // undo mappers
+ {
+ ClientResource app = findClientByClientId(adminClient.realm("test"), "test-app");
+ ClientRepresentation clientRepresentation = app.toRepresentation();
+ for (ProtocolMapperRepresentation model : clientRepresentation.getProtocolMappers()) {
+ if (model.getName().equals("empty")
+ || model.getName().equals("null")
+ ) {
+ app.getProtocolMappers().delete(model.getId());
+ }
+ }
+ }
+
+ events.clear();
+
+ {
+ OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password");
+ IDToken idToken = oauth.verifyIDToken(response.getIdToken());
+ assertNull(idToken.getAddress());
+ assertNull(idToken.getOtherClaims().get("empty"));
+ assertNull(idToken.getOtherClaims().get("null"));
+
+ oauth.openLogout();
+ }
+ events.clear();
+ }
+
+
@Test
public void testUserRoleToAttributeMappers() throws Exception {