Make sure that signature validation possible to configure for OIDC id… (#44516)

closes #44473


Signed-off-by: mposolda <mposolda@gmail.com>
Signed-off-by: Marek Posolda <mposolda@gmail.com>
Co-authored-by: Ricardo Martin <rmartinc@redhat.com>
This commit is contained in:
Marek Posolda 2025-11-28 08:51:20 +01:00 committed by GitHub
parent 427d0f181f
commit 38768819e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 142 additions and 53 deletions

View File

@ -530,7 +530,7 @@ secretExpiresOn=Secret expires on {{time}}
searchClientByName=Search client by name searchClientByName=Search client by name
loginTimeout=Login timeout loginTimeout=Login timeout
attributeName=Attribute [Name] attributeName=Attribute [Name]
updateError=Could not update the provider {{error}} updateError=Could not update the provider. {{error}}
importUsersHelp=If true, LDAP users will be imported into the local database and synced by the configured sync policies. If import is enabled, the username and email attributes will be stored in the local database using case-insensitivity values, in lower-case. If disabled, those attributes will be treated as case-sensitive and the values will have the same format from their corresponding LDAP entries. importUsersHelp=If true, LDAP users will be imported into the local database and synced by the configured sync policies. If import is enabled, the username and email attributes will be stored in the local database using case-insensitivity values, in lower-case. If disabled, those attributes will be treated as case-sensitive and the values will have the same format from their corresponding LDAP entries.
emptyClientProfilesInstructions=There are no profiles, select 'Create client profile' to create a new client profile emptyClientProfilesInstructions=There are no profiles, select 'Create client profile' to create a new client profile
policyProvider.js=Define conditions for your permissions using JavaScript. It is one of the rule-based policy types supported by Keycloak, and provides flexibility to write any policy based on the Evaluation API. policyProvider.js=Define conditions for your permissions using JavaScript. It is one of the rule-based policy types supported by Keycloak, and provides flexibility to write any policy based on the Evaluation API.
@ -2027,7 +2027,7 @@ scopePermissions.clients.view-description=Policies that decide if an administrat
allowEcpFlow=Allow ECP flow allowEcpFlow=Allow ECP flow
rsa=rsa rsa=rsa
setPasswordConfirmText=Are you sure you want to set the password for the user {{username}}? setPasswordConfirmText=Are you sure you want to set the password for the user {{username}}?
updateErrorIdentityProvider=Could not update the provider {{error}} updateErrorIdentityProvider=Could not update the provider. {{error}}
emptyProfiles=No client profiles configured emptyProfiles=No client profiles configured
createClientProfileError=Could not create client profile\: '{{error}}' createClientProfileError=Could not create client profile\: '{{error}}'
usermodel.clientRoleMapping.clientId.tooltip=Client ID for role mappings. Just client roles of this client will be added to the token. If this is unset, client roles of all clients will be added to the token. usermodel.clientRoleMapping.clientId.tooltip=Client ID for role mappings. Just client roles of this client will be added to the token. If this is unset, client roles of all clients will be added to the token.
@ -2479,7 +2479,7 @@ targetContextAttributes=Target Context Attributes
targetContextAttributesHelp=Defines the evaluation of context attributes (claims) instead of identity attributes targetContextAttributesHelp=Defines the evaluation of context attributes (claims) instead of identity attributes
filteredByClaim=Verify essential claim filteredByClaim=Verify essential claim
rowCancelBtnAriaLabel=Cancel edits for {{messageBundle}} rowCancelBtnAriaLabel=Cancel edits for {{messageBundle}}
validateSignatureHelp=Enable/disable signature validation of external IDP signatures. For Federated Client Authentication and JWT Authorization Grant the signature validation must be enabled. validateSignatureHelp=Enable/disable signature validation of external IDP tokens. When enabled, Keycloak will validate JWT tokens retrieved from the Identity provider during scenarios related to user authentication, for example when Keycloak obtains IDToken or accessToken after completion of the OIDC/OAuth2 flow with Identity provider. For Federated Client Authentication and JWT Authorization Grant, the signature validation is always used regardless of the value of this switch.
searchForFlow=Search for flow searchForFlow=Search for flow
verifyEmail=Verify email verifyEmail=Verify email
addressClaim.locality.label=User Attribute Name for Locality addressClaim.locality.label=User Attribute Name for Locality

View File

@ -35,6 +35,14 @@ const Fields = ({ readOnly, isOIDC }: DiscoverySettingsProps) => {
control, control,
name: "config.pkceEnabled", name: "config.pkceEnabled",
}); });
const jwtAuthorizationGrantEnabled = useWatch({
control,
name: "config.jwtAuthorizationGrantEnabled",
});
const supportsClientAssertions = useWatch({
control,
name: "config.supportsClientAssertions",
});
return ( return (
<div className="pf-v5-c-form pf-m-horizontal"> <div className="pf-v5-c-form pf-m-horizontal">
@ -93,7 +101,9 @@ const Fields = ({ readOnly, isOIDC }: DiscoverySettingsProps) => {
isDisabled={readOnly} isDisabled={readOnly}
stringify stringify
/> />
{validateSignature === "true" && ( {(validateSignature === "true" ||
jwtAuthorizationGrantEnabled === "true" ||
supportsClientAssertions == "true") && (
<> <>
<DefaultSwitchControl <DefaultSwitchControl
name="config.useJwksUrl" name="config.useJwksUrl"
@ -103,9 +113,11 @@ const Fields = ({ readOnly, isOIDC }: DiscoverySettingsProps) => {
stringify stringify
/> />
{useJwks === "true" ? ( {useJwks === "true" ? (
<TextAreaControl <TextControl
name="config.jwksUrl" name="config.jwksUrl"
label={t("jwksUrl")} label={t("jwksUrl")}
labelIcon={t("jwksUrlHelp")}
type="url"
readOnly={readOnly} readOnly={readOnly}
/> />
) : ( ) : (
@ -113,10 +125,12 @@ const Fields = ({ readOnly, isOIDC }: DiscoverySettingsProps) => {
<TextAreaControl <TextAreaControl
name="config.publicKeySignatureVerifier" name="config.publicKeySignatureVerifier"
label={t("validatingPublicKey")} label={t("validatingPublicKey")}
labelIcon={t("validatingPublicKeyHelp")}
/> />
<TextControl <TextControl
name="config.publicKeySignatureVerifierKeyId" name="config.publicKeySignatureVerifierKeyId"
label={t("validatingPublicKeyId")} label={t("validatingPublicKeyId")}
labelIcon={t("validatingPublicKeyIdHelp")}
readOnly={readOnly} readOnly={readOnly}
/> />
</> </>

View File

@ -87,7 +87,7 @@ export async function assertInvalidUrlNotification(
urlType: UrlType, urlType: UrlType,
) { ) {
await expect(page.getByTestId("last-alert")).toHaveText( await expect(page.getByTestId("last-alert")).toHaveText(
`Could not update the provider The url [${urlType}${urlType.startsWith("single") ? "U" : "_u"}rl] is malformed`, `Could not update the provider. The url [${urlType}${urlType.startsWith("single") ? "U" : "_u"}rl] is malformed`,
); );
} }

View File

@ -67,6 +67,15 @@ test.describe.serial("OIDC identity provider test", () => {
await assertPkceMethodExists(page); await assertPkceMethodExists(page);
await clickSaveButton(page); await clickSaveButton(page);
await assertNotificationMessage(
page,
"Could not update the provider. The 'Validating public key' is required when 'Validate signatures' enabled and 'Use JWKS URL' disabled",
);
await switchOn(page, "#config\\.useJwksUrl");
await assertJwksUrlExists(page, true);
await clickSaveButton(page);
await assertNotificationMessage(page, "Provider successfully updated"); await assertNotificationMessage(page, "Provider successfully updated");
}); });
}); });

View File

@ -60,7 +60,7 @@ async function startServer() {
path.join(SERVER_DIR, `bin/kc${SCRIPT_EXTENSION}`), path.join(SERVER_DIR, `bin/kc${SCRIPT_EXTENSION}`),
[ [
"start-dev", "start-dev",
`--features="login:v2,account:v3,admin-fine-grained-authz:v2,transient-users,oid4vc-vci,organization,declarative-ui,quick-theme,spiffe,kubernetes-service-accounts,workflows"`, `--features="transient-users,oid4vc-vci,declarative-ui,quick-theme,spiffe,kubernetes-service-accounts,workflows,client-auth-federated,jwt-authorization-grant"`,
...keycloakArgs, ...keycloakArgs,
], ],
{ {

View File

@ -8,23 +8,27 @@ import static org.keycloak.protocol.oidc.OIDCLoginProtocol.ISSUER;
public interface JWTAuthorizationGrantConfig { public interface JWTAuthorizationGrantConfig {
public static final String JWT_AUTHORIZATION_GRANT_ENABLED = "jwtAuthorizationGrantEnabled"; String JWT_AUTHORIZATION_GRANT_ENABLED = "jwtAuthorizationGrantEnabled";
public static final String JWT_AUTHORIZATION_GRANT_ASSERTION_REUSE_ALLOWED = "jwtAuthorizationGrantAssertionReuseAllowed"; String JWT_AUTHORIZATION_GRANT_ASSERTION_REUSE_ALLOWED = "jwtAuthorizationGrantAssertionReuseAllowed";
public static final String JWT_AUTHORIZATION_GRANT_MAX_ALLOWED_ASSERTION_EXPIRATION = "jwtAuthorizationGrantMaxAllowedAssertionExpiration"; String JWT_AUTHORIZATION_GRANT_MAX_ALLOWED_ASSERTION_EXPIRATION = "jwtAuthorizationGrantMaxAllowedAssertionExpiration";
public static final String JWT_AUTHORIZATION_GRANT_ASSERTION_SIGNATURE_ALG = "jwtAuthorizationGrantAssertionSignatureAlg"; String JWT_AUTHORIZATION_GRANT_ASSERTION_SIGNATURE_ALG = "jwtAuthorizationGrantAssertionSignatureAlg";
public static final String JWT_AUTHORIZATION_GRANT_ALLOWED_CLOCK_SKEW = "jwtAuthorizationGrantAllowedClockSkew"; String JWT_AUTHORIZATION_GRANT_ALLOWED_CLOCK_SKEW = "jwtAuthorizationGrantAllowedClockSkew";
Map<String, String> getConfig(); Map<String, String> getConfig();
default boolean getJWTAuthorizationGrantEnabled() { default boolean isJWTAuthorizationGrantEnabled() {
return Boolean.parseBoolean(getConfig().getOrDefault(JWT_AUTHORIZATION_GRANT_ENABLED, "false")); return Boolean.parseBoolean(getConfig().getOrDefault(JWT_AUTHORIZATION_GRANT_ENABLED, "false"));
} }
default boolean getJWTAuthorizationGrantAssertionReuseAllowed() { default void setJWTAuthorizationGrantEnabled(boolean jwtAuthorizationGrantEnableds) {
getConfig().put(JWT_AUTHORIZATION_GRANT_ENABLED, String.valueOf(jwtAuthorizationGrantEnableds));
}
default boolean isJWTAuthorizationGrantAssertionReuseAllowed() {
return Boolean.parseBoolean(getConfig().getOrDefault(JWT_AUTHORIZATION_GRANT_ASSERTION_REUSE_ALLOWED, "false")); return Boolean.parseBoolean(getConfig().getOrDefault(JWT_AUTHORIZATION_GRANT_ASSERTION_REUSE_ALLOWED, "false"));
} }

View File

@ -51,7 +51,7 @@ public class JWTAuthorizationGrantIdentityProvider implements JWTAuthorizationGr
@Override @Override
public boolean isAssertionReuseAllowed() { public boolean isAssertionReuseAllowed() {
return config.getJWTAuthorizationGrantAssertionReuseAllowed(); return config.isJWTAuthorizationGrantAssertionReuseAllowed();
} }
@Override @Override

View File

@ -630,7 +630,15 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
protected boolean verify(JWSInput jws) { protected boolean verify(JWSInput jws) {
if (!getConfig().isValidateSignature()) return true; if (!getConfig().isValidateSignature()) return true;
return verifySignature(jws);
}
/**
* Verify signature on given JWS
*
* @return true if signature was successfully verified with the keys available to identity provider
*/
protected boolean verifySignature(JWSInput jws) {
try { try {
KeyWrapper key = getIdentityProviderKeyWrapper(jws); KeyWrapper key = getIdentityProviderKeyWrapper(jws);
if (key == null) { if (key == null) {
@ -1054,7 +1062,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
public boolean verifyClientAssertion(ClientAuthenticationFlowContext context) throws Exception { public boolean verifyClientAssertion(ClientAuthenticationFlowContext context) throws Exception {
OIDCIdentityProviderConfig config = getConfig(); OIDCIdentityProviderConfig config = getConfig();
FederatedJWTClientValidator validator = new FederatedJWTClientValidator(context, v -> verify(v.getJws()), FederatedJWTClientValidator validator = new FederatedJWTClientValidator(context, v -> verifySignature(v.getJws()),
config.getIssuer(), config.getAllowedClockSkew(), config.isSupportsClientAssertionReuse()); config.getIssuer(), config.getAllowedClockSkew(), config.isSupportsClientAssertionReuse());
if (!Profile.isFeatureEnabled(Profile.Feature.CLIENT_AUTH_FEDERATED)) { if (!Profile.isFeatureEnabled(Profile.Feature.CLIENT_AUTH_FEDERATED)) {
@ -1065,24 +1073,16 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
throw new RuntimeException("Issuer does not support client assertions"); throw new RuntimeException("Issuer does not support client assertions");
} }
if (!config.isValidateSignature()) {
throw new RuntimeException("Signature validation not enabled for issuer");
}
return validator.validate(); return validator.validate();
} }
public BrokeredIdentityContext validateAuthorizationGrantAssertion(JWTAuthorizationGrantValidationContext context) throws IdentityBrokerException { public BrokeredIdentityContext validateAuthorizationGrantAssertion(JWTAuthorizationGrantValidationContext context) throws IdentityBrokerException {
if (!getConfig().getJWTAuthorizationGrantEnabled()) { if (!getConfig().isJWTAuthorizationGrantEnabled()) {
throw new IdentityBrokerException("JWT Authorization Granted is not enabled for the identity provider"); throw new IdentityBrokerException("JWT Authorization Granted is not enabled for the identity provider");
} }
if (!getConfig().isValidateSignature()) {
throw new IdentityBrokerException("Signature validation not enabled for issuer");
}
// verify signature // verify signature
if (!verify(context.getJws())) { if (!verifySignature(context.getJws())) {
throw new IdentityBrokerException("Invalid signature"); throw new IdentityBrokerException("Invalid signature");
} }
@ -1099,7 +1099,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
@Override @Override
public boolean isAssertionReuseAllowed() { public boolean isAssertionReuseAllowed() {
return getConfig().getJWTAuthorizationGrantAssertionReuseAllowed(); return getConfig().isJWTAuthorizationGrantAssertionReuseAllowed();
} }
@Override @Override

View File

@ -186,6 +186,10 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig imp
return Boolean.parseBoolean(getConfig().get(SUPPORTS_CLIENT_ASSERTIONS)); return Boolean.parseBoolean(getConfig().get(SUPPORTS_CLIENT_ASSERTIONS));
} }
public void setSupportsClientAssertions(boolean supportsClientAssertions) {
getConfig().put(SUPPORTS_CLIENT_ASSERTIONS, String.valueOf(supportsClientAssertions));
}
public boolean isSupportsClientAssertionReuse() { public boolean isSupportsClientAssertionReuse() {
return Boolean.parseBoolean(getConfig().get(SUPPORTS_CLIENT_ASSERTION_REUSE)); return Boolean.parseBoolean(getConfig().get(SUPPORTS_CLIENT_ASSERTION_REUSE));
} }
@ -196,5 +200,18 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig imp
SslRequired sslRequired = realm.getSslRequired(); SslRequired sslRequired = realm.getSslRequired();
checkUrl(sslRequired, getJwksUrl(), "jwks_url"); checkUrl(sslRequired, getJwksUrl(), "jwks_url");
checkUrl(sslRequired, getLogoutUrl(), "logout_url"); checkUrl(sslRequired, getLogoutUrl(), "logout_url");
if (isValidateSignature() || isJWTAuthorizationGrantEnabled() || isSupportsClientAssertions()) {
String optionText = isValidateSignature() ? "Validate signatures" :
(isJWTAuthorizationGrantEnabled() ? "JWT Authorization Grant" : "Supports client assertions");
if (isUseJwksUrl()) {
if (getJwksUrl() == null) {
throw new IllegalArgumentException(String.format("JWKS URL is required when '%s' enabled and 'Use JWKS URL' enabled", optionText));
}
} else if (getPublicKeySignatureVerifier() == null) {
throw new IllegalArgumentException(String.format("The 'Validating public key' is required when '%s' enabled and 'Use JWKS URL' disabled", optionText));
}
}
} }
} }

View File

@ -365,11 +365,7 @@ public class IdentityProviderOidcTest extends AbstractIdentityProviderTest {
resource.update(representation); resource.update(representation);
fail("Invalid URL"); fail("Invalid URL");
} catch (Exception e) { } catch (Exception e) {
assertTrue(e instanceof ClientErrorException); assertError(e, "The url [authorization_url] is malformed");
Response response = ClientErrorException.class.cast(e).getResponse();
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
assertEquals("The url [authorization_url] is malformed", error.getErrorMessage());
} }
oidcConfig.setAuthorizationUrl(null); oidcConfig.setAuthorizationUrl(null);
@ -379,11 +375,7 @@ public class IdentityProviderOidcTest extends AbstractIdentityProviderTest {
resource.update(representation); resource.update(representation);
fail("Invalid URL"); fail("Invalid URL");
} catch (Exception e) { } catch (Exception e) {
assertTrue(e instanceof ClientErrorException); assertError(e, "The url [token_url] requires secure connections");
Response response = ClientErrorException.class.cast(e).getResponse();
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
assertEquals("The url [token_url] requires secure connections", error.getErrorMessage());
} }
oidcConfig.setAuthorizationUrl(null); oidcConfig.setAuthorizationUrl(null);
@ -393,11 +385,7 @@ public class IdentityProviderOidcTest extends AbstractIdentityProviderTest {
resource.update(representation); resource.update(representation);
fail("Invalid URL"); fail("Invalid URL");
} catch (Exception e) { } catch (Exception e) {
assertTrue(e instanceof ClientErrorException); assertError(e, "The url [jwks_url] requires secure connections");
Response response = ClientErrorException.class.cast(e).getResponse();
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
assertEquals("The url [jwks_url] requires secure connections", error.getErrorMessage());
} }
oidcConfig.setAuthorizationUrl(null); oidcConfig.setAuthorizationUrl(null);
@ -408,11 +396,7 @@ public class IdentityProviderOidcTest extends AbstractIdentityProviderTest {
resource.update(representation); resource.update(representation);
fail("Invalid URL"); fail("Invalid URL");
} catch (Exception e) { } catch (Exception e) {
assertTrue(e instanceof ClientErrorException); assertError(e, "The url [logout_url] requires secure connections");
Response response = ClientErrorException.class.cast(e).getResponse();
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
assertEquals("The url [logout_url] requires secure connections", error.getErrorMessage());
} }
oidcConfig.setAuthorizationUrl(null); oidcConfig.setAuthorizationUrl(null);
@ -425,11 +409,7 @@ public class IdentityProviderOidcTest extends AbstractIdentityProviderTest {
resource.update(representation); resource.update(representation);
fail("Invalid URL"); fail("Invalid URL");
} catch (Exception e) { } catch (Exception e) {
assertTrue(e instanceof ClientErrorException); assertError(e, "The url [userinfo_url] requires secure connections");
Response response = ClientErrorException.class.cast(e).getResponse();
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
assertEquals("The url [userinfo_url] requires secure connections", error.getErrorMessage());
} }
managedRealm.updateWithCleanup(r -> r.sslRequired(SslRequired.EXTERNAL.name())); managedRealm.updateWithCleanup(r -> r.sslRequired(SslRequired.EXTERNAL.name()));
@ -438,6 +418,67 @@ public class IdentityProviderOidcTest extends AbstractIdentityProviderTest {
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove()); managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
} }
@Test
public void testOIDCKeysRequiredForVariousConfigs() {
String id = create(createRep("keycloak-oidc", "keycloak-oidc"));
IdentityProviderResource resource = this.managedRealm.admin().identityProviders().get("keycloak-oidc");
IdentityProviderRepresentation representation = resource.toRepresentation();
OIDCIdentityProviderConfigRep oidcConfig = new OIDCIdentityProviderConfigRep(representation);
// OIDC Keys required when "validate signature" is ON
oidcConfig.setValidateSignature(true);
try {
resource.update(representation);
fail("Not expected to update identity provider");
} catch (Exception e) {
assertError(e, "The 'Validating public key' is required when 'Validate signatures' enabled and 'Use JWKS URL' disabled");
}
// OIDC Keys (set by JWKS URL) required when "validate signature" is ON
oidcConfig.setUseJwksUrl(true);
try {
resource.update(representation);
fail("JWKS URL is required when 'Validate signatures' enabled and 'Use JWKS URL' enabled");
} catch (Exception e) {
assertError(e, "JWKS URL is required when 'Validate signatures' enabled and 'Use JWKS URL' enabled");
}
// OIDC Keys (set by JWKS URL) required when "authorization grant" is ON
oidcConfig.setValidateSignature(false);
oidcConfig.setJWTAuthorizationGrantEnabled(true);
try {
resource.update(representation);
fail("JWKS URL is required when 'Validate signatures' enabled and 'Use JWKS URL' enabled");
} catch (Exception e) {
assertError(e, "JWKS URL is required when 'JWT Authorization Grant' enabled and 'Use JWKS URL' enabled");
}
// OIDC Keys (set by JWKS URL) required when "federated client authentication" is ON
oidcConfig.setJWTAuthorizationGrantEnabled(false);
oidcConfig.setSupportsClientAssertions(true);
try {
resource.update(representation);
fail("JWKS URL is required when 'Validate signatures' enabled and 'Use JWKS URL' enabled");
} catch (Exception e) {
assertError(e, "JWKS URL is required when 'Supports client assertions' enabled and 'Use JWKS URL' enabled");
}
// Successful update when JWKS URL set
oidcConfig.setJwksUrl("https://foo");
resource.update(representation);
managedRealm.cleanup().add(r -> r.identityProviders().get(id).remove());
}
private void assertError(Exception e, String expectedError) {
assertTrue(e instanceof ClientErrorException);
Response response = ClientErrorException.class.cast(e).getResponse();
assertEquals( Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
ErrorRepresentation error = ((ClientErrorException) e).getResponse().readEntity(ErrorRepresentation.class);
assertEquals(expectedError, error.getErrorMessage());
}
@Test @Test
public void testNoExport() { public void testNoExport() {
String id = create(createRep("keycloak-oidc", "keycloak-oidc")); String id = create(createRep("keycloak-oidc", "keycloak-oidc"));

View File

@ -43,9 +43,13 @@ public class OIDCIdentityProviderJWTAuthorizationGrantTest extends AbstractJWTAu
rep.getConfig().put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, "false"); rep.getConfig().put(OIDCIdentityProviderConfig.VALIDATE_SIGNATURE, "false");
}); });
// Test with JWT signed by invalid key. It tests that signature validation is triggered even if "validate signature" switch on OIDC provider is false
testInvalidSignature();
// Test with correct signature
String jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIssuer(), IDP_ISSUER)); String jwt = getIdentityProvider().encodeToken(createAuthorizationGrantToken("basic-user-id", oAuthClient.getEndpoints().getIssuer(), IDP_ISSUER));
AccessTokenResponse response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send(); AccessTokenResponse response = oAuthClient.jwtAuthorizationGrantRequest(jwt).send();
assertFailure("Signature validation not enabled for issuer", response, events.poll()); assertSuccess("test-app", response);
} }
public static class JWTAuthorizationGrantRealmConfig extends AbstractJWTAuthorizationGrantTest.JWTAuthorizationGrantRealmConfig { public static class JWTAuthorizationGrantRealmConfig extends AbstractJWTAuthorizationGrantTest.JWTAuthorizationGrantRealmConfig {