From 49a57ed53a8a3a6d59dcf6ce45013975964dc0be Mon Sep 17 00:00:00 2001 From: Marco Di Sabatino Di Diodoro Date: Mon, 9 Nov 2026 16:44:12 +0100 Subject: [PATCH 1/6] [SYNCOPE-2009] Add new parameters for OIDC services --- .../ClientAppModalPanelBuilder.java | 28 +++++++- .../common/lib/to/OIDCRPClientAppTO.java | 56 ++++++++++++++++ .../api/entity/am/OIDCRPClientApp.java | 18 +++++ .../jpa/entity/am/JPAOIDCRPClientApp.java | 65 +++++++++++++++++++ .../neo4j/entity/am/Neo4jOIDCRPClientApp.java | 64 ++++++++++++++++++ .../java/data/ClientAppDataBinderImpl.java | 11 ++++ .../apache/syncope/fit/AbstractITCase.java | 5 ++ .../syncope/fit/core/ClientAppITCase.java | 15 +++++ .../mapping/OIDCRPClientAppTOMapper.java | 33 ++++++++++ .../wa/starter/WAServiceRegistryTest.java | 42 ++++++++++++ 10 files changed, 336 insertions(+), 1 deletion(-) diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java index f0dc3632eb4..4fbbfa8b682 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java @@ -374,6 +374,16 @@ protected void onUpdate(final AjaxRequestTarget target) { fields.add(new AjaxTripleStateButtonPanel( "field", "jwtAccessToken", new PropertyModel<>(clientAppTO, "jwtAccessToken")). addRequiredLabel()); + + AjaxDropDownChoicePanel jwtAccessTokenSigningAlg = + new AjaxDropDownChoicePanel<>( + "field", "jwtAccessTokenSigningAlg", + new PropertyModel<>(clientAppTO, "jwtAccessTokenSigningAlg"), false); + + jwtAccessTokenSigningAlg.setChoices(Stream.of(OIDCTokenSigningAlg.values()). + filter(alg -> alg != OIDCTokenSigningAlg.none).toList()); + fields.add(jwtAccessTokenSigningAlg); + fields.add(new AjaxTripleStateButtonPanel( "field", "bypassApprovalPrompt", new PropertyModel<>(clientAppTO, "bypassApprovalPrompt")). addRequiredLabel()); @@ -486,6 +496,9 @@ public void setObject(final String object) { new PropertyModel<>(clientAppTO, "accessTokenMaxActiveTokens")); fields.add(accessTokenMaxActiveTokens); + fields.add(new AjaxTripleStateButtonPanel( + "field", "renewRefreshToken", new PropertyModel<>(clientAppTO, "renewRefreshToken"))); + AjaxTextFieldPanel refreshTokenTimeToKill = new AjaxTextFieldPanel( "field", "refreshTokenTimeToKill", new PropertyModel<>(clientAppTO, "refreshTokenTimeToKill"), false); @@ -501,6 +514,19 @@ public void setObject(final String object) { "field", "deviceTokenTimeToKill", new PropertyModel<>(clientAppTO, "deviceTokenTimeToKill"), false); fields.add(deviceTokenTimeToKill); + + fields.add(new AjaxTextFieldPanel( + "field", "idTokenTimeToKill", + new PropertyModel<>(clientAppTO, "idTokenTimeToKill"), false)); + + fields.add(new AjaxTextFieldPanel( + "field", "authorizationCodeTimeToLive", + new PropertyModel<>(clientAppTO, "authorizationCodeTimeToLive"), false)); + + AjaxTextFieldPanel audience = new AjaxTextFieldPanel("panel", "audience", Model.of()); + fields.add(new MultiFieldPanel.Builder( + new PropertyModel<>(clientAppTO, "audience")).build( + "field", "audience", audience)); break; case SAML2SP: @@ -615,7 +641,7 @@ public String getObject() { addRequiredLabel()); fields.add(new AjaxTripleStateButtonPanel( - "field", "skipGeneratingSubjectConfirmationAddress", + "field", "skipGeneratingSubjectConfirmationAddress", new PropertyModel<>(clientAppTO, "skipGeneratingSubjectConfirmationAddress")). addRequiredLabel()); diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/OIDCRPClientAppTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/OIDCRPClientAppTO.java index 44754e26870..f943ef2f219 100644 --- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/OIDCRPClientAppTO.java +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/OIDCRPClientAppTO.java @@ -94,6 +94,16 @@ public class OIDCRPClientAppTO extends ClientAppTO { private String deviceTokenTimeToKill; + private OIDCTokenSigningAlg jwtAccessTokenSigningAlg; + + private Boolean renewRefreshToken; + + private String idTokenTimeToKill; + + private String authorizationCodeTimeToLive; + + private final List audience = new ArrayList<>(); + private OIDCClientAuthenticationMethod tokenEndpointAuthenticationMethod = OIDCClientAuthenticationMethod.client_secret_basic; @@ -332,6 +342,42 @@ public void setDeviceTokenTimeToKill(final String deviceTokenTimeToKill) { this.deviceTokenTimeToKill = deviceTokenTimeToKill; } + public OIDCTokenSigningAlg getJwtAccessTokenSigningAlg() { + return jwtAccessTokenSigningAlg; + } + + public void setJwtAccessTokenSigningAlg(final OIDCTokenSigningAlg jwtAccessTokenSigningAlg) { + this.jwtAccessTokenSigningAlg = jwtAccessTokenSigningAlg; + } + + public Boolean getRenewRefreshToken() { + return renewRefreshToken; + } + + public void setRenewRefreshToken(final Boolean renewRefreshToken) { + this.renewRefreshToken = renewRefreshToken; + } + + public String getIdTokenTimeToKill() { + return idTokenTimeToKill; + } + + public void setIdTokenTimeToKill(final String idTokenTimeToKill) { + this.idTokenTimeToKill = idTokenTimeToKill; + } + + public String getAuthorizationCodeTimeToLive() { + return authorizationCodeTimeToLive; + } + + public void setAuthorizationCodeTimeToLive(final String authorizationCodeTimeToLive) { + this.authorizationCodeTimeToLive = authorizationCodeTimeToLive; + } + + public List getAudience() { + return audience; + } + @Override public boolean equals(final Object obj) { if (obj == null) { @@ -376,6 +422,11 @@ public boolean equals(final Object obj) { .append(this.refreshTokenTimeToKill, rhs.accessTokenTimeToKill) .append(this.refreshTokenMaxActiveTokens, rhs.refreshTokenMaxActiveTokens) .append(this.deviceTokenTimeToKill, rhs.deviceTokenTimeToKill) + .append(this.jwtAccessTokenSigningAlg, rhs.jwtAccessTokenSigningAlg) + .append(this.renewRefreshToken, rhs.renewRefreshToken) + .append(this.idTokenTimeToKill, rhs.idTokenTimeToKill) + .append(this.authorizationCodeTimeToLive, rhs.authorizationCodeTimeToLive) + .append(this.audience, rhs.audience) .isEquals(); } @@ -413,6 +464,11 @@ public int hashCode() { .append(refreshTokenTimeToKill) .append(refreshTokenMaxActiveTokens) .append(deviceTokenTimeToKill) + .append(jwtAccessTokenSigningAlg) + .append(renewRefreshToken) + .append(idTokenTimeToKill) + .append(authorizationCodeTimeToLive) + .append(audience) .toHashCode(); } } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java index 0eb2dbda8d3..d42a12d0019 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java @@ -141,4 +141,22 @@ public interface OIDCRPClientApp extends ClientApp { String getAccessTokenMaxTimeToLive(); void setAccessTokenMaxTimeToLive(String accessTokenMaxTimeToLive); + + OIDCTokenSigningAlg getJwtAccessTokenSigningAlg(); + + void setJwtAccessTokenSigningAlg(OIDCTokenSigningAlg jwtAccessTokenSigningAlg); + + Boolean getRenewRefreshToken(); + + void setRenewRefreshToken(Boolean renewRefreshToken); + + String getIdTokenTimeToKill(); + + void setIdTokenTimeToKill(String idTokenTimeToKill); + + String getAuthorizationCodeTimeToLive(); + + void setAuthorizationCodeTimeToLive(String authorizationCodeTimeToLive); + + Set getAudience(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java index b0a368262b8..2e7ab638254 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java @@ -154,6 +154,21 @@ public class JPAOIDCRPClientApp extends AbstractClientApp implements OIDCRPClien private String deviceTokenTimeToKill; + @Enumerated(EnumType.STRING) + private OIDCTokenSigningAlg jwtAccessTokenSigningAlg; + + private Boolean renewRefreshToken; + + private String idTokenTimeToKill; + + private String authorizationCodeTimeToLive; + + @Lob + private String audience; + + @Transient + private Set audienceSet = new HashSet<>(); + @Override public Set getRedirectUris() { return redirectUrisSet; @@ -436,11 +451,57 @@ public void setAccessTokenMaxTimeToLive(final String accessTokenMaxTimeToLive) { this.accessTokenMaxTimeToLive = accessTokenMaxTimeToLive; } + @Override + public OIDCTokenSigningAlg getJwtAccessTokenSigningAlg() { + return jwtAccessTokenSigningAlg; + } + + @Override + public void setJwtAccessTokenSigningAlg(final OIDCTokenSigningAlg jwtAccessTokenSigningAlg) { + this.jwtAccessTokenSigningAlg = jwtAccessTokenSigningAlg; + } + + @Override + public Boolean getRenewRefreshToken() { + return renewRefreshToken; + } + + @Override + public void setRenewRefreshToken(final Boolean renewRefreshToken) { + this.renewRefreshToken = renewRefreshToken; + } + + @Override + public String getIdTokenTimeToKill() { + return idTokenTimeToKill; + } + + @Override + public void setIdTokenTimeToKill(final String idTokenTimeToKill) { + this.idTokenTimeToKill = idTokenTimeToKill; + } + + @Override + public String getAuthorizationCodeTimeToLive() { + return authorizationCodeTimeToLive; + } + + @Override + public void setAuthorizationCodeTimeToLive(final String authorizationCodeTimeToLive) { + this.authorizationCodeTimeToLive = authorizationCodeTimeToLive; + } + + @Override + public Set getAudience() { + return audienceSet; + } + protected void json2list(final boolean clearFirst) { if (clearFirst) { getRedirectUris().clear(); getSupportedGrantTypes().clear(); getSupportedResponseTypes().clear(); + getAudience().clear(); } if (redirectUris != null) { getRedirectUris().addAll(POJOHelper.deserialize(redirectUris, STRING_TYPEREF)); @@ -454,6 +515,9 @@ protected void json2list(final boolean clearFirst) { if (scopes != null) { getScopes().addAll(POJOHelper.deserialize(scopes, SCOPE_TYPEREF)); } + if (audience != null) { + getAudience().addAll(POJOHelper.deserialize(audience, STRING_TYPEREF)); + } } @PostLoad @@ -474,5 +538,6 @@ public void list2json() { supportedGrantTypes = POJOHelper.serialize(getSupportedGrantTypes()); supportedResponseTypes = POJOHelper.serialize(getSupportedResponseTypes()); scopes = POJOHelper.serialize(getScopes()); + audience = POJOHelper.serialize(getAudience()); } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java index c4766924333..90a7a037719 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java @@ -132,6 +132,20 @@ public class Neo4jOIDCRPClientApp extends AbstractClientApp implements OIDCRPCli private String deviceTokenTimeToKill; + private OIDCTokenSigningAlg jwtAccessTokenSigningAlg; + + private Boolean renewRefreshToken; + + private String idTokenTimeToKill; + + private String authorizationCodeTimeToLive; + + private String audience; + + @Transient + private Set audienceSet = new HashSet<>(); + + @Override public Set getRedirectUris() { return redirectUrisSet; @@ -414,11 +428,57 @@ public void setAccessTokenMaxTimeToLive(final String accessTokenMaxTimeToLive) { this.accessTokenMaxTimeToLive = accessTokenMaxTimeToLive; } + @Override + public OIDCTokenSigningAlg getJwtAccessTokenSigningAlg() { + return jwtAccessTokenSigningAlg; + } + + @Override + public void setJwtAccessTokenSigningAlg(final OIDCTokenSigningAlg jwtAccessTokenSigningAlg) { + this.jwtAccessTokenSigningAlg = jwtAccessTokenSigningAlg; + } + + @Override + public Boolean getRenewRefreshToken() { + return renewRefreshToken; + } + + @Override + public void setRenewRefreshToken(final Boolean renewRefreshToken) { + this.renewRefreshToken = renewRefreshToken; + } + + @Override + public String getIdTokenTimeToKill() { + return idTokenTimeToKill; + } + + @Override + public void setIdTokenTimeToKill(final String idTokenTimeToKill) { + this.idTokenTimeToKill = idTokenTimeToKill; + } + + @Override + public String getAuthorizationCodeTimeToLive() { + return authorizationCodeTimeToLive; + } + + @Override + public void setAuthorizationCodeTimeToLive(final String authorizationCodeTimeToLive) { + this.authorizationCodeTimeToLive = authorizationCodeTimeToLive; + } + + @Override + public Set getAudience() { + return audienceSet; + } + protected void json2list(final boolean clearFirst) { if (clearFirst) { getRedirectUris().clear(); getSupportedGrantTypes().clear(); getSupportedResponseTypes().clear(); + getAudience().clear(); } if (redirectUris != null) { getRedirectUris().addAll(POJOHelper.deserialize(redirectUris, STRING_TYPEREF)); @@ -432,6 +492,9 @@ protected void json2list(final boolean clearFirst) { if (scopes != null) { getScopes().addAll(POJOHelper.deserialize(scopes, SCOPE_TYPEREF)); } + if (audience != null) { + getAudience().addAll(POJOHelper.deserialize(audience, STRING_TYPEREF)); + } } @PostLoad @@ -448,5 +511,6 @@ public void list2json() { supportedGrantTypes = POJOHelper.serialize(getSupportedGrantTypes()); supportedResponseTypes = POJOHelper.serialize(getSupportedResponseTypes()); scopes = POJOHelper.serialize(getScopes()); + audience = POJOHelper.serialize(getAudience()); } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java index 753c6a48044..6cbd1f2103d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java @@ -328,6 +328,12 @@ protected void doUpdate(final OIDCRPClientApp clientApp, final OIDCRPClientAppTO clientApp.setRefreshTokenMaxActiveTokens(clientAppTO.getRefreshTokenMaxActiveTokens()); clientApp.setRefreshTokenTimeToKill(clientAppTO.getRefreshTokenTimeToKill()); clientApp.setDeviceTokenTimeToKill(clientAppTO.getDeviceTokenTimeToKill()); + clientApp.setJwtAccessTokenSigningAlg(clientAppTO.getJwtAccessTokenSigningAlg()); + clientApp.setRenewRefreshToken(clientAppTO.getRenewRefreshToken()); + clientApp.setIdTokenTimeToKill(clientAppTO.getIdTokenTimeToKill()); + clientApp.setAuthorizationCodeTimeToLive(clientAppTO.getAuthorizationCodeTimeToLive()); + clientApp.getAudience().clear(); + clientApp.getAudience().addAll(clientAppTO.getAudience()); Set allowedScopes = new HashSet<>(); Stream.of(OIDCStandardScope.values()).map(OIDCStandardScope::name).forEach(allowedScopes::add); @@ -379,6 +385,11 @@ protected OIDCRPClientAppTO getOIDCClientAppTO(final OIDCRPClientApp clientApp) clientAppTO.setRefreshTokenTimeToKill(clientApp.getRefreshTokenTimeToKill()); clientAppTO.setRefreshTokenMaxActiveTokens(clientApp.getRefreshTokenMaxActiveTokens()); clientAppTO.setDeviceTokenTimeToKill(clientApp.getDeviceTokenTimeToKill()); + clientAppTO.setJwtAccessTokenSigningAlg(clientApp.getJwtAccessTokenSigningAlg()); + clientAppTO.setRenewRefreshToken(clientApp.getRenewRefreshToken()); + clientAppTO.setIdTokenTimeToKill(clientApp.getIdTokenTimeToKill()); + clientAppTO.setAuthorizationCodeTimeToLive(clientApp.getAuthorizationCodeTimeToLive()); + clientAppTO.getAudience().addAll(clientApp.getAudience()); return clientAppTO; } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index 56f6ccb39ec..fe626beb890 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -115,6 +115,7 @@ import org.apache.syncope.common.lib.types.OIDCGrantType; import org.apache.syncope.common.lib.types.OIDCResponseType; import org.apache.syncope.common.lib.types.OIDCSubjectType; +import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.lib.types.PolicyType; import org.apache.syncope.common.lib.types.SAML2SPNameId; @@ -979,6 +980,10 @@ protected static OIDCRPClientAppTO buildOIDCRP() { oidcrpTO.setRefreshTokenTimeToKill("P14D"); oidcrpTO.setDeviceTokenTimeToKill("PT5M"); + oidcrpTO.setRenewRefreshToken(true); + oidcrpTO.setIdTokenTimeToKill("PT5M"); + oidcrpTO.setAuthorizationCodeTimeToLive("PT30S"); + oidcrpTO.getAudience().add("https://api.apache.org"); return oidcrpTO; } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java index de79ce4833c..557e6769d2b 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.fail; import jakarta.ws.rs.core.Response; +import java.util.List; import java.util.Set; import java.util.UUID; import org.apache.commons.lang3.StringUtils; @@ -38,6 +39,7 @@ import org.apache.syncope.common.lib.to.SAML2SPClientAppTO; import org.apache.syncope.common.lib.types.ClientAppType; import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; import org.apache.syncope.common.lib.types.PolicyType; import org.apache.syncope.fit.AbstractITCase; import org.junit.jupiter.api.Test; @@ -146,6 +148,10 @@ public void readOIDCRP() { assertNotNull(found.getAccessPolicy()); assertNotNull(found.getAuthPolicy()); assertNotNull(found.getAccessTokenMaxTimeToLive()); + assertEquals(Boolean.TRUE, found.getRenewRefreshToken()); + assertEquals("PT5M", found.getIdTokenTimeToKill()); + assertEquals("PT30S", found.getAuthorizationCodeTimeToLive()); + assertEquals(List.of("https://api.apache.org"), found.getAudience()); } @Test @@ -194,6 +200,11 @@ public void updateOIDCRP() { oidcrpTO.setClientId("newClientId"); oidcrpTO.setAccessPolicy(accessPolicyTO.getKey()); oidcrpTO.setDeviceTokenTimeToKill("PT6M"); + oidcrpTO.setRenewRefreshToken(false); + oidcrpTO.setIdTokenTimeToKill("PT10M"); + oidcrpTO.setAuthorizationCodeTimeToLive("PT1M"); + oidcrpTO.getAudience().clear(); + oidcrpTO.getAudience().add("https://new-api.apache.org"); CLIENT_APP_SERVICE.update(ClientAppType.OIDCRP, oidcrpTO); OIDCRPClientAppTO updated = CLIENT_APP_SERVICE.read(ClientAppType.OIDCRP, oidcrpTO.getKey()); @@ -202,6 +213,10 @@ public void updateOIDCRP() { assertEquals("newClientId", updated.getClientId()); assertNotNull(updated.getAccessPolicy()); assertEquals("PT6M", updated.getDeviceTokenTimeToKill()); + assertEquals(Boolean.FALSE, updated.getRenewRefreshToken()); + assertEquals("PT10M", updated.getIdTokenTimeToKill()); + assertEquals("PT1M", updated.getAuthorizationCodeTimeToLive()); + assertEquals(List.of("https://new-api.apache.org"), updated.getAudience()); } @Test diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java index b1bc7e656c6..4d30da5c5e1 100644 --- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java +++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/mapping/OIDCRPClientAppTOMapper.java @@ -20,6 +20,7 @@ import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.to.ClientAppTO; @@ -29,6 +30,7 @@ import org.apache.syncope.common.lib.types.OIDCTokenEncryptionAlg; import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; import org.apache.syncope.common.lib.wa.WAClientApp; +import org.apereo.cas.oidc.services.DefaultRegisteredServiceOidcIdTokenExpirationPolicy; import org.apereo.cas.services.OidcRegisteredService; import org.apereo.cas.services.RegisteredService; import org.apereo.cas.services.RegisteredServiceAccessStrategy; @@ -40,6 +42,7 @@ import org.apereo.cas.services.RegisteredServiceServiceTicketExpirationPolicy; import org.apereo.cas.services.RegisteredServiceTicketGrantingTicketExpirationPolicy; import org.apereo.cas.support.oauth.services.DefaultRegisteredServiceOAuthAccessTokenExpirationPolicy; +import org.apereo.cas.support.oauth.services.DefaultRegisteredServiceOAuthCodeExpirationPolicy; import org.apereo.cas.support.oauth.services.DefaultRegisteredServiceOAuthDeviceTokenExpirationPolicy; import org.apereo.cas.support.oauth.services.DefaultRegisteredServiceOAuthRefreshTokenExpirationPolicy; @@ -100,6 +103,13 @@ public RegisteredService map( ifPresent(v -> service.setUserInfoEncryptedResponseEncoding(v.getExternalForm())); service.setJwtAccessToken(rp.isJwtAccessToken()); + if (rp.isJwtAccessToken()) { + Optional.ofNullable(rp.getJwtAccessTokenSigningAlg()). + filter(alg -> alg != OIDCTokenSigningAlg.none). + ifPresent(alg -> service.setJwtAccessTokenSigningAlg(alg.name())); + } + + Optional.ofNullable(rp.getRenewRefreshToken()).ifPresent(service::setRenewRefreshToken); service.setBypassApprovalPrompt(rp.isBypassApprovalPrompt()); service.setGenerateRefreshToken(rp.isGenerateRefreshToken()); if (StringUtils.isNotBlank(rp.getJwksUri())) { @@ -118,6 +128,12 @@ public RegisteredService map( service.setScopes(rp.getScopes().stream().collect(Collectors.toSet())); + Set audience = rp.getAudience().stream(). + filter(StringUtils::isNotBlank).collect(Collectors.toSet()); + if (!audience.isEmpty()) { + service.setAudience(audience); + } + setPolicies(service, authPolicy, mfaPolicy, accessStrategy, attributeReleasePolicy, tgtExpirationPolicy, stExpirationPolicy, tgtProxyExpirationPolicy, stProxyExpirationPolicy); @@ -148,6 +164,23 @@ public RegisteredService map( service.setDeviceTokenExpirationPolicy(policy); }); + Optional.ofNullable(rp.getIdTokenTimeToKill()).filter(StringUtils::isNotBlank). + ifPresent(duration -> { + DefaultRegisteredServiceOidcIdTokenExpirationPolicy policy = + new DefaultRegisteredServiceOidcIdTokenExpirationPolicy(); + policy.setTimeToKill(duration); + service.setIdTokenExpirationPolicy(policy); + }); + + Optional.ofNullable(rp.getAuthorizationCodeTimeToLive()).filter(StringUtils::isNotBlank). + ifPresent(duration -> { + DefaultRegisteredServiceOAuthCodeExpirationPolicy policy = + new DefaultRegisteredServiceOAuthCodeExpirationPolicy(); + policy.setNumberOfUses(1); + policy.setTimeToLive(duration); + service.setCodeExpirationPolicy(policy); + }); + return service; } } diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java index 69888267f6c..956df649a4f 100644 --- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java +++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java @@ -19,7 +19,9 @@ package org.apache.syncope.wa.starter; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collection; @@ -42,6 +44,7 @@ import org.apache.syncope.common.lib.types.OIDCGrantType; import org.apache.syncope.common.lib.types.OIDCResponseType; import org.apache.syncope.common.lib.types.OIDCSubjectType; +import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; import org.apache.syncope.common.lib.types.SAML2SPNameId; import org.apache.syncope.common.lib.wa.WAClientApp; import org.apache.syncope.common.rest.api.service.AuthModuleService; @@ -76,6 +79,12 @@ private static OIDCRPClientAppTO buildOIDCRP() { oidcrpTO.getSupportedGrantTypes().add(OIDCGrantType.password); oidcrpTO.getSupportedResponseTypes().add(OIDCResponseType.CODE); oidcrpTO.setLogoutType(LogoutType.BACK_CHANNEL); + oidcrpTO.setJwtAccessToken(true); + oidcrpTO.setJwtAccessTokenSigningAlg(OIDCTokenSigningAlg.RS256); + oidcrpTO.setRenewRefreshToken(true); + oidcrpTO.setIdTokenTimeToKill("PT5M"); + oidcrpTO.setAuthorizationCodeTimeToLive("PT30S"); + oidcrpTO.getAudience().add("https://api.example.org"); return oidcrpTO; } @@ -178,6 +187,12 @@ public void addClientApp() { assertEquals("uri1|uri2", oidc.getServiceId()); assertEquals(oidcrpto.getClientId(), oidc.getClientId()); assertEquals(oidcrpto.getClientSecret(), oidc.getClientSecret()); + assertEquals("RS256", oidc.getJwtAccessTokenSigningAlg()); + assertTrue(oidc.isRenewRefreshToken()); + assertEquals("PT5M", oidc.getIdTokenExpirationPolicy().getTimeToKill()); + assertEquals("PT30S", oidc.getCodeExpirationPolicy().getTimeToLive()); + assertEquals(1, oidc.getCodeExpirationPolicy().getNumberOfUses()); + assertEquals(Set.of("https://api.example.org"), oidc.getAudience()); assertTrue(oidc.getAuthenticationPolicy().getRequiredAuthenticationHandlers().contains("TestAuthModule")); assertTrue(((AnyAuthenticationHandlerRegisteredServiceAuthenticationPolicyCriteria) oidc. getAuthenticationPolicy().getCriteria()).isTryAll()); @@ -224,6 +239,33 @@ public void addClientApp() { assertTrue(found.getAttributeReleasePolicy() instanceof DenyAllAttributeReleasePolicy); } + @Test + public void unconfiguredOIDCProperties() { + SyncopeCoreTestingServer.CLIENT_APPS.clear(); + OIDCRPClientAppTO oidcrpTO = buildOIDCRP(); + oidcrpTO.setJwtAccessTokenSigningAlg(null); + oidcrpTO.setRenewRefreshToken(null); + oidcrpTO.setIdTokenTimeToKill(null); + oidcrpTO.setAuthorizationCodeTimeToLive(null); + oidcrpTO.getAudience().clear(); + + WAClientApp waClientApp = new WAClientApp(); + waClientApp.setClientAppTO(oidcrpTO); + addPolicies(waClientApp, false); + SyncopeCoreTestingServer.CLIENT_APPS.add(waClientApp); + waRestClient.getService(WAClientAppService.class).list(); + servicesManager.load(); + + OidcRegisteredService oidc = (OidcRegisteredService) + servicesManager.findServiceBy(oidcrpTO.getClientAppId()); + assertNotNull(oidc); + assertNull(oidc.getJwtAccessTokenSigningAlg()); + assertFalse(oidc.isRenewRefreshToken()); + assertNull(oidc.getIdTokenExpirationPolicy()); + assertNull(oidc.getCodeExpirationPolicy()); + assertTrue(oidc.getAudience().isEmpty()); + } + @Test public void delegatedAuthentication() { // 1. start with 1 client app and 1 auth module defined on mocked Core From 8fb6f61f3b10ca60d9d307758541edebda81d793 Mon Sep 17 00:00:00 2001 From: Marco Di Sabatino Di Diodoro Date: Mon, 9 Nov 2026 16:44:46 +0100 Subject: [PATCH 2/6] [SYNCOPE-2009] Fix checkstyle --- .../src/test/java/org/apache/syncope/fit/AbstractITCase.java | 1 - .../test/java/org/apache/syncope/fit/core/ClientAppITCase.java | 1 - 2 files changed, 2 deletions(-) diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index fe626beb890..2804010d7e3 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -115,7 +115,6 @@ import org.apache.syncope.common.lib.types.OIDCGrantType; import org.apache.syncope.common.lib.types.OIDCResponseType; import org.apache.syncope.common.lib.types.OIDCSubjectType; -import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.lib.types.PolicyType; import org.apache.syncope.common.lib.types.SAML2SPNameId; diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java index 557e6769d2b..62666346491 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java @@ -39,7 +39,6 @@ import org.apache.syncope.common.lib.to.SAML2SPClientAppTO; import org.apache.syncope.common.lib.types.ClientAppType; import org.apache.syncope.common.lib.types.ClientExceptionType; -import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; import org.apache.syncope.common.lib.types.PolicyType; import org.apache.syncope.fit.AbstractITCase; import org.junit.jupiter.api.Test; From f0d6375a84e926fbef17111ab6c2d66eca1915b5 Mon Sep 17 00:00:00 2001 From: Marco Di Sabatino Di Diodoro Date: Mon, 9 Nov 2026 17:33:14 +0100 Subject: [PATCH 3/6] [SYNCOPE-2009] Fix field --- .../common/lib/to/OIDCRPClientAppTO.java | 6 ++--- .../api/entity/am/OIDCRPClientApp.java | 4 +-- .../jpa/entity/am/JPAOIDCRPClientApp.java | 4 +-- .../neo4j/entity/am/Neo4jOIDCRPClientApp.java | 4 +-- .../wa/starter/WAServiceRegistryTest.java | 27 ------------------- 5 files changed, 9 insertions(+), 36 deletions(-) diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/OIDCRPClientAppTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/OIDCRPClientAppTO.java index f943ef2f219..2d1c2ae9829 100644 --- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/OIDCRPClientAppTO.java +++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/OIDCRPClientAppTO.java @@ -96,7 +96,7 @@ public class OIDCRPClientAppTO extends ClientAppTO { private OIDCTokenSigningAlg jwtAccessTokenSigningAlg; - private Boolean renewRefreshToken; + private boolean renewRefreshToken; private String idTokenTimeToKill; @@ -350,11 +350,11 @@ public void setJwtAccessTokenSigningAlg(final OIDCTokenSigningAlg jwtAccessToken this.jwtAccessTokenSigningAlg = jwtAccessTokenSigningAlg; } - public Boolean getRenewRefreshToken() { + public boolean getRenewRefreshToken() { return renewRefreshToken; } - public void setRenewRefreshToken(final Boolean renewRefreshToken) { + public void setRenewRefreshToken(final boolean renewRefreshToken) { this.renewRefreshToken = renewRefreshToken; } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java index d42a12d0019..f2e1ac10b49 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/OIDCRPClientApp.java @@ -146,9 +146,9 @@ public interface OIDCRPClientApp extends ClientApp { void setJwtAccessTokenSigningAlg(OIDCTokenSigningAlg jwtAccessTokenSigningAlg); - Boolean getRenewRefreshToken(); + boolean getRenewRefreshToken(); - void setRenewRefreshToken(Boolean renewRefreshToken); + void setRenewRefreshToken(boolean renewRefreshToken); String getIdTokenTimeToKill(); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java index 2e7ab638254..e04c383c18e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java @@ -462,12 +462,12 @@ public void setJwtAccessTokenSigningAlg(final OIDCTokenSigningAlg jwtAccessToken } @Override - public Boolean getRenewRefreshToken() { + public boolean getRenewRefreshToken() { return renewRefreshToken; } @Override - public void setRenewRefreshToken(final Boolean renewRefreshToken) { + public void setRenewRefreshToken(final boolean renewRefreshToken) { this.renewRefreshToken = renewRefreshToken; } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java index 90a7a037719..c1d892b8af3 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java @@ -439,12 +439,12 @@ public void setJwtAccessTokenSigningAlg(final OIDCTokenSigningAlg jwtAccessToken } @Override - public Boolean getRenewRefreshToken() { + public boolean getRenewRefreshToken() { return renewRefreshToken; } @Override - public void setRenewRefreshToken(final Boolean renewRefreshToken) { + public void setRenewRefreshToken(final boolean renewRefreshToken) { this.renewRefreshToken = renewRefreshToken; } diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java index 956df649a4f..88a9949ff6d 100644 --- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java +++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java @@ -239,33 +239,6 @@ public void addClientApp() { assertTrue(found.getAttributeReleasePolicy() instanceof DenyAllAttributeReleasePolicy); } - @Test - public void unconfiguredOIDCProperties() { - SyncopeCoreTestingServer.CLIENT_APPS.clear(); - OIDCRPClientAppTO oidcrpTO = buildOIDCRP(); - oidcrpTO.setJwtAccessTokenSigningAlg(null); - oidcrpTO.setRenewRefreshToken(null); - oidcrpTO.setIdTokenTimeToKill(null); - oidcrpTO.setAuthorizationCodeTimeToLive(null); - oidcrpTO.getAudience().clear(); - - WAClientApp waClientApp = new WAClientApp(); - waClientApp.setClientAppTO(oidcrpTO); - addPolicies(waClientApp, false); - SyncopeCoreTestingServer.CLIENT_APPS.add(waClientApp); - waRestClient.getService(WAClientAppService.class).list(); - servicesManager.load(); - - OidcRegisteredService oidc = (OidcRegisteredService) - servicesManager.findServiceBy(oidcrpTO.getClientAppId()); - assertNotNull(oidc); - assertNull(oidc.getJwtAccessTokenSigningAlg()); - assertFalse(oidc.isRenewRefreshToken()); - assertNull(oidc.getIdTokenExpirationPolicy()); - assertNull(oidc.getCodeExpirationPolicy()); - assertTrue(oidc.getAudience().isEmpty()); - } - @Test public void delegatedAuthentication() { // 1. start with 1 client app and 1 auth module defined on mocked Core From 0c54c93808e30b303d837e1ba9f6a0003d395afb Mon Sep 17 00:00:00 2001 From: Marco Di Sabatino Di Diodoro Date: Mon, 9 Nov 2026 17:39:00 +0100 Subject: [PATCH 4/6] [SYNCOPE-2009] Fix field --- .../core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java index e04c383c18e..2f4c4f669a6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAOIDCRPClientApp.java @@ -157,7 +157,7 @@ public class JPAOIDCRPClientApp extends AbstractClientApp implements OIDCRPClien @Enumerated(EnumType.STRING) private OIDCTokenSigningAlg jwtAccessTokenSigningAlg; - private Boolean renewRefreshToken; + private boolean renewRefreshToken; private String idTokenTimeToKill; From d695073024cdbb992fb562b464e0d088f56b6ad1 Mon Sep 17 00:00:00 2001 From: Marco Di Sabatino Di Diodoro Date: Mon, 9 Nov 2026 17:42:59 +0100 Subject: [PATCH 5/6] [SYNCOPE-2009] Fix field --- .../core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java index c1d892b8af3..b421470d63c 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java @@ -134,7 +134,7 @@ public class Neo4jOIDCRPClientApp extends AbstractClientApp implements OIDCRPCli private OIDCTokenSigningAlg jwtAccessTokenSigningAlg; - private Boolean renewRefreshToken; + private boolean renewRefreshToken; private String idTokenTimeToKill; From 7a30823c2a3612a244f76a0c11de3370f1323d67 Mon Sep 17 00:00:00 2001 From: Marco Di Sabatino Di Diodoro Date: Mon, 9 Nov 2026 19:19:51 +0100 Subject: [PATCH 6/6] [SYNCOPE-2009] fix import --- .../org/apache/syncope/wa/starter/WAServiceRegistryTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java index 88a9949ff6d..3cde39c9338 100644 --- a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java +++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java @@ -19,9 +19,7 @@ package org.apache.syncope.wa.starter; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collection;