Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
plnordquist opened this issue Jun 26, 2020 · 1 comment
Assignees
Labels
in: saml2 An issue in SAML2 modules type: enhancement A general enhancement
Milestone

Comments

@plnordquist
Copy link

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.

@plnordquist plnordquist added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Jun 26, 2020
@jzheaux jzheaux self-assigned this Jun 29, 2020
@jzheaux jzheaux added in: saml2 An issue in SAML2 modules type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Jun 29, 2020
jzheaux added a commit that referenced this issue Jul 15, 2020
Refactored into collaborators in preparation for introducing setters

Issue gh-8769
jzheaux added a commit that referenced this issue Aug 4, 2020
@jzheaux jzheaux closed this as completed in a402c38 Aug 4, 2020
@jzheaux
Copy link
Contributor

jzheaux commented Aug 4, 2020

Support for this has been added via the setAssertionValidator configuration method.

To validate assertions that have a <OneTimeUse> condition, you can do:

StorageService storage = new MemoryStorageService();
ReplayCache cache = new ReplayCache();
cache.setStorageService(storage);
ConditionValidator conditionValidator = new OneTimeUseConditionValidator(cache, null);

OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
provider.setAssertionValidator(assertionToken -> {
    Saml2ResponseValidatorResult result = OpenSamlAuthenticationProvider
            .createDefaultAssertionValidator().context(assertionToken);
    Assertion assertion = assertionToken.getAssertion();
    OneTimeUse oneTimeUse = assertion.getConditions().getOneTimeUse();
    ValidationContext context = // ... specify any context parameters needed
    try {
        if (conditionValidator.validate(oneTimeUse, assertion, context) == ValidationResult.VALID) {
            return result;
        }
    } catch (Exception e) {
        return result.concat(new Saml2Error(INVALID_ASSERTION, e.getMessage());
    }
    return result.concat(new Saml2Error(INVALID_ASSERTION, context.getValidationFailureMessage()));
});

http
    .saml2Login(saml2 -> saml2
        .authenticationManager(new ProviderManager(provider))
    );

Note that a default construction of OneTimeUseConditionValidator was not added to OpenSamlAuthenticationProvider at this time since it's not clear to me how to configure its replay cache without needing to replicate significant portions of the OpenSAML API inside of OpenSamlAuthenticationProvider.

@jzheaux jzheaux added this to the 5.4.0-RC1 milestone Aug 5, 2020
jzheaux added a commit that referenced this issue Oct 14, 2020
Remove deprecated usage

Issue gh-8769
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: saml2 An issue in SAML2 modules type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants