Java Cas Client - Preserving Request Parameters

227 views
Skip to first unread message

Colin Ryan

unread,
Dec 18, 2019, 4:30:38 PM12/18/19
to cas-...@apereo.org
Folks,

Me again...sorry...still trying yet another way to do what I need
done...le sigh...


Anyhow all these variations now lead me to this new question, hopefully
as I learn more about CAS my questions become more clear.


So I have my Web Application that is using Spring Security and CAS. I
need this application to support multiple dynamic contexts where each
context represents a Service definition in CAS which in turn has varying
authentication parameters for it.

So for example if someone enters on:


https://myapp.com?profile=xxxx versus https://myapp.com?profile=yyyy

Then the application will redirect to CAS which will match a service
pattern for ?profile=xxxx or ?profile=yyyyy which in turn allows me to
specify a specific authentication configuration for profile xxxx to be,
for example, LDAP, or yyyy to be RADIUS etc. Note, these are different
authentication providers not decisions for multi-factor, so  as far as I
can tell I cannot use any of the mfa-auth triggering.

However when I write my Java Spring CAS Web client I must specify the
serviceURL in the ServiceProperties Bean. As such anything I GET to the
web application is supplanted by the serviceURL as it's redirected to
CAS and hence I loose pattern match to different profiles at the CAS
authentication.


@Bean
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        serviceProperties.setService("https://myapp.com");
        serviceProperties.setSendRenew(false);
        return serviceProperties;
    }


Documentation around the service parameter in other places like
mod_auth_cas etc, implies that so long as things are properly escaped in
the URL's that they will survive the CAS redirect.


But in the Java client examples the serviceURL is in a Bean and is hard
coded and hence all the parameter are dropped as the user is taken over
to the CAS login screens.


Is there a way via the Java API to take the request.queryString()
contents and get that appended to the serviceURL so that when redirected
to CAS that the parameters are preserved...i.e.
https://cas/cas/login?service=https://myapp.com?profile=xxx.


Thanks


Colin Ryan


Ray Bon

unread,
Dec 18, 2019, 5:04:53 PM12/18/19
to cas-...@apereo.org
Colin,

I see what you are saying. I checked a spring service app and it does indeed change the URL.
Could you switch to web.xml defined cas client configuration? It preserves the attributes (I checked another of our apps).

Ray
-- 
Ray Bon
Programmer Analyst
Development Services, University Systems

I respectfully acknowledge that my place of work is located within the ancestral, traditional and unceded territory of the Songhees, Esquimalt and WSÁNEĆ Nations.

Cemal Önder

unread,
Dec 19, 2019, 2:35:39 AM12/19/19
to CAS Community
Yes I had that problem too when I want to create generic library that serves as helper to make my microservices CASify. Here is a solution: Spring Security CAS calls createServcieUrl of CasAuthenticationEntryPoint before every request. This is the place where serviceProperites are used for redirection. You can overwrite ServiceProperties with your dynamically created url here. But keep in mind that neither I like this solution but no choice because of ServiceProperties requires URL on startup which you mentioned.

public class DynamicRedirectCasAuthenticationEntryPoint extends CasAuthenticationEntryPoint
{
  // ...

@Override
protected String createServiceUrl( final HttpServletRequest request, final HttpServletResponse response )
{
      // here set your new serviceProperties based on the request etc. with your business logic
this.setServiceProperties( serviceProperties );
return super.createServiceUrl( request, response );
}

  // ...

}

Colin Ryan

unread,
Dec 23, 2019, 2:24:53 PM12/23/19
to cas-...@apereo.org
Cemal,

I tried this approach to this but I keep getting a too many re-directs error. I'm new'ish to Spring Security so maybe I'm missing something.

But basically I see the "DynamicRedirectCasAuthenticationEntryPoint" being processed in every request the first time through it I'm intercepting


Then in my overridden entrypoint I'm changing the serviceURL to be https://host/desktop?parameter=one which in turn redirects me to CAS where I authenticate against a service that matches /desktop?parameter=one, per your code hint.

But as far as I can tell it then redirects me back to https://host/desktop?parameter=one&ticket=xxxxx but seems to treat this like a new request and sends me back to CAS - CAS does not in turn prompt be to authenticate again - but returns to the application again but with


then on and on each time getting a new ticket parameter.

