[java-idp-plugin-vci] 01/01: Reviewing issuer-metadata flow, intercept, counter, profile id constant, headers, tests
Codeberg
noreply at shibboleth.net
Fri Nov 6 11:41:50 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch dev/REVISSFLOW
in repository java-idp-plugin-vci.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-vci/commit/20e631f3dbf9a7417ed928f7e8aa6c595f36bf2d
commit 20e631f3dbf9a7417ed928f7e8aa6c595f36bf2d
Author: Janne Lauros <janne.lauros at csc.fi>
AuthorDate: Fri Nov 6 13:41:24 2026 +0200
Reviewing issuer-metadata flow, intercept, counter, profile id constant, headers, tests
---
README.md | 2 +
...efaultOpenIDVCIIssuerMetadataConfiguration.java | 5 ++
.../FormOutboundIssuerMetadataResponseMessage.java | 52 ++++++++++++--
.../vci/issuer-metadata/issuer-metadata-beans.xml | 20 ++----
.../vci/issuer-metadata/issuer-metadata-flow.xml | 6 +-
.../plugin/openidvci/conf/openid-vci.properties | 2 +
...ltOpenIDVCIIssuerMetadataConfigurationTest.java | 37 ++++++++++
...mOutboundIssuerMetadataResponseMessageTest.java | 83 +++++++++++++++++++++-
8 files changed, 185 insertions(+), 22 deletions(-)
diff --git a/README.md b/README.md
index 5000174..979ed4a 100644
--- a/README.md
+++ b/README.md
@@ -4682,6 +4682,7 @@ Endpoint `/idp/profile/openid/vci/issuer-metadata`, profile ID `http://geant.org
| Name | Type | Default | Description |
|---|---|---|---|
| `issuerMetadataSignatureSigningConfiguration` | SignatureSigningConfiguration | **openidvci.issuerMetadata.SigningConfiguration** | Signing of the metadata. Published unsigned until **openidvci.issuerMetadata.SigningCredentials** exists. Signed metadata is served for `Accept: application/jwt`. |
+| `inboundInterceptorFlows` | List<String> | | Interceptor flows run before the metadata is formed. |
| `outboundInterceptorFlows` | List<String> | | Interceptor flows run before the response is sent, also for an error response. |
**OAuth.JWTVCIssuer** (`/idp/profile/oauth/jwt-vc-issuer`) and **OAuth.AuthorizationServer**
@@ -4976,6 +4977,7 @@ The file comes with the plugin and is loaded automatically.
| `openidvci.issuerMetadata.resolver.values` | Bean ID | **openidvci.issuerMetadata.DefaultDynamicValueResolvers** | Members resolved outside the file. |
| `openidvci.issuerMetadata.minRefreshDelay`, `…maxRefreshDelay` | Duration | `PT5M`, `PT4H` | Bounds of re-reading the file. |
| `openidvci.issuerMetadata.signedLifetime` | Duration | | `exp` of signed metadata. Unset, no `exp`. |
+| `openidvci.issuerMetadata.cacheControl` | String | `max-age=3600` | Cache-Control of the response. Keep `max-age` shorter than `signedLifetime`. |
| `openidvci.jwtVcIssuer.template` | File pathname | `%{idp.home}/static/jwt-vc-issuer.json` | JWT VC issuer metadata template. |
| `openidvci.jwtVcIssuer.templated` | Boolean | `true` | Whether the file is Velocity. |
| `openidvci.jwtVcIssuer.resolver` | Bean ID | **openidvci.DefaultJWTVCIssuerMetadataResolver** | Resolver of the document. |
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/config/impl/DefaultOpenIDVCIIssuerMetadataConfiguration.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/config/impl/DefaultOpenIDVCIIssuerMetadataConfiguration.java
index 3034ffe..7bb33f4 100644
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/config/impl/DefaultOpenIDVCIIssuerMetadataConfiguration.java
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/config/impl/DefaultOpenIDVCIIssuerMetadataConfiguration.java
@@ -50,6 +50,11 @@ public class DefaultOpenIDVCIIssuerMetadataConfiguration extends AbstractInterce
@NotEmpty
public static final String PROFILE_ID = "http://geant.org/ns/profiles/openid/vci/issuer-metadata";
+ /** Credential Issuer metadata profile counter name. */
+ @Nonnull
+ @NotEmpty
+ public static final String PROFILE_COUNTER = "net.shibboleth.idp.profiles.openid.vci.issuer-metadata";
+
/** Signing of the published metadata. */
@Nonnull
private Function<ProfileRequestContext, SignatureSigningConfiguration>
diff --git a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundIssuerMetadataResponseMessage.java b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundIssuerMetadataResponseMessage.java
index 3ef7a23..ec6b380 100644
--- a/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundIssuerMetadataResponseMessage.java
+++ b/openid-vci-impl/src/main/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundIssuerMetadataResponseMessage.java
@@ -37,19 +37,24 @@ import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
import org.slf4j.Logger;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.net.HttpHeaders;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.oidc.security.jose.SignatureException;
import net.shibboleth.oidc.security.jose.SignatureSigningParameters;
import net.shibboleth.oidc.security.jose.context.SecurityParametersContext;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
import net.shibboleth.shared.resolver.ResolverException;
/**
@@ -95,12 +100,28 @@ public class FormOutboundIssuerMetadataResponseMessage extends AbstractProfileAc
@NonnullBeforeExec
private CredentialIssuerMetadata metadata;
+ /** Value of the Cache-Control header. */
+ @Nonnull
+ @NotEmpty
+ private String cacheControl;
+
/** Constructor. */
public FormOutboundIssuerMetadataResponseMessage() {
final Function<ProfileRequestContext, SecurityParametersContext> splcs = new ChildContextLookup<>(
SecurityParametersContext.class).compose(new OutboundMessageContextLookup());
assert splcs != null;
securityParametersLookupStrategy = splcs;
+ cacheControl = "max-age=3600";
+ }
+
+ /**
+ * Set the value of the Cache-Control header.
+ *
+ * @param value header value
+ */
+ public void setCacheControl(@Nonnull @NotEmpty final String value) {
+ checkSetterPreconditions();
+ cacheControl = Constraint.isNotNull(StringSupport.trimOrNull(value), "Cache-Control cannot be null or empty");
}
/**
@@ -178,24 +199,33 @@ public class FormOutboundIssuerMetadataResponseMessage extends AbstractProfileAc
@Override
protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ final HttpServletResponse response = getHttpServletResponse();
+ if (response == null) {
+ log.error("{} Could not fetch HttpServletResponse", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, IdPEventIds.INVALID_PROFILE_CONFIG);
+ return;
+ }
+
if (prefersSignedMetadata()) {
final SignatureSigningParameters parameters = resolveSigningParameters(profileRequestContext);
if (parameters != null) {
- publishSigned(profileRequestContext, parameters);
+ publishSigned(profileRequestContext, parameters, response);
return;
}
log.debug("{} Signed metadata was asked for but no signing credential is configured, "
+ "publishing the document unsigned", getLogPrefix());
}
+ final CredentialIssuerMetadataSuccessResponse unsigned;
try {
- profileRequestContext.ensureOutboundMessageContext()
- .setMessage(new CredentialIssuerMetadataSuccessResponse(metadata.getMembers()));
+ unsigned = new CredentialIssuerMetadataSuccessResponse(metadata.getMembers());
} catch (final JsonProcessingException e) {
log.error("{} Could not serialize Credential Issuer metadata", getLogPrefix(), e);
ActionSupport.buildEvent(profileRequestContext, EventIds.IO_ERROR);
return;
}
+ setHeaders(response);
+ profileRequestContext.ensureOutboundMessageContext().setMessage(unsigned);
log.debug("{} Publishing Credential Issuer metadata of {}, members {}", getLogPrefix(),
metadata.getCredentialIssuer(), metadata.getMembers().keySet());
@@ -206,9 +236,10 @@ public class FormOutboundIssuerMetadataResponseMessage extends AbstractProfileAc
*
* @param profileRequestContext current profile request context
* @param parameters parameters to sign with
+ * @param response response to set the headers of
*/
private void publishSigned(@Nonnull final ProfileRequestContext profileRequestContext,
- @Nonnull final SignatureSigningParameters parameters) {
+ @Nonnull final SignatureSigningParameters parameters, @Nonnull final HttpServletResponse response) {
final Instant now = Instant.now();
final JWTClaimsSet.Builder claims = new JWTClaimsSet.Builder();
@@ -230,6 +261,7 @@ public class FormOutboundIssuerMetadataResponseMessage extends AbstractProfileAc
return;
}
+ setHeaders(response);
profileRequestContext.ensureOutboundMessageContext()
.setMessage(new SignedCredentialIssuerMetadataSuccessResponse(document));
@@ -237,6 +269,18 @@ public class FormOutboundIssuerMetadataResponseMessage extends AbstractProfileAc
metadata.getCredentialIssuer(), metadata.getMembers().keySet());
}
+ /**
+ * Set the headers of a published document.
+ *
+ * @param response response to set the headers of
+ */
+ private void setHeaders(@Nonnull final HttpServletResponse response) {
+ response.setHeader(HttpHeaders.CACHE_CONTROL, cacheControl);
+ response.setHeader(HttpHeaders.X_CONTENT_TYPE_OPTIONS, FormOutboundMetadataDocumentResponseMessage.NOSNIFF);
+ response.setHeader(HttpHeaders.CONTENT_SECURITY_POLICY,
+ FormOutboundMetadataDocumentResponseMessage.CONTENT_SECURITY_POLICY);
+ }
+
/**
* Get whether the request names signed metadata and not the unsigned document.
*
diff --git a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-beans.xml b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-beans.xml
index bc3d407..f911156 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-beans.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-beans.xml
@@ -12,9 +12,11 @@
default-init-method="initialize" default-destroy-method="destroy">
<bean id="openidvci.profileId" class="java.lang.String"
- c:_0="http://geant.org/ns/profiles/openid/vci/issuer-metadata"/>
+ c:_0="#{T(org.geant.shibboleth.plugin.openidvci.profile.config.impl.DefaultOpenIDVCIIssuerMetadataConfiguration).PROFILE_ID}"/>
<bean id="openidvci.loggingId" class="java.lang.String"
c:_0="%{openidvci.logging.issuer-metadata:OpenID.VCI.IssuerMetadata}"/>
+ <util:constant id="shibboleth.metrics.ProfileCounter"
+ static-field="org.geant.shibboleth.plugin.openidvci.profile.config.impl.DefaultOpenIDVCIIssuerMetadataConfiguration.PROFILE_COUNTER"/>
<bean id="InitializeUnverifiedRelyingPartyContext"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeUnverifiedRelyingPartyContext" scope="prototype" />
@@ -27,21 +29,17 @@
<bean lazy-init="true"
class="org.geant.shibboleth.plugin.openidvci.profile.config.navigate.IssuerMetadataSignatureSigningConfigurationLookupFunction" />
</property>
- <!-- Metadata is signed with the issuer's own key, there is no client to have one. -->
<property name="signatureSigningParametersResolver">
<bean class="net.shibboleth.oidc.security.jose.impl.BasicSignatureSigningParametersResolver" />
</property>
- <property name="securityParametersContextLookupStrategy">
- <bean parent="shibboleth.Functions.Compose"
- c:g-ref="shibboleth.oidc.ChildLookupOrCreate.JWTSecurityParameters"
- c:f-ref="shibboleth.MessageContextLookup.Outbound" />
- </property>
</bean>
<bean id="OpenIDVCIFormOutboundMessage"
class="org.geant.shibboleth.plugin.openidvci.profile.impl.FormOutboundIssuerMetadataResponseMessage"
scope="prototype"
p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
+ p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier"
+ p:cacheControl="%{openidvci.issuerMetadata.cacheControl:max-age=3600}"
p:metadataResolver-ref="#{'%{openidvci.issuerMetadata.resolver:openidvci.DefaultCredentialIssuerMetadataResolver}'.trim()}"
p:signedMetadataLifetime="#{'%{openidvci.issuerMetadata.signedLifetime:}'.trim().isEmpty() ? null : T(java.time.Duration).parse('%{openidvci.issuerMetadata.signedLifetime:}'.trim())}" />
@@ -51,14 +49,6 @@
<property name="eventContextLookupStrategy">
<bean class="net.shibboleth.idp.profile.context.navigate.WebFlowCurrentEventLookupFunction" />
</property>
- <property name="mappedErrors">
- <map value-type="com.nimbusds.oauth2.sdk.ErrorObject">
- <entry key="#{T(org.opensaml.profile.action.EventIds).IO_ERROR}"
- value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).SERVER_ERROR}" />
- <entry key="#{T(org.opensaml.profile.action.EventIds).INVALID_SEC_CFG}"
- value="#{T(com.nimbusds.oauth2.sdk.OAuth2Error).SERVER_ERROR}" />
- </map>
- </property>
</bean>
</beans>
diff --git a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-flow.xml b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-flow.xml
index 3840e91..8ec6ff0 100644
--- a/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-flow.xml
+++ b/openid-vci-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-flow.xml
@@ -5,6 +5,9 @@
parent="openid/vci/abstract-api">
<action-state id="InitializeMandatoryContexts">
+ <on-entry>
+ <set name="flowScope.transitionAfterInboundIntercept" value="'BuildResponseMessage'"/>
+ </on-entry>
<evaluate expression="InitializeProfileRequestContext" />
<evaluate expression="PopulateMetricContext" />
<evaluate expression="FlowStartPopulateAuditContext" />
@@ -13,9 +16,10 @@
<evaluate expression="SelectRelyingPartyConfiguration" />
<evaluate expression="SelectProfileConfiguration" />
<evaluate expression="PopulateIssuerMetadataSignatureSigningParameters" />
+ <evaluate expression="PopulateInboundInterceptContext" />
<evaluate expression="'proceed'" />
- <transition on="proceed" to="BuildResponseMessage" />
+ <transition on="proceed" to="CheckInboundInterceptContext" />
</action-state>
<bean-import resource="classpath:/META-INF/net/shibboleth/idp/flows/openid/vci/issuer-metadata/issuer-metadata-beans.xml" />
diff --git a/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties b/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties
index dd03190..3c109e6 100644
--- a/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties
+++ b/openid-vci-impl/src/main/resources/org/geant/shibboleth/plugin/openidvci/conf/openid-vci.properties
@@ -53,6 +53,8 @@ openidvci.issuer =
#openidvci.issuerMetadata.maxRefreshDelay = PT4H
# Lifetime written as 'exp' of signed metadata. Leave unset to publish it without one.
#openidvci.issuerMetadata.signedLifetime = PT4H
+# Cache-Control of the response, unsigned and signed. Keep max-age shorter than signedLifetime.
+#openidvci.issuerMetadata.cacheControl = max-age=3600
# Signing key of issued Verifiable Credentials. Used only if you enable the credential of the
# same name in conf/openid-vci-credentials.xml. Until that, Credentials are signed with the
diff --git a/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/config/impl/DefaultOpenIDVCIIssuerMetadataConfigurationTest.java b/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/config/impl/DefaultOpenIDVCIIssuerMetadataConfigurationTest.java
new file mode 100644
index 0000000..8ec39a7
--- /dev/null
+++ b/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/config/impl/DefaultOpenIDVCIIssuerMetadataConfigurationTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2025, GÉANT
+ *
+ * Licensed under the Apache License, Version 2.0 (the “License”); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an “AS IS” BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.geant.shibboleth.plugin.openidvci.profile.config.impl;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/** Tests for {@link DefaultOpenIDVCIIssuerMetadataConfiguration}. */
+public class DefaultOpenIDVCIIssuerMetadataConfigurationTest {
+
+ @Test
+ public void testId() {
+ Assert.assertEquals(new DefaultOpenIDVCIIssuerMetadataConfiguration().getId(),
+ DefaultOpenIDVCIIssuerMetadataConfiguration.PROFILE_ID);
+ }
+
+ @Test
+ public void testCounter() {
+ Assert.assertEquals(DefaultOpenIDVCIIssuerMetadataConfiguration.PROFILE_COUNTER,
+ "net.shibboleth.idp.profiles.openid.vci.issuer-metadata");
+ }
+
+}
diff --git a/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundIssuerMetadataResponseMessageTest.java b/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundIssuerMetadataResponseMessageTest.java
index 25eab5d..f5cacb1 100644
--- a/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundIssuerMetadataResponseMessageTest.java
+++ b/openid-vci-impl/src/test/java/org/geant/shibboleth/plugin/openidvci/profile/impl/FormOutboundIssuerMetadataResponseMessageTest.java
@@ -49,6 +49,8 @@ import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
import net.shibboleth.idp.profile.testing.ActionTestingSupport;
import net.shibboleth.idp.profile.testing.RequestContextBuilder;
@@ -56,9 +58,11 @@ import net.shibboleth.oidc.jwa.support.SignatureConstants;
import net.shibboleth.oidc.security.credential.BasicJWKCredential;
import net.shibboleth.oidc.security.jose.SignatureSigningParameters;
import net.shibboleth.oidc.security.jose.context.SecurityParametersContext;
+import net.shibboleth.shared.logic.ConstraintViolationException;
import net.shibboleth.shared.primitive.NonnullSupplier;
import net.shibboleth.shared.resolver.ResolverException;
import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.webflow.execution.RequestContext;
/**
@@ -72,6 +76,8 @@ public class FormOutboundIssuerMetadataResponseMessageTest {
private MockHttpServletRequest httpRequest;
+ private MockHttpServletResponse httpResponse;
+
private ECKey signingKey;
@BeforeMethod
@@ -80,6 +86,7 @@ public class FormOutboundIssuerMetadataResponseMessageTest {
profileRequestCtx = new WebflowRequestContextProfileRequestContextLookup().apply(requestCtx);
profileRequestCtx.setOutboundMessageContext(new MessageContext());
httpRequest = new MockHttpServletRequest();
+ httpResponse = new MockHttpServletResponse();
signingKey = new ECKeyGenerator(Curve.P_256).keyID("metadata-signing").generate();
}
@@ -100,6 +107,14 @@ public class FormOutboundIssuerMetadataResponseMessageTest {
private FormOutboundIssuerMetadataResponseMessage actionOf(final CredentialIssuerMetadata metadata,
final Duration lifetime) throws Exception {
+ final FormOutboundIssuerMetadataResponseMessage action = uninitialized(metadata, true);
+ action.setSignedMetadataLifetime(lifetime);
+ action.initialize();
+ return action;
+ }
+
+ private FormOutboundIssuerMetadataResponseMessage uninitialized(final CredentialIssuerMetadata metadata,
+ final boolean withResponse) {
final FormOutboundIssuerMetadataResponseMessage action = new FormOutboundIssuerMetadataResponseMessage();
action.setMetadataResolver(new StubResolver(metadata, false));
action.setHttpServletRequestSupplier(new NonnullSupplier<>() {
@@ -107,11 +122,24 @@ public class FormOutboundIssuerMetadataResponseMessageTest {
return httpRequest;
}
});
- action.setSignedMetadataLifetime(lifetime);
- action.initialize();
+ if (withResponse) {
+ action.setHttpServletResponseSupplier(new NonnullSupplier<>() {
+ public HttpServletResponse get() {
+ return httpResponse;
+ }
+ });
+ }
return action;
}
+ private void assertDocumentHeaders(final String cacheControl) {
+ Assert.assertEquals(httpResponse.getHeaders("Cache-Control"), List.of(cacheControl));
+ Assert.assertEquals(httpResponse.getHeaders("X-Content-Type-Options"),
+ List.of(FormOutboundMetadataDocumentResponseMessage.NOSNIFF));
+ Assert.assertEquals(httpResponse.getHeaders("Content-Security-Policy"),
+ List.of(FormOutboundMetadataDocumentResponseMessage.CONTENT_SECURITY_POLICY));
+ }
+
private void configureSigning(final String algorithm, final PrivateKey key) throws Exception {
final BasicJWKCredential credential = new BasicJWKCredential();
credential.setPublicKey(signingKey.toPublicKey());
@@ -323,6 +351,57 @@ public class FormOutboundIssuerMetadataResponseMessageTest {
ActionTestingSupport.assertEvent(actionOf(CredentialIssuerMetadata.parse(members())).execute(requestCtx),
EventIds.INVALID_SEC_CFG);
+ Assert.assertNull(httpResponse.getHeader("Cache-Control"));
+ }
+
+ @Test
+ public void testUnsignedHeaders() throws Exception {
+ ActionTestingSupport.assertProceedEvent(
+ actionOf(CredentialIssuerMetadata.parse(members())).execute(requestCtx));
+
+ assertDocumentHeaders("max-age=3600");
+ }
+
+ @Test
+ public void testSignedHeaders() throws Exception {
+ httpRequest.addHeader("Accept", "application/jwt");
+ configureSigning(SignatureConstants.ALGO_ID_SIGNATURE_ES_256, signingKey.toPrivateKey());
+
+ ActionTestingSupport.assertProceedEvent(
+ actionOf(CredentialIssuerMetadata.parse(members())).execute(requestCtx));
+
+ assertDocumentHeaders("max-age=3600");
+ }
+
+ @Test
+ public void testCacheControlReplacesPreviousValue() throws Exception {
+ httpResponse.addHeader("Cache-Control", "no-store");
+ final FormOutboundIssuerMetadataResponseMessage action =
+ uninitialized(CredentialIssuerMetadata.parse(members()), true);
+ action.setCacheControl(" max-age=60 ");
+ action.initialize();
+
+ ActionTestingSupport.assertProceedEvent(action.execute(requestCtx));
+
+ assertDocumentHeaders("max-age=60");
+ }
+
+ @Test
+ public void testEmptyCacheControl() throws Exception {
+ final FormOutboundIssuerMetadataResponseMessage action =
+ uninitialized(CredentialIssuerMetadata.parse(members()), true);
+
+ Assert.assertThrows(ConstraintViolationException.class, () -> action.setCacheControl(" "));
+ }
+
+ @Test
+ public void testNoHttpServletResponse() throws Exception {
+ final FormOutboundIssuerMetadataResponseMessage action =
+ uninitialized(CredentialIssuerMetadata.parse(members()), false);
+ action.initialize();
+
+ ActionTestingSupport.assertEvent(action.execute(requestCtx), IdPEventIds.INVALID_PROFILE_CONFIG);
+ Assert.assertNull(profileRequestCtx.ensureOutboundMessageContext().getMessage());
}
@Test
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list