[KEYCLOAK-8901] - Identity Provider : UserInfo response as JWT Token not supported
This commit is contained in:
parent
b5d2b23d02
commit
e798c3bca2
2 changed files with 117 additions and 12 deletions
|
@ -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";
|
||||
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;
|
||||
|
||||
try {
|
||||
String tmp = response.asString();
|
||||
if (tmp != null) msg = tmp;
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
jwsInput = new JWSInput(response.asString());
|
||||
} catch (JWSInputException cause) {
|
||||
throw new RuntimeException("Failed to parse JWT userinfo response", cause);
|
||||
}
|
||||
throw new IdentityBrokerException("Failed to invoke on user info url: " + msg);
|
||||
|
||||
if (verify(jwsInput)) {
|
||||
userInfo = JsonSerialization.readValue(jwsInput.getContent(), JsonNode.class);
|
||||
} else {
|
||||
throw new RuntimeException("Failed to verify signature of userinfo response from [" + userInfoUrl + "].");
|
||||
}
|
||||
} 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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue