[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.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.QueryParam;
|
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.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
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 (userInfoUrl != null && !userInfoUrl.isEmpty() && (id == null || name == null || preferredUsername == null || email == null)) {
|
||||||
|
|
||||||
if (accessToken != null) {
|
if (accessToken != null) {
|
||||||
SimpleHttp.Response response = SimpleHttp.doGet(userInfoUrl, session)
|
SimpleHttp.Response response = executeRequest(userInfoUrl, SimpleHttp.doGet(userInfoUrl, session).header("Authorization", "Bearer " + accessToken));
|
||||||
.header("Authorization", "Bearer " + accessToken).asResponse();
|
String contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
|
||||||
if (response.getStatus() != 200) {
|
JsonNode userInfo;
|
||||||
String msg = "failed to invoke user info url";
|
|
||||||
|
if (MediaType.APPLICATION_JSON.equals(contentType)) {
|
||||||
|
userInfo = response.asJson();
|
||||||
|
} else if ("application/jwt".equals(contentType)) {
|
||||||
|
JWSInput jwsInput;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String tmp = response.asString();
|
jwsInput = new JWSInput(response.asString());
|
||||||
if (tmp != null) msg = tmp;
|
} catch (JWSInputException cause) {
|
||||||
|
throw new RuntimeException("Failed to parse JWT userinfo response", cause);
|
||||||
} catch (IOException e) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
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");
|
id = getJsonProperty(userInfo, "sub");
|
||||||
name = getJsonProperty(userInfo, "name");
|
name = getJsonProperty(userInfo, "name");
|
||||||
|
@ -434,6 +443,21 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||||
return getConfig().getUserInfoUrl();
|
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) {
|
private String verifyAccessToken(AccessTokenResponse tokenResponse) {
|
||||||
String accessToken = tokenResponse.getToken();
|
String accessToken = tokenResponse.getToken();
|
||||||
|
|
|
@ -1,9 +1,27 @@
|
||||||
package org.keycloak.testsuite.broker;
|
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.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
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.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.IdentityProviderMapperRepresentation;
|
||||||
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.testsuite.Assert;
|
||||||
|
|
||||||
public class KcOidcBrokerTest extends AbstractBrokerTest {
|
public class KcOidcBrokerTest extends AbstractBrokerTest {
|
||||||
|
|
||||||
|
@ -32,4 +50,67 @@ public class KcOidcBrokerTest extends AbstractBrokerTest {
|
||||||
|
|
||||||
return Lists.newArrayList(attrMapper1, attrMapper2);
|
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