mirror of
https://github.com/keycloak/keycloak.git
synced 2025-12-21 06:20:05 -06:00
Add config param disableTypeClaimCheck in order to validate external tokens without typ claim
Closes #33332
Signed-off-by: Venelin Cvetkov <venelin.tsvetkov@gmail.com>
(cherry picked from commit d388dc7936)
This commit is contained in:
committed by
Marek Posolda
parent
a2deff172b
commit
4ae7d60784
@@ -315,6 +315,7 @@ revert=Revert
|
||||
eventTypes.IDENTITY_PROVIDER_RETRIEVE_TOKEN.description=Identity provider retrieve token
|
||||
dependentPermission=Dependent permission
|
||||
disableNonce=Disable nonce
|
||||
disableTypeClaimCheck=Disable type claim check
|
||||
addAssociatedRolesSuccess=Associated roles have been added
|
||||
groupDeleted_one=Group deleted
|
||||
userHelp=Optionally select user, for whom the example access token will be generated. If you do not select a user, example access token will not be generated during evaluation
|
||||
@@ -2797,6 +2798,7 @@ eventTypes.OAUTH2_DEVICE_CODE_TO_TOKEN.name=OAuth2 device code to token
|
||||
searchGroups=Search groups
|
||||
trusted-hosts.tooltip=List of Hosts, which are trusted and are allowed to invoke Client Registration Service and/or be used as values of Client URIs. You can use hostnames or IP addresses. If you use star at the beginning (for example '*.example.com' ) then whole domain example.com will be trusted.
|
||||
disableNonceHelp=Do not send the nonce parameter in the authentication request. The nonce parameter is sent and verified by default.
|
||||
disableTypeClaimCheckHelp=Disables the validation of the `typ` claim of tokens received from the Identity Provider. If this is `off` the type claim is validated (default).
|
||||
deleteClientProfile=Delete this client profile
|
||||
none=None
|
||||
type=Type
|
||||
|
||||
@@ -57,6 +57,10 @@ export const ExtendedNonDiscoverySettings = () => {
|
||||
/>
|
||||
<SwitchField field="config.disableUserInfo" label="disableUserInfo" />
|
||||
<SwitchField field="config.disableNonce" label="disableNonce" />
|
||||
<SwitchField
|
||||
field="config.disableTypeClaimCheck"
|
||||
label="disableTypeClaimCheck"
|
||||
/>
|
||||
<TextField field="config.defaultScope" label="scopes" />
|
||||
<FormGroupField label="prompt">
|
||||
<Controller
|
||||
|
||||
@@ -863,7 +863,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
}
|
||||
|
||||
try {
|
||||
if (!isTokenTypeSupported(parsedToken)) {
|
||||
if (!getConfig().isDisableTypeClaimCheck() && !isTokenTypeSupported(parsedToken)) {
|
||||
throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, "token type not supported", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
boolean idTokenType = OAuth2Constants.ID_TOKEN_TYPE.equals(subjectTokenType);
|
||||
|
||||
@@ -168,6 +168,18 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDisableTypeClaimCheck() {
|
||||
return Boolean.parseBoolean(getConfig().get("disableTypeClaimCheck"));
|
||||
}
|
||||
|
||||
public void setDisableTypeClaimCheck(boolean disableTypeClaimCheck) {
|
||||
if (disableTypeClaimCheck) {
|
||||
getConfig().put("disableTypeClaimCheck", Boolean.TRUE.toString());
|
||||
} else {
|
||||
getConfig().remove("disableTypeClaimCheck");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(RealmModel realm) {
|
||||
super.validate(realm);
|
||||
|
||||
@@ -23,8 +23,6 @@ import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS;
|
||||
import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import jakarta.ws.rs.client.Client;
|
||||
import jakarta.ws.rs.client.Entity;
|
||||
import jakarta.ws.rs.client.WebTarget;
|
||||
@@ -199,6 +197,67 @@ public final class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBase
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoredTokenTypesValidationWhenExplicitlyConfigured() throws Exception {
|
||||
testingClient.server(BrokerTestConstants.REALM_CONS_NAME).run(KcOidcBrokerTokenExchangeTest::setupRealm);
|
||||
RealmResource providerRealm = realmsResouce().realm(bc.providerRealmName());
|
||||
ClientsResource clients = providerRealm.clients();
|
||||
ClientRepresentation brokerApp = clients.findByClientId("brokerapp").get(0);
|
||||
brokerApp.setDirectAccessGrantsEnabled(true);
|
||||
ClientResource brokerAppResource = providerRealm.clients().get(brokerApp.getId());
|
||||
brokerAppResource.update(brokerApp);
|
||||
RealmResource consumerRealm = realmsResouce().realm(bc.consumerRealmName());
|
||||
IdentityProviderResource identityProviderResource = consumerRealm.identityProviders().get(bc.getIDPAlias());
|
||||
IdentityProviderRepresentation idpRep = identityProviderResource.toRepresentation();
|
||||
idpRep.getConfig().put("disableUserInfo", "true");
|
||||
idpRep.getConfig().put("disableTypeClaimCheck", "true");
|
||||
identityProviderResource.update(idpRep);
|
||||
getCleanup().addCleanup(() -> {
|
||||
idpRep.getConfig().put("disableUserInfo", "false");
|
||||
idpRep.getConfig().put("disableTypeClaimCheck", "false");
|
||||
identityProviderResource.update(idpRep);
|
||||
});
|
||||
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.realm(bc.providerRealmName())
|
||||
.clientId(brokerApp.getClientId())
|
||||
.doGrantAccessTokenRequest(brokerApp.getSecret(), bc.getUserLogin(), bc.getUserPassword());
|
||||
assertThat(tokenResponse.getIdToken(), notNullValue());
|
||||
String idTokenString = tokenResponse.getIdToken();
|
||||
oauth.realm(bc.providerRealmName());
|
||||
String logoutUrl = oauth.getLogoutUrl()
|
||||
.idTokenHint(idTokenString)
|
||||
.postLogoutRedirectUri(oauth.APP_AUTH_ROOT).build();
|
||||
driver.navigate().to(logoutUrl);
|
||||
|
||||
String logoutToken = testingClient.testApp().getBackChannelRawLogoutToken();
|
||||
Assert.assertNotNull(logoutToken);
|
||||
|
||||
Client httpClient = AdminClientUtil.createResteasyClient();
|
||||
try {
|
||||
WebTarget exchangeUrl = httpClient.target(OAuthClient.AUTH_SERVER_ROOT)
|
||||
.path("/realms")
|
||||
.path(bc.consumerRealmName())
|
||||
.path("protocol/openid-connect/token");
|
||||
// test user info validation.
|
||||
try (Response response = exchangeUrl.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader(
|
||||
"test-app", "secret"))
|
||||
.post(Entity.form(
|
||||
new Form()
|
||||
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
|
||||
.param(OAuth2Constants.SUBJECT_TOKEN, logoutToken)
|
||||
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE)
|
||||
.param(OAuth2Constants.SUBJECT_ISSUER, bc.getIDPAlias())
|
||||
.param(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID)
|
||||
|
||||
))) {
|
||||
assertThat(response.getStatus(), equalTo(Status.OK.getStatusCode()));
|
||||
}
|
||||
} finally {
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupRealm(KeycloakSession session) {
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
IdentityProviderModel idp = session.identityProviders().getByAlias(IDP_OIDC_ALIAS);
|
||||
|
||||
Reference in New Issue
Block a user