Justin, Loggin out of the SP does not necessarily log out of cas (SLO is messy business). If ForceAuthn is not forcing authentication, that should be your focus. Perhaps cas is not sending ForceAuthn to the delegated authn server, or perhaps the delegated server is ignoring it.
Why does resolveServiceFromRequestContext return null? Is the service removed from the context somewhere along the flow? Or perhaps resolve18Context is broken? Ray On Wed, 2023-11-22 at 09:34 -0800, Justin Isenhour wrote: Notice: This message was sent from outside the University of Victoria email system. Please be cautious with links and sensitive information. I've been tracing the code and have made the following observations: The AuthNRequst from the SP comes into CAS and a TST is created with the service reference, then you are redirected to IDP. After authenticating with IDP, you are redirected back to CAS, which triggers DelegatedClientAuthenticationAction.doExecute(final RequestContext context)<https://github.com/apereo/cas/blob/5.3.x/support/cas-server-support-pac4j-webflow/src/main/java/org/apereo/cas/web/flow/DelegatedClientAuthenticationAction.java#L184>. For the first login, singleSignOnSessionExists(context) [L188] returns false. As a result of this, restoreAuthenticationRequestInContext(context, webContext, clientName) [L212] is called which restores the details from the TST, including the requested service reference. After logout of SP and new AuthNRequest, singleSignOnSessionExists(context) [L188] returns true because the previously issued TGT is still in the webflow scope and the TGT is not expired. Because of this, the code takes a different path, restoreAuthenticationRequestInContext is not called, instead the service is set by calling resolveServiceFromRequestContext(context) [L204], which returns null. With debugging, I have seen that on the subsequent logins, if I have singleSignOnSessionExists return false like it does the first time through, it appears to work as desired and you land back in the client app every time. That points me to the TGT being in the webflow is causing this behavior. Thoughts? DelegatedClientAuthenticationAction.doExecute code for reference: @Override public Event doExecute(final RequestContext context) { final HttpServletRequest request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context); final HttpServletResponse response = WebUtils.getHttpServletResponseFromExternalWebflowContext(context); if (!isLogoutRequest(request) && singleSignOnSessionExists(context)) { final String tgt = WebUtils.getTicketGrantingTicketId(context); final Optional<Authentication> authnResult = getSingleSignOnAuthenticationFrom(context); if (authnResult.isPresent()) { final Authentication authentication = authnResult.get(); final Object clientNames = authentication.getAttributes() .getOrDefault(ClientCredential.AUTHENTICATION_ATTRIBUTE_CLIENT_NAME, new ArrayList<>()); final String clientName = CollectionUtils.firstElement(clientNames).map(Object::toString).orElse(StringUtils.EMPTY); final Service service = resolveServiceFromRequestContext(context); if (isDelegatedClientAuthorizedFor(clientName, service)) { LOGGER.debug("An existing single sign-on session already exists. Skipping delegation and routing back to CAS authentication flow"); prepareForLoginPage(context); return resumeWebflow(); } } final Service resolvedService = resolveServiceFromRequestContext(context); LOGGER.debug("Single sign-on session in unauthorized for service [{}]", resolvedService); centralAuthenticationService.deleteTicket(tgt); } final String clientName = request.getParameter(Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER); LOGGER.debug("Delegated authentication is handled by client name [{}]", clientName); if (hasDelegationRequestFailed(request, response.getStatus()).isPresent()) { throw new IllegalArgumentException("Delegated authentication has failed with client " + clientName); } final String logoutEndpoint = request.getParameter(SAML2ServiceProviderMetadataResolver.LOGOUT_ENDPOINT_PARAMETER); final J2EContext webContext = Pac4jUtils.getPac4jJ2EContext(request, response); if (StringUtils.isNotBlank(clientName)) { final Service service; if (StringUtils.isBlank(logoutEndpoint)) { service = restoreAuthenticationRequestInContext(context, webContext, clientName); } else { service = null; } final BaseClient<Credentials, CommonProfile> client = findDelegatedClientByName(request, clientName, service); final Credentials credentials; try { credentials = client.getCredentials(webContext); LOGGER.debug("Retrieved credentials from client as [{}]", credentials); if (credentials == null) { throw new IllegalArgumentException("Unable to determine credentials from the context with client " + client.getName()); } } catch (final Exception e) { return handleException(webContext, client, e); } final ClientCredential clientCredential = new ClientCredential(credentials, client.getName()); WebUtils.putCredential(context, clientCredential); WebUtils.putService(context, service); final Service resolvedService = authenticationRequestServiceSelectionStrategies.resolveService(service); final RegisteredService registeredService = servicesManager.findServiceBy(resolvedService); WebUtils.putRegisteredService(context, registeredService); return super.doExecute(context); } prepareForLoginPage(context); if (response.getStatus() == HttpStatus.UNAUTHORIZED.value()) { return stopWebflow(); } return error(); } On Wednesday, November 22, 2023 at 12:11:38 AM UTC-5 Justin Isenhour wrote: Hello, I'm hoping someone may have a suggestion of where I can look for the root of this problem. We are running CAS 5.3.16 and have a mix of authentication handlers setup including several LDAP auth handlers, delegated auth to AzureAD via OIDC, and SAML delegated auth to various other IDPs. We have a SAML client that is sending an AuthNRequest with ForceAuthn="true" that is not working as expected when CAS uses Delegated auth. On the first login request, everything seems to be working fine. If you log out of that client application, then login again, you get prompted for authentication as expected, but instead of being redirected back to the requested client, CAS directs to the the generic success page. This is only an issue when authentication is done via delegated authentication client, saml and oidc but have the same issue. If authentication is done directly in CAS via LDAP auth handler, then the flow works as expected and you land back into the app every time. I have CAS source code and am pretty familiar with the code, we been using CAS since 3.x, but I haven't been able to pin point the issue yet. Anyone have any advice or suggestions? Thanks in advance, Justin Isenhour -- - Website: https://apereo.github.io/cas - Gitter Chatroom: https://gitter.im/apereo/cas - List Guidelines: https://goo.gl/1VRrw7 - Contributions: https://goo.gl/mh7qDG --- You received this message because you are subscribed to the Google Groups "CAS Community" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/a/apereo.org/d/msgid/cas-user/c3ec0df864eab453a39f312d525909ffe2424c00.camel%40uvic.ca.
