diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index b5a45b8..42d135f 100755
--- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -55,7 +55,7 @@ import org.keycloak.util.JsonSerialization;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
@@ -378,20 +378,29 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
if (userInfoUrl != null && !userInfoUrl.isEmpty() && (id == null || name == null || preferredUsername == null || email == null)) {
if (accessToken != null) {
- SimpleHttp.Response response = SimpleHttp.doGet(userInfoUrl, session)
- .header("Authorization", "Bearer " + accessToken).asResponse();
- if (response.getStatus() != 200) {
- String msg = "failed to invoke user info url";
- try {
- String tmp = response.asString();
- if (tmp != null) msg = tmp;
+ SimpleHttp.Response response = executeRequest(userInfoUrl, SimpleHttp.doGet(userInfoUrl, session).header("Authorization", "Bearer " + accessToken));
+ String contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
+ JsonNode userInfo;
+
+ if (MediaType.APPLICATION_JSON.equals(contentType)) {
+ userInfo = response.asJson();
+ } else if ("application/jwt".equals(contentType)) {
+ JWSInput jwsInput;
- } catch (IOException e) {
+ try {
+ jwsInput = new JWSInput(response.asString());
+ } catch (JWSInputException cause) {
+ throw new RuntimeException("Failed to parse JWT userinfo response", cause);
+ }
+ if (verify(jwsInput)) {
+ userInfo = JsonSerialization.readValue(jwsInput.getContent(), JsonNode.class);
+ } else {
+ throw new RuntimeException("Failed to verify signature of userinfo response from [" + userInfoUrl + "].");
}
- throw new IdentityBrokerException("Failed to invoke on user info url: " + msg);
+ } else {
+ throw new RuntimeException("Unsupported content-type [" + contentType + "] in response from [" + userInfoUrl + "].");
}
- JsonNode userInfo = response.asJson();
id = getJsonProperty(userInfo, "sub");
name = getJsonProperty(userInfo, "name");
@@ -434,6 +443,21 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
return getConfig().getUserInfoUrl();
}
+ private SimpleHttp.Response executeRequest(String url, SimpleHttp request) throws IOException {
+ SimpleHttp.Response response = request.asResponse();
+ if (response.getStatus() != 200) {
+ String msg = "failed to invoke url [" + url + "]";
+ try {
+ String tmp = response.asString();
+ if (tmp != null) msg = tmp;
+
+ } catch (IOException e) {
+
+ }
+ throw new IdentityBrokerException("Failed to invoke url [" + url + "]: " + msg);
+ }
+ return response;
+ }
private String verifyAccessToken(AccessTokenResponse tokenResponse) {
String accessToken = tokenResponse.getToken();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTest.java
index 0fdd82f..809fa34 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTest.java
@@ -1,9 +1,27 @@
package org.keycloak.testsuite.broker;
+import static org.keycloak.testsuite.broker.BrokerTestConstants.REALM_PROV_NAME;
+import static org.keycloak.testsuite.broker.BrokerTestTools.getAuthRoot;
+import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
+
+import java.util.List;
+
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.admin.client.resource.IdentityProviderResource;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
import org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper;
+import org.keycloak.crypto.Algorithm;
+import org.keycloak.protocol.oidc.OIDCConfigAttributes;
+import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.Assert;
public class KcOidcBrokerTest extends AbstractBrokerTest {
@@ -32,4 +50,67 @@ public class KcOidcBrokerTest extends AbstractBrokerTest {
return Lists.newArrayList(attrMapper1, attrMapper2);
}
+
+ @Test
+ public void loginFetchingUserFromUserEndpoint() {
+ RealmResource realm = realmsResouce().realm(bc.providerRealmName());
+ ClientsResource clients = realm.clients();
+ ClientRepresentation brokerApp = clients.findByClientId("brokerapp").get(0);
+
+ try {
+ IdentityProviderResource identityProviderResource = realmsResouce().realm(bc.consumerRealmName()).identityProviders().get(bc.getIDPAlias());
+ IdentityProviderRepresentation idp = identityProviderResource.toRepresentation();
+
+ idp.getConfig().put(OIDCIdentityProviderConfig.JWKS_URL, getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/openid-connect/certs");
+ identityProviderResource.update(idp);
+
+ brokerApp.getAttributes().put(OIDCConfigAttributes.USER_INFO_RESPONSE_SIGNATURE_ALG, Algorithm.RS256);
+ brokerApp.getAttributes().put("validateSignature", Boolean.TRUE.toString());
+ clients.get(brokerApp.getId()).update(brokerApp);
+
+ driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
+
+ log.debug("Clicking social " + bc.getIDPAlias());
+ accountLoginPage.clickSocial(bc.getIDPAlias());
+
+ waitForPage(driver, "log in to", true);
+
+ Assert.assertTrue("Driver should be on the provider realm page right now",
+ driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
+
+ log.debug("Logging in");
+ accountLoginPage.login(bc.getUserLogin(), bc.getUserPassword());
+
+ waitForPage(driver, "update account information", false);
+
+ updateAccountInformationPage.assertCurrent();
+ Assert.assertTrue("We must be on correct realm right now",
+ driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
+
+ log.debug("Updating info on updateAccount page");
+ updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname");
+
+ UsersResource consumerUsers = adminClient.realm(bc.consumerRealmName()).users();
+
+ int userCount = consumerUsers.count();
+ Assert.assertTrue("There must be at least one user", userCount > 0);
+
+ List<UserRepresentation> users = consumerUsers.search("", 0, userCount);
+
+ boolean isUserFound = false;
+ for (UserRepresentation user : users) {
+ if (user.getUsername().equals(bc.getUserLogin()) && user.getEmail().equals(bc.getUserEmail())) {
+ isUserFound = true;
+ break;
+ }
+ }
+
+ Assert.assertTrue("There must be user " + bc.getUserLogin() + " in realm " + bc.consumerRealmName(),
+ isUserFound);
+ } finally {
+ brokerApp.getAttributes().put(OIDCConfigAttributes.USER_INFO_RESPONSE_SIGNATURE_ALG, null);
+ brokerApp.getAttributes().put("validateSignature", Boolean.FALSE.toString());
+ clients.get(brokerApp.getId()).update(brokerApp);
+ }
+ }
}