store-for-match-later in delegated authN

42 views
Skip to first unread message

Yan Zhou

unread,
Oct 6, 2025, 11:01:13 AM (14 days ago) Oct 6
to CAS Community
Hello,

CAS 7.2, delegated AuthN with SAML. In CAS screen where you enter username in order for CAS to locate the external IdP, the business problem I deal with is that the username in CAS is Different from that is in External IdP.  For instance, I may enter username: johnsmith in CAS, it goes to Okta, but in Okta, user may enter their Okta username: jsmith. When SAML response comes back to CAS, I want CAS create a principal with the CAS username johnsmith (Not jsmith as Okta says), and with attributes from Okta jsmith user.  There is one level of indirection here.

How and where do I store the CAS username before CAS delegates to external Idp, and match it with response later on?  The outbound delegation and inbound response are two different requests.

thx!

Ray Bon

unread,
Oct 6, 2025, 2:23:03 PM (14 days ago) Oct 6
to cas-...@apereo.org
Do you control the data in the remote IdP?
If you do not control the remote data, how do you guarantee that the username returned is unique and not modifiable by the user?
What would happen if the user entered a different username, johns, instead?
If you just store the username as part of the login flow, any user with an Okta account could log in as one of your users.

You need to establish the remote / local relationship before the user logs in; then look up the local user with the remote username from Okta.

There is this property to get the remote  username [1]:
Cas.authn.pac4j.saml[0].name-id-attribute 


Ray


From: cas-...@apereo.org <cas-...@apereo.org> on behalf of Yan Zhou <yana...@gmail.com>
Sent: October 6, 2025 07:13
To: CAS Community <cas-...@apereo.org>
Subject: [cas-user] store-for-match-later in delegated authN
 
--
- Website: https://apereo.github.io/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 cas-user+u...@apereo.org.
To view this discussion visit https://groups.google.com/a/apereo.org/d/msgid/cas-user/fc344419-f41a-4b54-8ed9-84cc3e6649e0n%40apereo.org.

Yan Zhou

unread,
Oct 6, 2025, 5:05:57 PM (14 days ago) Oct 6
to CAS Community, Ray Bon
that is true!  I tried PrincipalIdAttribute, the flow made no difference.

But the problem remains.  say user johnsmith in CAS maps to  jsmith in Okta.  I can do some validation so that when Okta authenticates jsmith, CAS returns principal as johnsmith, I still want CAS to store johnsmith somewhere Before it delegates to Okta.  (It is possible multiple users in CAS maps to the same Okta user, thus we want to know the username entered in CAS -before- delegating to Okta).  This is similar to how CAS handles relay_state in SAML delegated authN. 

this following is my idea, but did not work.

public class MyDelegatedClientAuthenticationWebflowStateContributor extends DefaultDelegatedClientAuthenticationWebflowStateContributor {
    @Override
    public Map<String, Serializable> store(final RequestContext requestContext, final WebContext webContext,
                                           final Client client) throws Throwable {
// user enters username: johnsmith in CAS UI, CAS looks up external IdP, it is Okta.
// we store username johnsmith before delegated authN
// but I do not know how to get the username that user entered earlier in CAS UI
        String userBeforeDelegateAuthN = (String) ..... ??
        Map<String, Serializable> properties = new HashMap<>(super.store(requestContext, webContext, client));
        properties.put("userBeforeDelegateAuthN", userBeforeDelegateAuthN);
        return properties;
    }
   
    @Override
    public Service restore(final RequestContext requestContext,
                           final WebContext webContext,
                           final Optional<TransientSessionTicket> givenSessionTicket,
                           final Client client) {  
        val service = super.restore(requestContext, webContext, givenSessionTicket, client);  
        val properties = givenSessionTicket.get().getProperties();
// we put username back into request attribute as SAML response comes from Okta
// then, somewhere else (such as authenticationHandler),  I get this attribute, create the principal using this attribute:  johnsmith (CAS username), 
         // copying Okta attributes for jsmith
        webContext.setRequestAttribute("userBeforeDelegateAuthN", properties.get("userBeforeDelegateAuthN"));
        return service;
    }    
}
Reply all
Reply to author
Forward
0 new messages