Shinyproxy: SAML authentication timeout (?) error

Created on 15 Feb 2021  ·  6Comments  ·  Source: openanalytics/shinyproxy

Hello 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:

image

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?

question

All 6 comments

I found the following issue, which points to similar error messages indicating a mismatch between SHA-1 and SHA-256 signing algorithms, but if that were the case in our installation, I'm not sure why it would work most of the time, but then fail intermittently after a long period of inactivity, so I don't think this is my problem.

As a followup, I set the SAML portion of the app to DEBUG level logging, and saw the following from one of these timeout events:

shinyproxy-server_1  | 2021-02-15 21:03:14.050 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.context.SAMLContextProviderImpl  : No IDP specified, using default http://auth.company.com/adfs/services/trust
shinyproxy-server_1  | 2021-02-15 21:03:14.050 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.util.SAMLUtil          : Index for AssertionConsumerService not specified, returning default
shinyproxy-server_1  | 2021-02-15 21:03:14.051 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLEntryPoint         : Processing SSO using WebSSO profile
shinyproxy-server_1  | 2021-02-15 21:03:14.051 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.websso.WebSSOProfileImpl      : Using default consumer service with binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
shinyproxy-server_1  | 2021-02-15 21:03:14.052 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.storage.HttpSessionStorage    : Storing message a17g5ahbxxxxxxxxxxx454jj to session hxxxxxxJ_E8xxxxxxxxHP_TwIxxxxxxxH
shinyproxy-server_1  | 2021-02-15 21:03:14.053  INFO 1 --- [  XNIO-1 task-2] o.s.security.saml.log.SAMLDefaultLogger  : AuthNRequest;SUCCESS;XXX.XX.XXX.XX;https://my.company.com/app;http://auth.company.com/adfs/services/trust;;;
shinyproxy-server_1  | 2021-02-15 21:03:14.805 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Request is to process authentication
shinyproxy-server_1  | 2021-02-15 21:03:14.806 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Attempting SAML2 authentication using profile urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser
shinyproxy-server_1  | 2021-02-15 21:03:14.811 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.processor.SAMLProcessorImpl   : Retrieving message using binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
shinyproxy-server_1  | 2021-02-15 21:03:14.816  INFO 1 --- [  XNIO-1 task-2] colMessageXMLSignatureSecurityPolicyRule : SAML protocol message was not signed, skipping XML signature processing
shinyproxy-server_1  | 2021-02-15 21:03:14.816 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.util.SAMLUtil          : Found endpoint org.opensaml.saml2.metadata.impl.AssertionConsumerServiceImpl@6531c789 for request URL https://my.company.com/app/saml/SSO based on location attribute in metadata
shinyproxy-server_1  | 2021-02-15 21:03:14.816 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.storage.HttpSessionStorage    : Message a17g5ahb0gieeh9b4e48ff6a3d454jj found in session hXmYJ_E8wf6F2plFsHP_TwIZS1Cg6n2Vu4wD4i2H, clearing
shinyproxy-server_1  | 2021-02-15 21:03:14.817 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.w.WebSSOProfileConsumerImpl      : Verifying issuer of the Response
shinyproxy-server_1  | 2021-02-15 21:03:14.817 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.w.WebSSOProfileConsumerImpl      : Verifying signature
shinyproxy-server_1  | 2021-02-15 21:03:14.820 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.t.MetadataCredentialResolver     : Added 1 credentials resolved from metadata of entity http://auth.company.com/adfs/services/trust
shinyproxy-server_1  | 2021-02-15 21:03:14.826 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.w.WebSSOProfileConsumerImpl      : Processing Bearer subject confirmation
shinyproxy-server_1  | 2021-02-15 21:03:14.836 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.w.WebSSOProfileConsumerImpl      : Validation of authentication statement in assertion failed, skipping
shinyproxy-server_1  | org.springframework.security.authentication.CredentialsExpiredException: Authentication statement is too old to be used with value 2021-02-15T15:51:35.161Z
                        ...
shinyproxy-server_1  | 2021-02-15 21:03:14.839 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.SAMLAuthenticationProvider    : Error validating SAML message
shinyproxy-server_1  |
shinyproxy-server_1  | org.opensaml.common.SAMLException: Response doesn't have any valid assertion which would pass subject validation
                       ...
