SAML authentication timeout (?) error
See original GitHub issueHello again! As we’re progressing through development of our app using SAML, we’ve found an issue with authentication that only appears to happen if a tab is left open for some amount of time (haven’t figured out exact timespan that causes the issue) and a user then refreshes their tab (not sure if a new window also triggers it at this point). It appears that after this time of inactivity, ShinyProxy tries to reauthenticate, and fails for some reason. The only solution I’ve found is to clear site data in the browser for our IdP (auth.company.com). Once that is done, things work as expected.
When this happens, ShinyProxy tries to authenticate many times in a row (I can see many SAML redirect callbacks flick through in the URL bar), but eventually fails, and dumps me onto http://my.company.com/app/saml/SSO with the following error:
In the server logs, I see the following:
shinyproxy-server_1 | 2021-02-15 04:00:11.531 INFO 1 --- [ XNIO-1 task-4] o.s.security.saml.log.SAMLDefaultLogger : AuthNRequest;SUCCESS;XXX.XX.XX.XXX;https://my.company.com/app;http://auth.company.com/adfs/services/trust;;;
shinyproxy-server_1 | 2021-02-15 04:00:11.608 INFO 1 --- [ XNIO-1 task-4] colMessageXMLSignatureSecurityPolicyRule : Validation of protocol message signature succeeded, message type: {urn:oasis:names:tc:SAML:2.0:protocol}Response
shinyproxy-server_1 | 2021-02-15 04:00:11.609 INFO 1 --- [ XNIO-1 task-4] o.s.security.saml.log.SAMLDefaultLogger : AuthNResponse;FAILURE;XXX.XX.XX.XXX;https://my.company.com/app;http://auth.company.com/adfs/services/trust;;;org.springframework.security.saml.SAMLStatusException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null
Something odd is it looks like the first authentication attempt succeeds, but then ShinyProxy doesn’t like it for some reason and tries again.
Full error traceback:
shinyproxy-server_1 | at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:146)
shinyproxy-server_1 | at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:88)
shinyproxy-server_1 | at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)
shinyproxy-server_1 | at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:219)
shinyproxy-server_1 | at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:524)
shinyproxy-server_1 | at sun.reflect.GeneratedMethodAccessor63.invoke(Unknown Source)
shinyproxy-server_1 | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
shinyproxy-server_1 | at java.lang.reflect.Method.invoke(Method.java:498)
shinyproxy-server_1 | at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
shinyproxy-server_1 | at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205)
shinyproxy-server_1 | at com.sun.proxy.$Proxy90.authenticate(Unknown Source)
shinyproxy-server_1 | at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:92)
shinyproxy-server_1 | at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:155)
shinyproxy-server_1 | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
shinyproxy-server_1 | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92)
shinyproxy-server_1 | at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77)
shinyproxy-server_1 | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
shinyproxy-server_1 | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.saml.metadata.MetadataGeneratorFilter.doFilter(MetadataGeneratorFilter.java:87)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
shinyproxy-server_1 | at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
shinyproxy-server_1 | at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
shinyproxy-server_1 | at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
shinyproxy-server_1 | at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
shinyproxy-server_1 | at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
shinyproxy-server_1 | at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
shinyproxy-server_1 | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
shinyproxy-server_1 | at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
shinyproxy-server_1 | at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
shinyproxy-server_1 | at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
shinyproxy-server_1 | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
shinyproxy-server_1 | at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
shinyproxy-server_1 | at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
shinyproxy-server_1 | at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
shinyproxy-server_1 | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
shinyproxy-server_1 | at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
shinyproxy-server_1 | at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
shinyproxy-server_1 | at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
shinyproxy-server_1 | at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
shinyproxy-server_1 | at io.undertow.server.handlers.PathHandler.handleRequest(PathHandler.java:91)
shinyproxy-server_1 | at eu.openanalytics.containerproxy.util.ProxyMappingManager$ProxyPathHandler.handleRequest(ProxyMappingManager.java:160)
shinyproxy-server_1 | at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
shinyproxy-server_1 | at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
shinyproxy-server_1 | at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
shinyproxy-server_1 | at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
shinyproxy-server_1 | at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
shinyproxy-server_1 | at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
shinyproxy-server_1 | at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
shinyproxy-server_1 | at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
shinyproxy-server_1 | at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
shinyproxy-server_1 | at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
shinyproxy-server_1 | at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130)
shinyproxy-server_1 | at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
shinyproxy-server_1 | at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78)
shinyproxy-server_1 | at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99)
shinyproxy-server_1 | at io.undertow.server.Connectors.executeRootHandler(Connectors.java:370)
shinyproxy-server_1 | at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:836)
shinyproxy-server_1 | at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
shinyproxy-server_1 | at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2019)
shinyproxy-server_1 | at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1558)
shinyproxy-server_1 | at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1449)
shinyproxy-server_1 | at java.lang.Thread.run(Thread.java:748)
The simple solution would be to click the “Logout” button, but that button is not shown on the SAML error screen. It’s tough to debug, because I cannot make it happen on demand. According to my IdP support person, ADFS indeed issues a valid token at the beginning of this process, so it seems to be a problem local to how ShinyProxy is handling the token it receives. I’m guessing since the browser tab never closed, perhaps the login cookie has expired and isn’t renewed correctly, but I have no idea what I’m talking about, so take that with a grain of salt.
I’ll come back with some hopefully more useful information when I have the error happen again, but I was curious to see if anyone had seen this and had any ideas on how to fix it?
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (1 by maintainers)
Top GitHub Comments
This seems to be working over the past few days (haven’t seen the error again), so I’ll go ahead and close this out. Thanks again!
@LEDfan thanks so much for your prompt reply, and solution! I’ve implemented that change, but now I must wait 24 hours to see if it worked 😏 I’ve at least confirmed that I see
ForceAuthn="true"
in the SAML payload, so the option definitely works on the ShinyProxy side.I think the
forceAuthN
option will not be too hard on our users, since most of our users use enterprise authentication hooked in with the automatic Windows credential management, so they don’t have to enter any passwords to get access to SAML-protected resources. I’ll report back here in a few days to let you know if it worked for our case.