Describe the bug ValidationContext is lost unexpectedly causing InResponseTo validation to fail

To Reproduce Set a breakpoint here: https://github.com/tyler555g/SpringSecuritySAMLPOC/blob/main/src/main/java/com/SpringSecuritySSOPOC/saml/config/Saml2LoginSecurityConfig.java#L162 Run backend. Create a login call. Wait for a couple minutes. Create another login call. Login call occasionally loses ValidationContext. See logs attached and working code example.

Expected behavior InResponseTo validation passes correctly

Sample https://github.com/tyler555g/SpringSecuritySAMLPOC/blob/main/src/main/java/com/SpringSecuritySSOPOC/saml/config/Saml2LoginSecurityConfig.java#L162 SpringSecurityLogsNoResponseTo20250109.txt

Log snippets:

2025-01-09T14:53:11.642-05:00 TRACE 29112 --- [nio-8091-exec-5] o.o.s.s.a.SAML20AssertionValidator : SAML 2 Assertion ValidationContext - static parameters: {saml2.ValidIssuers=[redacted], saml2.SubjectConfirmation.ValidInResponseTo=null, saml2.ClockSkew=PT5M, saml2.Conditions.ValidAudiences=[http://localhost:8091/saml2/service-provider-metadata/azure-ad], saml2.SubjectConfirmation.ValidRecipients=[http://localhost:8091/login/saml2/sso/azure-ad]} 2025-01-09T14:53:11.642-05:00 TRACE 29112 --- [nio-8091-exec-5] o.o.s.s.a.SAML20AssertionValidator : SAML 2 Assertion ValidationContext - dynamic parameters: {} 2025-01-09T14:53:11.642-05:00 DEBUG 29112 --- [nio-8091-exec-5] o.o.s.s.a.SAML20AssertionValidator : Evaluating Assertion Issuer of : redacted 2025-01-09T14:53:11.642-05:00 DEBUG 29112 --- [nio-8091-exec-5] o.o.s.s.a.SAML20AssertionValidator : Matched valid issuer: redacted 2025-01-09T14:53:11.642-05:00 DEBUG 29112 --- [nio-8091-exec-5] o.o.s.s.a.SAML20AssertionValidator : No Conditions were indicated as required ...

Comment From: jzheaux

Hi, @tyler555g, thanks for the report.

I can't use the sample yet because it doesn't appear to start up. If I use the local profile, then I get the following exception:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'saml.decrypt' in value "${saml.decrypt}"
    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-6.1.13.jar:6.1.13]
    at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-6.1.13.jar:6.1.13]
    at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[spring-core-6.1.13.jar:6.1
        ....

When I use the default profile, then I get the following:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'backend.url' in value "${backend.url}"
    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-6.1.13.jar:6.1.13]
    at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-6.1.13.jar:6.1.13]
...

Can you update the code so that it starts up, and then I'd be happy to try and reproduce?

Comment From: jzheaux

One thing that strikes me is that OpenSAML's InResponseTo property is present, but null. That likely means that Spring Security couldn't find the associated AuthnRequest to compare to the value.

It makes me wonder if the issue is a race condition, perhaps via the browser, where the session cookie is different, so the Saml2AuthenticationRequestRepository is empty.

Comment From: tyler555g

Hi, @tyler555g, thanks for the report.

I can't use the sample yet because it doesn't appear to start up. If I use the local profile, then I get the following exception:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'saml.decrypt' in value "${saml.decrypt}" at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-6.1.13.jar:6.1.13] at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-6.1.13.jar:6.1.13] at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[spring-core-6.1.13.jar:6.1 .... When I use the default profile, then I get the following:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'backend.url' in value "${backend.url}" at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-6.1.13.jar:6.1.13] at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-6.1.13.jar:6.1.13] ... Can you update the code so that it starts up, and then I'd be happy to try and reproduce?

https://github.com/tyler555g/SpringSecuritySAMLPOC/blob/main/src/main/resources/application-local.properties

#You must either replace SAML_DECRYPTION_CERTIFICATE, SAML_SIGNING_KEY and IDP_METADATA_URL with your own values or specify them in environment variables
#X509 certificate used to decrypt incoming SAML messages
saml.decryption.certificate=${SAML_DECRYPTION_CERTIFICATE}
#RSA key used to sign AuthnRequest messages
saml.signing.key=${SAML_SIGNING_KEY}
security.jwt.token.expire-hour-length=12
webapp.url=http://localhost:4200/
backend.url=http://localhost:8091/
saml.idp.metadata-url=${IDP_METADATA_URL}

I've updated the application-local.properties with a comment explaining use of the environment variables. Please replace with your own as configured for your specific IDP.

Comment From: tyler555g

One thing that strikes me is that OpenSAML's InResponseTo property is present, but null. That likely means that Spring Security couldn't find the associated AuthnRequest to compare to the value.

It makes me wonder if the issue is a race condition, perhaps via the browser, where the session cookie is different, so the Saml2AuthenticationRequestRepository is empty.

Could you point to additional logging or tracing I can do to support this hypothesis? I am able to reproduce and capture the entire session in HTTP Toolkit.

Comment From: jzheaux

I am able to reproduce and capture the entire session in HTTP Toolkit.

Yes, I believe that may help. Are you able to post the HTTP request and response headers of the full authentication flow? Specifically, the /saml2/authenticate/azure-ad request and response, the IdP's response to the AuthnRequest, and the /login/saml2/sso/azure-ad request and response.

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.