It's almost like it's treating the new ticket string as not matching the original dynamically modified service string but upon taking me to CAS  it SSO's me but with a different ticket and around and around we go.

I was under the impression that the serviceURL definition in the Service definition of the authenticationEntryPoint was to match a service policy in CAS, and then CAS would SSO it to other URL's that are behind the .authenticated() filters of Spring SecurityConfig...but it's seems to want to go validate very string permutation.

Thoughts, what blindingly obvious thing am I missing :-).

Here is the SecurityFilter as an aside:

 protected void configure(HttpSecurity http) throws Exception {
      http
        .authorizeRequests()
        .regexMatchers("/desktop/.*","/desktop?.*","/login.*")
        .authenticated()
        .and()
        .authorizeRequests()
        .regexMatchers("/")
        .permitAll()
        .and()
        .httpBasic()
        .authenticationEntryPoint(dynamicAuthenticationEntryPoint)
        .and()
        .logout().logoutSuccessUrl("/logout")
        .and()
        .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
        .addFilterBefore(logoutFilter, LogoutFilter.class);
      

    }
Thank's in Advance.


Colin
--
- 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 cas-user+u...@apereo.org.
To view this discussion on the web visit https://groups.google.com/a/apereo.org/d/msgid/cas-user/f574fc4c-55ca-4c34-bb1f-d751a8f34553%40apereo.org.


Anmol Budhewar

unread,
Dec 24, 2019, 1:40:12 AM12/24/19
to cas-...@apereo.org
Can you refer how to get java cas client because I don't have any idea how to build java cas client can you help me


--
- 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 cas-user+u...@apereo.org.

Cemal Önder

unread,
Dec 24, 2019, 2:37:27 AM12/24/19
to CAS Community
Ryan,

How do you configure your CasAuthenticationFilter? Do you configure AuthenticationManager? Here is an example:

.addFilterBefore( casAuthenticationFilter(), .....)


private CasAuthenticationFilter casAuthenticationFilter( )
{
 
CasAuthenticationFilter authenticationFilter = new CasAuthenticationFilter( );
 authenticationFilter
.setAuthenticationManager( authenticationManager );
 
return authenticationFilter;
}

@Bean
protected AuthenticationManager authenticationManager( )
{
 
return new ProviderManager( Arrays.asList( casAuthenticationProvider( ) ) );
}


@Bean
public CasAuthenticationProvider casAuthenticationProvider( )
{
 
final CasAuthenticationProvider provider = new CasAuthenticationProvider( );
 provider
.setServiceProperties( serviceProperties );
 provider
.setKey( "CAS_DUMMY_KEY" );
 
return provider;
}


Colin Ryan

unread,
Dec 24, 2019, 10:19:38 AM12/24/19
to cas-...@apereo.org
Cemal,


 protected void configure(HttpSecurity http) throws Exception {
      http
        .authorizeRequests()
        .regexMatchers("/desktop/.*","/desktop?.*","/login.*")
        .authenticated()
        .and()
        .authorizeRequests()
        .regexMatchers("/")
        .permitAll()
        .and()
        .httpBasic()
        .authenticationEntryPoint(authenticationEntryPoint)

        .and()
        .logout().logoutSuccessUrl("/logout")
        .and()
        .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
        .addFilterBefore(logoutFilter, LogoutFilter.class);
     

    }

@Bean
    @Primary
    public DynamicRedirectCasAuthenticationEntryPoint authenticationEntryPoint(
      ServiceProperties sP) {
     
        DynamicRedirectCasAuthenticationEntryPoint entryPoint
          = new DynamicRedirectCasAuthenticationEntryPoint();
        entryPoint.setLoginUrl("https://casserver/cas/login");
        entryPoint.setServiceProperties(sP);
        return entryPoint;
    }

@Bean
    public CasAuthenticationProvider casAuthenticationProvider() {
     
        CasAuthenticationProvider provider = new CasAuthenticationProvider();
        provider.setServiceProperties(serviceProperties());
        provider.setTicketValidator(ticketValidator());
       
        provider.setUserDetailsService(
          s -> new User("colinr", "Mellon", true, true, true, true,
            AuthorityUtils.createAuthorityList("ROLE_ADMIN")));
        provider.setKey("CAS_PROVIDER_LOCALHOST_9000");
        return provider;
    }

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.authenticationProvider(authenticationProvider);
    }

    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
      return new ProviderManager(Arrays.asList(authenticationProvider));
    }

    @Bean
    public CasAuthenticationFilter casAuthenticationFilter(ServiceProperties sP) throws Exception {
      CasAuthenticationFilter filter = new CasAuthenticationFilter();
      filter.setServiceProperties(sP);
      filter.setAuthenticationManager(authenticationManager());
      return filter;
    }
