diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticator.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticator.java index cb9dee314bf..deaebb68921 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticator.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticator.java @@ -670,8 +670,15 @@ public class OpenIdConnectAuthenticator { } else if (value1 instanceof JSONObject) { idToken.put(entry.getKey(), mergeObjects((JSONObject) value1, value2)); } else if (value1.getClass().equals(value2.getClass()) == false) { - throw new IllegalStateException("Error merging ID token and userinfo claim value for claim [" + entry.getKey() + "]. " + - "Cannot merge [" + value1.getClass().getName() + "] with [" + value2.getClass().getName() + "]"); + // A special handling for certain OPs that mix the usage of true and "true" + if (value1 instanceof Boolean && value2 instanceof String && String.valueOf(value1).equals(value2)) { + idToken.put(entry.getKey(), value1); + } else if (value2 instanceof Boolean && value1 instanceof String && String.valueOf(value2).equals(value1)) { + idToken.put(entry.getKey(), value2); + } else { + throw new IllegalStateException("Error merging ID token and userinfo claim value for claim [" + entry.getKey() + "]. " + + "Cannot merge [" + value1.getClass().getName() + "] with [" + value2.getClass().getName() + "]"); + } } } for (Map.Entry entry : userInfo.entrySet()) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticatorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticatorTests.java index d3186a7242a..fa13700264d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticatorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectAuthenticatorTests.java @@ -829,6 +829,51 @@ public class OpenIdConnectAuthenticatorTests extends OpenIdConnectTestCase { assertTrue(combinedAddress.containsKey("country")); } + public void testJsonObjectMergingWithBooleanLeniency() { + final JSONObject idTokenObject = new JWTClaimsSet.Builder() + .claim("email_verified", true) + .claim("email_verified_1", "true") + .claim("email_verified_2", false) + .claim("email_verified_3", "false") + .build() + .toJSONObject(); + final JSONObject userInfoObject = new JWTClaimsSet.Builder() + .claim("email_verified", "true") + .claim("email_verified_1", true) + .claim("email_verified_2", "false") + .claim("email_verified_3", false) + .build() + .toJSONObject(); + OpenIdConnectAuthenticator.mergeObjects(idTokenObject, userInfoObject); + assertSame(Boolean.TRUE, idTokenObject.get("email_verified")); + assertSame(Boolean.TRUE, idTokenObject.get("email_verified_1")); + assertSame(Boolean.FALSE, idTokenObject.get("email_verified_2")); + assertSame(Boolean.FALSE, idTokenObject.get("email_verified_3")); + + final JSONObject idTokenObject1 = new JWTClaimsSet.Builder() + .claim("email_verified", true) + .build() + .toJSONObject(); + final JSONObject userInfoObject1 = new JWTClaimsSet.Builder() + .claim("email_verified", "false") + .build() + .toJSONObject(); + IllegalStateException e = + expectThrows(IllegalStateException.class, () -> OpenIdConnectAuthenticator.mergeObjects(idTokenObject1, userInfoObject1)); + assertThat(e.getMessage(), containsString("Cannot merge [java.lang.Boolean] with [java.lang.String]")); + + final JSONObject idTokenObject2 = new JWTClaimsSet.Builder() + .claim("email_verified", true) + .build() + .toJSONObject(); + final JSONObject userInfoObject2 = new JWTClaimsSet.Builder() + .claim("email_verified", "yes") + .build() + .toJSONObject(); + e = expectThrows(IllegalStateException.class, () -> OpenIdConnectAuthenticator.mergeObjects(idTokenObject2, userInfoObject2)); + assertThat(e.getMessage(), containsString("Cannot merge [java.lang.Boolean] with [java.lang.String]")); + } + private OpenIdConnectProviderConfiguration getOpConfig() throws URISyntaxException { return new OpenIdConnectProviderConfiguration( new Issuer("https://op.example.com"),