KEYCLOAK-7201 OIDC Identity Brokering with Client parameter forward
Forward "custom" (non-standard) query parameters to external IDP
This commit is contained in:
parent
40cc826586
commit
959e7b1b01
5 changed files with 128 additions and 1 deletions
|
@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProvider.OIDCEndpoint;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
|
@ -42,6 +43,7 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
@ -59,6 +61,9 @@ import javax.ws.rs.core.UriBuilder;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -327,6 +332,15 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||
if (acr != null) {
|
||||
uriBuilder.queryParam(OAuth2Constants.ACR_VALUES, acr);
|
||||
}
|
||||
String forwardParameterConfig = getConfig().getForwardParameters() != null ? getConfig().getForwardParameters(): "";
|
||||
List<String> forwardParameters = Arrays.asList(forwardParameterConfig.split("\\s*,\\s*"));
|
||||
for(String forwardParameter: forwardParameters) {
|
||||
String name = AuthorizationEndpoint.LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + forwardParameter.trim();
|
||||
String parameter = request.getAuthenticationSession().getClientNote(name);
|
||||
if(parameter != null && !parameter.isEmpty()) {
|
||||
uriBuilder.queryParam(forwardParameter, parameter);
|
||||
}
|
||||
}
|
||||
return uriBuilder;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,4 +86,12 @@ public class OAuth2IdentityProviderConfig extends IdentityProviderModel {
|
|||
public String getPrompt() {
|
||||
return getConfig().get("prompt");
|
||||
}
|
||||
|
||||
public String getForwardParameters() {
|
||||
return getConfig().get("forwardParameters");
|
||||
}
|
||||
|
||||
public void setForwardParameters(String forwardParameters) {
|
||||
getConfig().put("forwardParameters", forwardParameters);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDER_ID;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||
|
||||
public class KcOidcBrokerParameterForwardTest extends KcOidcBrokerTest {
|
||||
|
||||
private static final String FORWARDED_PARAMETER = "forwarded_parameter";
|
||||
private static final String FORWARDED_PARAMETER_VALUE = "forwarded_value";
|
||||
private static final String PARAMETER_NOT_SET = "parameter_not_set";
|
||||
private static final String PARAMETER_NOT_FORWARDED = "parameter_not_forwarded";
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfigurationWithParameterForward();
|
||||
}
|
||||
|
||||
private class KcOidcBrokerConfigurationWithParameterForward extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(suiteContext, config);
|
||||
config.put("forwardParameters", FORWARDED_PARAMETER +", " + PARAMETER_NOT_SET);
|
||||
return idp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loginUser() {
|
||||
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
|
||||
|
||||
String queryString = "&" + FORWARDED_PARAMETER + "=" + FORWARDED_PARAMETER_VALUE + "&" + PARAMETER_NOT_FORWARDED + "=" + "value";
|
||||
driver.navigate().to(driver.getCurrentUrl() + queryString);
|
||||
|
||||
log.debug("Clicking social " + bc.getIDPAlias());
|
||||
accountLoginPage.clickSocial(bc.getIDPAlias());
|
||||
|
||||
waitForPage(driver, "log in to", true);
|
||||
|
||||
Assert.assertThat("Driver should be on the provider realm page right now",
|
||||
driver.getCurrentUrl(), containsString("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||
|
||||
Assert.assertThat(FORWARDED_PARAMETER + "=" + FORWARDED_PARAMETER_VALUE + " should be part of the url",
|
||||
driver.getCurrentUrl(), containsString(FORWARDED_PARAMETER + "=" + FORWARDED_PARAMETER_VALUE));
|
||||
|
||||
Assert.assertThat("\"" + PARAMETER_NOT_SET + "\"" + " should NOT be part of the url",
|
||||
driver.getCurrentUrl(), not(containsString(PARAMETER_NOT_SET)));
|
||||
|
||||
Assert.assertThat("\"" + PARAMETER_NOT_FORWARDED +"\"" + " should be NOT part of the url",
|
||||
driver.getCurrentUrl(), not(containsString(PARAMETER_NOT_FORWARDED)));
|
||||
|
||||
accountLoginPage.login(bc.getUserLogin(), bc.getUserPassword());
|
||||
waitForPage(driver, "update account information", false);
|
||||
|
||||
updateAccountInformationPage.assertCurrent();
|
||||
|
||||
Assert.assertThat("We must be on correct realm right now",
|
||||
driver.getCurrentUrl(), containsString("/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);
|
||||
}
|
||||
|
||||
}
|
|
@ -572,6 +572,8 @@ validating-public-key-id=Validating Public Key Id
|
|||
identity-provider.validating-public-key-id.tooltip=Explicit ID of the validating public key given above if the key ID. Leave blank if the key above should be used always, regardless of key ID specified by external IDP; set it if the key should only be used for verifying if key ID from external IDP matches.
|
||||
allowed-clock-skew=Allowed clock skew
|
||||
identity-provider.allowed-clock-skew.tooltip=Clock skew in seconds that is tolerated when validating identity provider tokens. Default value is zero.
|
||||
forwarded-query-parameters=Forwarded Query Parameters
|
||||
identity-provider.forwarded-query-parameters.tooltip=Non OpenID Connect/OAuth standard query parameters to be forwarded to external IDP from the initial application request to Authorization Endpoint. Multiple parameters can be entered, separated by comma (,).
|
||||
import-external-idp-config=Import External IDP Config
|
||||
import-external-idp-config.tooltip=Allows you to load external IDP metadata from a config file or to download it from a URL.
|
||||
import-from-url=Import from URL
|
||||
|
|
|
@ -253,7 +253,13 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'identity-provider.allowed-clock-skew.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="forwardParameters">{{:: 'forwarded-query-parameters' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" id="forwardParameters" type="text" ng-model="identityProvider.config.forwardParameters"/>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'identity-provider.forwarded-query-parameters.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset data-ng-show="newIdentityProvider">
|
||||
<legend uncollapsed><span class="text">{{:: 'import-external-idp-config' | translate}}</span> <kc-tooltip>{{:: 'import-external-idp-config.tooltip' | translate}}</kc-tooltip></legend>
|
||||
|
|
Loading…
Reference in a new issue