--
- 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 cas-user+u...@apereo.org.

Colin Ryan

unread,
Dec 24, 2019, 10:48:21 AM12/24/19
to cas-...@apereo.org, Anmol Budhewar
Anmol,

There are lot's of resources out there..

I found the following to be very useful in understanding the overall process/components.


and



and good starting point for Annotated forms of this I found at:




On 2019-12-24 1:39 a.m., Anmol Budhewar wrote:
Can you refer how to get java cas client because I don't have any idea how to build java cas client can you help me


On Thu, Dec 19, 2019, 03:00 Colin Ryan <col...@caveo.ca> wrote:
Folks,

Me again...sorry...still trying yet another way to do what I need
done...le sigh...


Anyhow all these variations now lead me to this new question, hopefully
as I learn more about CAS my questions become more clear.


So I have my Web Application that is using Spring Security and CAS. I
need this application to support multiple dynamic contexts where each
context represents a Service definition in CAS which in turn has varying
authentication parameters for it.

So for example if someone enters on:


Ray Bon

unread,
Jan 2, 2020, 1:04:49 PM1/2/20
to cas-...@apereo.org
Colin,

From your description, the 'desktop' page is protected and accessing it requires the user to be authenticated (and this is set in 'SecurityFilter'). The flow you describe has the user arriving at the 'desktop' page with a ST from CAS but not yet authenticated in the host. The host then redirects to CAS, as you experience, and ends with too many redirects.

I am not sure of how the 'SecurityFilter' should be configured, since I have not used it, but the order of processing should be; logout, validate, authenticate, others.
In other words, login validation (ST check) should happen before authentication. (Logout always happens first.) Validation creates an authenticated user in the host.

Other things to consider:
You do not need to have the login page authorized.
Escape the '?' in /desktop?.* or combine both desktops as '/desktop.*'

Ray

Ray Bon

unread,
Jan 8, 2020, 3:30:49 PM1/8/20
to cas-...@apereo.org
Colin,

Perhaps this approach can work (I assume on the login page, user is selecting a profile):

1. Redirect from /login to CAS, /cas?service=serviceURL?param=...
2. CAS sends ST to serviceURL
3. ST validation happens, authenticated principal is returned
4. how does spring know where to redirect?

Cemal's solution is the spring way. The above solution may result in a lot of extra work.

Ray

On Wed, 2020-01-08 at 12:27 -0500, Colin Ryan wrote:
Ray,

Sorry my friend, I'm catching what your saying but just not groking it...I even backed out my DynamicCasAuthEntryPoint overridden class and still getting too many redirects even when not appending things to the serviceURL.

@Override

    protected void configure(HttpSecurity http) throws Exception {
      http
        .authorizeRequests()
        .regexMatchers("/desktop.*")

        .authenticated()
        .and()
        .authorizeRequests()
        .regexMatchers("/")
        .permitAll()
        .and()
        .httpBasic()
        .authenticationEntryPoint(authenticationEntryPoint)

     .and()
        .logout().logoutSuccessUrl("/logout")
        .and()
        .addFilterBefore(singleSignOutFilter, CasAuthenticationFilter.class)
        .addFilterBefore(logoutFilter, LogoutFilter.class);
     

    }

The service URL here is just https://host/desktop thus going to https://host/desktop redirects me properly to CAS but then upon redirect back with the ST gives me redirect errors.

I believe I"m understanding the implications of what you've describe (i.e. it's arriving back from CAS authorization with a ST ticket appended but this isn't meaning to the /desktop application that it's authenticated....

But again, don't get how to set the SecurityFilter routes other than the above which is based upon many of the examples I see online.

Alas as a reminder my requirement is to have an central multi-user application (/desktop) that in turn must take to user to CAS with something  form of identifier (in my previous examples was the GET parameter) so that I can from a common app get mapped to specific set's of service definitions in CAS to ensure the user in question get's presented with the proper Authentication setup (not always just a difference in MFA so I can't rely on using step-up)

Le Sigh...
Reply all
Reply to author
Forward
0 new messages