KEYCLOAK-18499 Add max_age support to oauth2 brokered logins
Revise KcOidcBrokerPassMaxAgeTest to use setTimeOffset(...)
This commit is contained in:
parent
a3f339a1c4
commit
43623ea9d0
7 changed files with 125 additions and 3 deletions
|
@ -30,6 +30,7 @@ public class IdentityProviderModel implements Serializable {
|
|||
|
||||
public static final String ALLOWED_CLOCK_SKEW = "allowedClockSkew";
|
||||
public static final String LOGIN_HINT = "loginHint";
|
||||
public static final String PASS_MAX_AGE = "passMaxAge";
|
||||
|
||||
public static final String SYNC_MODE = "syncMode";
|
||||
|
||||
|
@ -230,6 +231,14 @@ public class IdentityProviderModel implements Serializable {
|
|||
getConfig().put(LOGIN_HINT, String.valueOf(loginHint));
|
||||
}
|
||||
|
||||
public boolean isPassMaxAge() {
|
||||
return Boolean.valueOf(getConfig().get(PASS_MAX_AGE));
|
||||
}
|
||||
|
||||
public void setPassMaxAge(boolean passMaxAge) {
|
||||
getConfig().put(PASS_MAX_AGE, String.valueOf(passMaxAge));
|
||||
}
|
||||
|
||||
|
||||
public boolean isHideOnLogin() {
|
||||
return Boolean.valueOf(getConfig().get(HIDE_ON_LOGIN));
|
||||
|
|
|
@ -331,6 +331,11 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||
uriBuilder.queryParam(OIDCLoginProtocol.LOGIN_HINT_PARAM, loginHint);
|
||||
}
|
||||
|
||||
String maxAge = request.getAuthenticationSession().getClientNote(OIDCLoginProtocol.MAX_AGE_PARAM);
|
||||
if (getConfig().isPassMaxAge() && maxAge != null) {
|
||||
uriBuilder.queryParam(OIDCLoginProtocol.MAX_AGE_PARAM, maxAge);
|
||||
}
|
||||
|
||||
if (getConfig().isUiLocales()) {
|
||||
uriBuilder.queryParam(OIDCLoginProtocol.UI_LOCALES_PARAM, session.getContext().resolveLocale(null).toLanguageTag());
|
||||
}
|
||||
|
|
|
@ -121,16 +121,17 @@ public class AbstractOAuth2IdentityProviderTest {
|
|||
}
|
||||
|
||||
private TestProvider getTested() {
|
||||
return new TestProvider(getConfig(null, null, null, Boolean.FALSE));
|
||||
return new TestProvider(getConfig(null, null, null, Boolean.FALSE, Boolean.FALSE));
|
||||
}
|
||||
|
||||
private OAuth2IdentityProviderConfig getConfig(final String autorizationUrl, final String defaultScope, final String clientId, final Boolean isLoginHint) {
|
||||
private OAuth2IdentityProviderConfig getConfig(final String autorizationUrl, final String defaultScope, final String clientId, final Boolean isLoginHint, final Boolean passMaxAge) {
|
||||
IdentityProviderModel model = new IdentityProviderModel();
|
||||
OAuth2IdentityProviderConfig config = new OAuth2IdentityProviderConfig(model);
|
||||
config.setAuthorizationUrl(autorizationUrl);
|
||||
config.setDefaultScope(defaultScope);
|
||||
config.setClientId(clientId);
|
||||
config.setLoginHint(isLoginHint);
|
||||
config.setPassMaxAge(passMaxAge);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
|
|
@ -292,7 +292,7 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
|
|||
/**
|
||||
* Get the login page for an existing client in provided realm
|
||||
*
|
||||
* @param contextRoot
|
||||
* @param contextRoot server base url without /auth
|
||||
* @param realmName Name of the realm
|
||||
* @param clientId ClientId of a client. Client has to exists in the realm.
|
||||
* @return Login URL
|
||||
|
@ -303,6 +303,9 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
|
|||
assertThat(clients, Matchers.is(Matchers.not(Matchers.empty())));
|
||||
|
||||
String redirectURI = clients.get(0).getBaseUrl();
|
||||
if (redirectURI.startsWith("/")) {
|
||||
redirectURI = contextRoot + "/auth" + redirectURI;
|
||||
}
|
||||
|
||||
return contextRoot + "/auth/realms/" + realmName + "/protocol/openid-connect/auth?client_id=" +
|
||||
clientId + "&redirect_uri=" + redirectURI + "&response_type=code&scope=openid";
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.getConsumerRoot;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||
|
||||
/**
|
||||
* Tests the propagation of the max_age parameter for brokered logins.
|
||||
*
|
||||
* see https://issues.redhat.com/browse/KEYCLOAK-18499
|
||||
*/
|
||||
public class KcOidcBrokerPassMaxAgeTest extends AbstractBrokerTest {
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return new KcOidcBrokerConfigurationWithPassMaxAge();
|
||||
}
|
||||
|
||||
private static class KcOidcBrokerConfigurationWithPassMaxAge extends KcOidcBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public IdentityProviderRepresentation setUpIdentityProvider(IdentityProviderSyncMode syncMode) {
|
||||
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
|
||||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
applyDefaultConfiguration(config, syncMode);
|
||||
config.put(IdentityProviderModel.LOGIN_HINT, "false");
|
||||
|
||||
config.put(IdentityProviderModel.PASS_MAX_AGE, "true");
|
||||
|
||||
return idp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
@Ignore
|
||||
public void testLogInAsUserInIDP() {
|
||||
// super.testLogInAsUserInIDP();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void loginWithExistingUser() {
|
||||
|
||||
// login as brokered user user, perform profile update on first broker login and logout user
|
||||
loginUser();
|
||||
testSingleLogout();
|
||||
|
||||
driver.navigate().to(getAccountUrl(getConsumerRoot(), bc.consumerRealmName()));
|
||||
loginPage.clickSocial(bc.getIDPAlias());
|
||||
waitForPage(driver, "sign in to", true);
|
||||
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||
|
||||
loginPage.login(bc.getUserLogin(), bc.getUserPassword());
|
||||
accountUpdateProfilePage.assertCurrent();
|
||||
|
||||
setTimeOffset(2);
|
||||
|
||||
// trigger re-auth with max_age while we are still authenticated
|
||||
String loginUrlWithMaxAge = getLoginUrl(getConsumerRoot(), bc.consumerRealmName(), "account") + "&max_age=1";
|
||||
driver.navigate().to(loginUrlWithMaxAge);
|
||||
|
||||
// we should now see the login page of the consumer
|
||||
waitForPage(driver, "sign in to", true);
|
||||
loginPage.assertCurrent(bc.consumerRealmName());
|
||||
Assert.assertTrue("Driver should be on the consumer realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/protocol/openid-connect/auth"));
|
||||
|
||||
loginPage.clickSocial(bc.getIDPAlias());
|
||||
// we should see the login page of the provider, since the max_age was propagated
|
||||
waitForPage(driver, "sign in to", true);
|
||||
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||
loginPage.assertCurrent(bc.providerRealmName());
|
||||
|
||||
// reauthenticate with password
|
||||
loginPage.login(bc.getUserPassword());
|
||||
waitForPage(driver, "account management", true);
|
||||
|
||||
testSingleLogout();
|
||||
}
|
||||
}
|
|
@ -718,6 +718,8 @@ token-url=Token URL
|
|||
token-url.tooltip=The Token URL.
|
||||
loginHint=Pass login_hint
|
||||
loginHint.tooltip=Pass login_hint to identity provider.
|
||||
passMaxAge=Pass max_age
|
||||
passMaxAge.tooltip=Pass max_age to identity provider.
|
||||
uiLocales=Pass current locale
|
||||
uiLocales.tooltip=Pass the current locale to the identity provider as a ui_locales parameter.
|
||||
logout-url=Logout URL
|
||||
|
|
|
@ -139,6 +139,13 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'loginHint.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="passMaxAge">{{:: 'passMaxAge' | translate}}</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="identityProvider.config.passMaxAge" id="passMaxAge" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'passMaxAge.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="uiLocales">{{:: 'uiLocales' | translate}}</label>
|
||||
<div class="col-sm-4">
|
||||
|
|
Loading…
Reference in a new issue