shinyproxy-server_1  |
shinyproxy-server_1  | 2021-02-15 21:03:14.841  INFO 1 --- [  XNIO-1 task-2] o.s.security.saml.log.SAMLDefaultLogger  : AuthNResponse;FAILURE;XXX.XX.XXX.XX;https://my.company.com/app;http://auth.company.com/adfs/services/trust;;;org.opensaml.common.SAMLException: Response doesn't have any valid assertion which would pass subject validation
shinyproxy-server_1  |  at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:265)
                       ...
shinyproxy-server_1  |
shinyproxy-server_1  | 2021-02-15 21:03:14.842  INFO 1 --- [  XNIO-1 task-2] e.o.containerproxy.service.UserService   : Authentication failure [user: ] [error: Error validating SAML message]
shinyproxy-server_1  | 2021-02-15 21:03:14.848 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
shinyproxy-server_1  |
shinyproxy-server_1  | org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
                       ...
shinyproxy-server_1  |
shinyproxy-server_1  | 2021-02-15 21:03:14.849 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Updated SecurityContextHolder to contain null Authentication
shinyproxy-server_1  | 2021-02-15 21:03:14.849 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@19dec0b4
shinyproxy-server_1  | 2021-02-15 21:03:14.858 ERROR 1 --- [  XNIO-1 task-2] org.thymeleaf.TemplateEngine             : [THYMELEAF][XNIO-1 task-2] Exception processing template "/": Error resolving template [/], template might not exist or might not be accessible by any of the configured Template Resolvers
shinyproxy-server_1  | org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/], template might not exist or might not be accessible by any of the configured Template Resolvers
                       ...
shinyproxy-server_1  |
shinyproxy-server_1  | 2021-02-15 21:03:14.861 ERROR 1 --- [  XNIO-1 task-2] io.undertow.request                      : UT005023: Exception handling request to /app/error
shinyproxy-server_1  |
shinyproxy-server_1  | org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/], template might not exist or might not be accessible by any of the configured Template Resolvers

This sequence is repeated 7 times in about 2 seconds before ShinyProxy gives up and dumps me to a generic springboot error page as shown above. Two curiosities:

1) org.springframework.security.authentication.CredentialsExpiredException: Authentication statement is too old to be used with value 2021-02-15T15:51:35.161Z -- this seems to be root of the problem, but I'm not sure if this is an issue on the IdP side or on the ShinyProxy side

2) 2021-02-15 21:03:14.861 ERROR 1 --- [ XNIO-1 task-2] io.undertow.request : UT005023: Exception handling request to /app/error -- it appears my error template is not loading properly. If it were, I think I could put a link to the "logout" page, which users could click on to reset their session (not sure if that would work, just a hunch)

Any ideas?

Related links:

1) https://stackoverflow.com/questions/48289763/spring-security-infinite-loop-after-initial-login-and-timeout
2) https://stackoverflow.com/questions/30528636/idp-initiated-saml-login-error-authentication-statement-is-too-old-to-be-used

Link 2 suggests a couple solutions: (1) align maxAuthenticationAge to the lifetime of the ADFS or (2) set forceAuthN to true

Are these possible from within the ShinyProxy config, or am I looking at a custom build necessary for this?

Hi @jat255

Thank you for your extensive report and research! This is quite a coincidence: I experienced the exact same issue on one our deployments as well. Yesterday I found the same solutions as you, I only had to test it :)

Setting the forceAuthN option indeed solves the issue on our end. The only downside that this has is that the user is forced to provide its full credentials again, whereas ADFS believes that the credentials of the user are still "fresh enough". Also, from the link you provided it seems that some IDPs ignore this option (e.g. google). You can enable the forceAuthN by specifying it in your saml configuration:

proxy:
  saml:
    idp-metadata-url: ... 
    ....
    force-authn: true

This option is not yet documented on our website, I'll take care of this.

Specifying the maxAuthenticationAge option is not yet implemented in ShinyProxy. However, as I see value in this option (.e.g. using google as IDP), I'm going to implement an option for this.

I'll also try to provide a more useful behavior in case of a CredentialsExpiredException.

@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 :smirk: 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.

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!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thomas-chauvet picture thomas-chauvet  ·  5Comments

xtrasimplicity picture xtrasimplicity  ·  5Comments

donarus picture donarus  ·  3Comments

erossini picture erossini  ·  3Comments

algo-se picture algo-se  ·  6Comments