diff --git a/security/jwt/src/main/java/io/helidon/security/jwt/Jwt.java b/security/jwt/src/main/java/io/helidon/security/jwt/Jwt.java index 881679f3a54..da8db8d77b5 100644 --- a/security/jwt/src/main/java/io/helidon/security/jwt/Jwt.java +++ b/security/jwt/src/main/java/io/helidon/security/jwt/Jwt.java @@ -951,6 +951,22 @@ public Errors validate(String issuer, String audience) { return validate(issuer, audience == null ? Set.of() : Set.of(audience)); } + /** + * Validates all default values. + * Values validated: {@link #validate(String, Set, boolean)} + * + * @param issuer validates that this JWT was issued by this issuer. Setting this to non-null value will make + * issuer claim mandatory + * @param audience validates that this JWT was issued for this audience. Setting this to non-null value will make + * audience claim mandatory + * @param checkAudience whether audience claim check is turned on. Validation will fail when {@code true} + * and audience claim is null + * @return errors instance to check for validation result + */ + public Errors validate(String issuer, String audience, boolean checkAudience) { + return validate(issuer, audience == null ? Set.of() : Set.of(audience), checkAudience); + } + /** * Validates all default values. * Values validated: @@ -966,23 +982,43 @@ public Errors validate(String issuer, String audience) { * issuer claim mandatory * @param audience validates that this JWT was issued for this audience. Setting this to non-null value and with * any non-null value in the Set will make audience claim mandatory + * @param checkAudience whether audience claim check is configured as mandatory. Validation will fail when {@code true} + * and audience claim is null * @return errors instance to check for validation result */ - public Errors validate(String issuer, Set audience) { + public Errors validate(String issuer, Set audience, boolean checkAudience) { List> validators = defaultTimeValidators(); if (null != issuer) { addIssuerValidator(validators, issuer, true); } - if (null != audience) { - audience.stream() - .filter(Objects::nonNull) - .findAny() - .ifPresent(it -> addAudienceValidator(validators, audience, true)); + // Audience check is turned on + if (checkAudience) { + if (null != audience) { + audience.stream() + .filter(Objects::nonNull) + .findAny() + .ifPresent(it -> addAudienceValidator(validators, audience, true)); + } } addUserPrincipalValidator(validators); return validate(validators); } + /** + * Validates all default values. + * Audience claim check is not mandatory. + * Values validated: {@link #validate(String, Set, boolean)} + * + * @param issuer validates that this JWT was issued by this issuer. Setting this to non-null value will make + * issuer claim mandatory + * @param audience validates that this JWT was issued for this audience. Setting this to non-null value and with + * any non-null value in the Set will make audience claim mandatory + * @return errors instance to check for validation result + */ + public Errors validate(String issuer, Set audience) { + return validate(issuer, audience, true); + } + /** * Adds a validator that makes sure the {@link Jwt#userPrincipal()} is present. * diff --git a/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/BaseBuilder.java b/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/BaseBuilder.java index a22ce38cd09..3b5fa044310 100644 --- a/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/BaseBuilder.java +++ b/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/BaseBuilder.java @@ -65,8 +65,10 @@ abstract class BaseBuilder, T> implements Builder{@code false} * Allow audience claim to be optional. * + * + * {@code check-audience} + * {@code true} + * Turn audience claim check on when {@code true} or off when {@code false}. + * * */ public final class OidcConfig extends TenantConfigImpl { diff --git a/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/TenantConfig.java b/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/TenantConfig.java index d9e9dbf9933..4291b1041d9 100644 --- a/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/TenantConfig.java +++ b/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/TenantConfig.java @@ -116,6 +116,13 @@ static Builder tenantBuilder() { */ String audience(); + /** + * Whether to validate audience token. + * + * @return audience + */ + boolean checkAudience(); + /** * Audience URI of custom scopes. * diff --git a/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/TenantConfigImpl.java b/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/TenantConfigImpl.java index 104c4ff6c3d..3a637712071 100644 --- a/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/TenantConfigImpl.java +++ b/security/providers/oidc-common/src/main/java/io/helidon/security/providers/oidc/common/TenantConfigImpl.java @@ -40,6 +40,7 @@ class TenantConfigImpl implements TenantConfig { private final boolean validateJwtWithJwk; private final String issuer; private final String audience; + private final boolean checkAudience; private final String realm; private final OidcConfig.ClientAuthentication tokenEndpointAuthentication; private final Duration clientTimeout; @@ -60,6 +61,7 @@ class TenantConfigImpl implements TenantConfig { this.validateJwtWithJwk = builder.validateJwtWithJwk(); this.issuer = builder.issuer(); this.audience = builder.audience(); + this.checkAudience = builder.checkAudience(); this.identityUri = builder.identityUri(); this.realm = builder.realm(); this.tokenEndpointUri = builder.tokenEndpointUri(); @@ -144,6 +146,11 @@ public String audience() { return audience; } + @Override + public boolean checkAudience() { + return checkAudience; + } + @Override public String scopeAudience() { return scopeAudience; diff --git a/security/providers/oidc-common/src/test/java/io/helidon/security/providers/oidc/common/OidcConfigFromBuilderTest.java b/security/providers/oidc-common/src/test/java/io/helidon/security/providers/oidc/common/OidcConfigFromBuilderTest.java index 72035441c73..0cfd048cba7 100644 --- a/security/providers/oidc-common/src/test/java/io/helidon/security/providers/oidc/common/OidcConfigFromBuilderTest.java +++ b/security/providers/oidc-common/src/test/java/io/helidon/security/providers/oidc/common/OidcConfigFromBuilderTest.java @@ -16,6 +16,11 @@ package io.helidon.security.providers.oidc.common; +import java.net.URI; +import java.time.Duration; +import java.util.Arrays; +import java.util.Map; + import io.helidon.common.http.Http; import io.helidon.config.Config; import io.helidon.config.ConfigSources; @@ -44,11 +49,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import java.net.URI; -import java.time.Duration; -import java.util.Arrays; -import java.util.Map; - /** * Unit test for {@link OidcConfig}. */ @@ -205,6 +205,17 @@ void testOptionalAudience() { assertThat(audience, nullValue()); } + @Test + void testCheckAudience() { + OidcConfig config = OidcConfig.builder() + .identityUri(URI.create("http://localhost/identity")) + .clientSecret("top-secret") + .clientId("client-id") + .checkAudience(false) + .build(); + assertThat(config.checkAudience(), is(false)); + } + // Stub the Builder class to be able to retrieve the cookie-encryption-password value private static class TestOidcConfigBuilder extends OidcConfig.Builder { diff --git a/security/providers/oidc-common/src/test/java/io/helidon/security/providers/oidc/common/OidcConfigFromConfigTest.java b/security/providers/oidc-common/src/test/java/io/helidon/security/providers/oidc/common/OidcConfigFromConfigTest.java index ba4a4c58fa7..0340ea0c8e1 100644 --- a/security/providers/oidc-common/src/test/java/io/helidon/security/providers/oidc/common/OidcConfigFromConfigTest.java +++ b/security/providers/oidc-common/src/test/java/io/helidon/security/providers/oidc/common/OidcConfigFromConfigTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -51,4 +52,10 @@ void testOptionalAudience() { assertThat(audience, nullValue()); } + @Test + void testDisabledAudience() { + OidcConfig oidcConfig = OidcConfig.create(config.get("security.oidc-disabled-aud")); + assertThat(oidcConfig.checkAudience(), is(false)); + } + } diff --git a/security/providers/oidc-common/src/test/resources/application.yaml b/security/providers/oidc-common/src/test/resources/application.yaml index 19941db5684..561bc9c5b92 100644 --- a/security/providers/oidc-common/src/test/resources/application.yaml +++ b/security/providers/oidc-common/src/test/resources/application.yaml @@ -36,3 +36,9 @@ security: client-id: "my-id" client-secret: "my-well-known-secret" optional-audience: true + + oidc-disabled-aud: + identity-uri: "https://my.identity" + client-id: "my-id" + client-secret: "my-well-known-secret" + check-audience: false \ No newline at end of file diff --git a/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java b/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java index d5afe1d4594..f9a6124043d 100644 --- a/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java +++ b/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java @@ -440,7 +440,9 @@ private AuthenticationResponse processValidationResult(ProviderRequest providerR Errors.Collector collector) { Jwt jwt = signedJwt.getJwt(); Errors errors = collector.collect(); - Errors validationErrors = jwt.validate(tenant.issuer(), tenantConfig.audience()); + Errors validationErrors = jwt.validate(tenant.issuer(), + tenantConfig.audience(), + tenantConfig.checkAudience()); if (errors.isValid() && validationErrors.isValid()) {