Skip to content

SAML Assertion validation fails when OneTimeUse condition is sent from the IdP #8769

@plnordquist

Description

@plnordquist

Describe the bug
When a SAML IdP is configured to add a OneTimeUse condition to the SAML Assertion, the OpenSamlAuthenticationProvider throws a Saml2Exception and claims that the OneTimeUse condition is an unknown condition.

To Reproduce

  1. Configure an IdP to send the OneTimeUse condition
  2. Attempt to authenticate to the application
  3. Authentication fails due to the Saml2Exception

In Keycloak, the client registration can be configured with the Include OneTimeUse Condition slider to replicate this issue.

Expected behavior
This condition should be parsed and handled appropriately.

Debug Log output

2020-06-26 10:30:35.002 DEBUG 12544 --- [nio-8080-exec-3] .p.s.s.f.Saml2WebSsoAuthenticationFilter : Request is to process authentication
2020-06-26 10:30:35.070 DEBUG 12544 --- [nio-8080-exec-3] s.s.p.s.a.OpenSamlAuthenticationProvider : Validating SAML response from <IdP>
2020-06-26 10:30:35.102 DEBUG 12544 --- [nio-8080-exec-3] s.s.p.s.a.OpenSamlAuthenticationProvider : Validating 1 assertions
2020-06-26 10:30:35.109 DEBUG 12544 --- [nio-8080-exec-3] s.s.p.s.a.OpenSamlAuthenticationProvider : Found 1 validation errors in SAML response [ID_7e615c61-9dda-4629-90fa-989a817c3282]
2020-06-26 10:30:35.109 DEBUG 12544 --- [nio-8080-exec-3] .p.s.s.f.Saml2WebSsoAuthenticationFilter : Authentication request failed: Saml2AuthenticationException{error=[invalid_assertion] Invalid assertion [ID_573ac246-7ea6-4098-af2a-2c97296011dc] for SAML response [ID_7e615c61-9dda-4629-90fa-989a817c3282]}

org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException: An error occurred while validating the assertion: Unknown Condition '{urn:oasis:names:tc:SAML:2.0:assertion}OneTimeUse' of type 'null' in assertion 'ID_573ac246-7ea6-4098-af2a-2c97296011dc'
	at org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider.authException(OpenSamlAuthenticationProvider.java:510)
	at org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider.validateResponse(OpenSamlAuthenticationProvider.java:320)
	at org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider.authenticate(OpenSamlAuthenticationProvider.java:205)
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)
	at org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter.attemptAuthentication(Saml2WebSsoAuthenticationFilter.java:109)
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:239)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:353)
	at org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter.doFilterInternal(Saml2WebSsoAuthenticationRequestFilter.java:146)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:353)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:353)
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:353)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:353)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:353)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:223)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:184)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:141)
	at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.springframework.security.saml2.Saml2Exception: An error occurred while validating the assertion: Unknown Condition '{urn:oasis:names:tc:SAML:2.0:assertion}OneTimeUse' of type 'null' in assertion 'ID_573ac246-7ea6-4098-af2a-2c97296011dc'
	at org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider.validateAssertion(OpenSamlAuthenticationProvider.java:403)
	at org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider.validateResponse(OpenSamlAuthenticationProvider.java:317)
	... 61 common frames omitted

Sample

I've added a OneTimeUse condition test in a commit to my fork here plnordquist@0e5b5a8a34 which replicates this failure.

I think fixing this requires adding a OneTimeUseConditionValidator to the list of ConditionValidators in the OpenSamlAuthenticationProvider but that validator seems to require a ReplayCache that enforces this condition so tokens cannot be replayed.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions