Jasig CAS - how to customize WebFlow to redirect user after successful login? - spring-webflow

I'm trying to integrate CAS auth in our web services and use the Jasig CAS server (v. 4.2) which is Spring-based webapp.
Unfortunately, Jasig CAS server can only use service ID for redirection after successful login.
It is unacceptable, because CAS server is located behind reverse proxy and we dont' use DNS.
So, login URL looks like:
http://a.b.c.d/cas/login?service=http://x.x.x.x/context-path/auth-entry-point
where
a.b.c.d - external (proxy) IP address
x.x.x.x - internal (service / CAS client) IP address
I've read Jasig docs, but found no way to obfuscate service URL.
For now i'm trying to implement custom logic. I want to pass redirect url as separate param:
http://a.b.c.d/login?service=<serviceUID_like_YYY-YYY-YYY>&redirect=<base64_encoded_URL>
.. and use this param for redirection instead of service ID.
According to doc Jasig CAS uses Spring Webflow to implement login scenario (login-webflow.xml).
And that's the place where redirection caused:
<end-state id="redirectView" view="externalRedirect:#{requestScope.response.url}"/>
Since i'm not familiar with Spring Weblow the question is:
How can i receive "redirect" URL param, decode and use it for redirection?
P.S. Sorry for my bad english, i hope it's at least parseable :-)

Ok, it was quite simple. For anyone who interested:
Create custom service bean under org.jasig.cas.* package:
package org.jasig.cas.usercustom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
#Component("userCustomRedirectHelper")
public class RedirectHelper {
final static Logger logger = LoggerFactory.getLogger(RedirectHelper.class);
public String decodeURLFromString(String src) {
String url = new String(Base64Utils.decodeFromString(src));
logger.debug("Redirect URL: " + src);
return url;
}
}
Modify login-webflow.xml as follows:
<on-start>
<!-- get redirect param, decode and place into flowScope -->
<evaluate expression="userCustomRedirectHelper.decodeURLFromString(requestParameters.redirect)" result="flowScope.customRedirectURL" />
</on-start>
<!-- redirect -->
<end-state id="redirectView" view="externalRedirect:#{flowScope.customRedirectURL}"/>

Related

How to enable CookieCsrfTokenRepository.withHttpOnlyFalse() and #EnableJdbcHttpSession?

I have a Spring boot application with Spring Authorization Server (the new 4.0.1 package Spring Authorization)
The server is in a cluster of servers thus it needs to save the session in a DB so I use #EnableJdbcHttpSession.
The Authorization service has state change request (e.g login etc...) with HTTP POST called from client written in Angular JS.
In order to secure my HTTP POST requests I use csrf e.g. csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
Its seems that #EnableJdbcHttpSession invoke the HttpSessionCsrfTokenRepository so I can't define the post request to work with CookieCsrfTokenRepository.
When I define CookieCsrfTokenRepository the SESSION cookie is not getting created, is it possible to define both somehow ?
#Configuration
#EnableJdbcHttpSession
public class SecurityConfiguration {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize ->
authorize.anyRequest().authenticated()
).formLogin(withDefaults())
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
return http.build();
}
}
This code will fix the issue
http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))

spring cloud gateway + spring security and multiple oauth2 client

I'm going to put a spring cloud gateway in front of some existing web applications which are already using keycloak as their identity provider and I want to authenticate the incoming requests inside the gateway. currently, each web application is already configured with the proper client-id and it redirects to the keycloak with the proper values. now, the gateway must do the authorization-code flow instead of each application, so it has to know in advance which client-is is for which requested url.
so, I was investigating how to implement it and I'm still here without any proper solution.
what is the solution for it? or, is it really a gateway responsibility to do that?
You can have a look to this answer:
How to create Spring Cloud gateway filter to add client-credentials access token?
In order to support different client-ids (secrets, token-uris and so on), you can just define multiple configurations in the spring.security.oauth2.client .registration section and make the clientid dynamic in the Oauth2ClientGatewayFilter class:
String clientId = ...
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientId)
.principal("myPrincipal").build();
Actually, I found a solution however I'm not sure whether it's the best one.
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange().pathMatchers("/actuator/**").permitAll().and()
.authorizeExchange().anyExchange().authenticated().and().csrf().disable().oauth2Login()
.and()
.exceptionHandling().authenticationEntryPoint(createEntryPoints())
.and()
.oauth2ResourceServer().jwt()
.jwtAuthenticationConverter(grantedAuthoritiesExtractor());
return http.build();
}
public ServerAuthenticationEntryPoint createEntryPoints() {
List<DelegateEntry> entryPoints = new ArrayList<>();
entryPoints
.add(new DelegateEntry(ServerWebExchangeMatchers.pathMatchers("/app1"),
new RedirectServerAuthenticationEntryPoint("/oauth2/authorization/client1")));
//other clients will be added here
DelegatingServerAuthenticationEntryPoint defaultEntryPoint = new DelegatingServerAuthenticationEntryPoint(
entryPoints);
defaultEntryPoint.setDefaultEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED));
return defaultEntryPoint;
}
so the client1 will be used for the /app1 and so on.
as i said earlier i'm not sure and there might be better solution for that.

Spring Security OAuth2 Behind a Proxy Server

There seem to be many examples of Spring Security OAuth2, but most of them run on localhost at some specific set of ports. I was able to get my application working with ports specified for my AuthorizationServer and my ResourceServer. The next step I needed to take was move this application behind a proxy server, but the application stopped functioning. The main issues seem to be path related, but I'm struggling with lack of examples on how to accomplish the task of moving OAuth2 Spring behind a proxy server. I've focused on overriding the WhitelabelApprovalEndpoint, but I'm not sure if this is what is required.
I was able to create a controller that is nearly identical to the WhiteLabelApprovalEndpoint, but do not know how to adapt it to accommodate being behind a proxy.
#Controller
#SessionAttributes("authorizationRequest")
public class ApprovalEndpoint {
#RequestMapping("/oauth/confirm_access")
...
private static String TEMPLATE = "<html><body><h1>OAuth Approval</h1>"
+ "<p>Do you authorize '${authorizationRequest.clientId}' to access your protected resources?</p>"
+ "<form id='confirmationForm' name='confirmationForm' action='authorize' method='post'><input name='user_oauth_approval' value='true' type='hidden'/>%csrf%%scopes%<label><input name='authorize' value='Authorize' type='submit'/></label></form>"
+ "%denial%</body></html>";
...
The only change I made to the class was to update the form action string, making the path relative by replacing
action='${path}/oauth/authorize'
with
action='authorize'
This allows the POST to go to the correct URL
http://localhost/proxy/stuff/javaPath/oauth/authorize
instead of
http://javaPath/oauth/authorize
The latter doesn't map when submitted through Apache (the frontend proxy). But it would seem that this creates other problems in the Java application, because this results in the error
error="invalid_request", error_description="Cannot approve uninitialized authorization request."
I see that this exception is thrown in the AuthorizationEndpoint when the authorizationRequest is null. This looks like it should be handled by my custom class having SessionAttributes set properly, but updating the just the path that I'm POSTing to seems to break this.
May be you already solved it but posting the answer as it may help someone.
It is because authorize end point URL (domain + path(including proxy)) should be consistent. I mean either it should be 'localhost' or your proxy path but it should be consistent.
As OAuth uses session internally and later fetches it from the same path (when the POST happens) . So if the URL changes (POST) it wont get the session then it throws Cannot approve uninitialized authorization request.
For my case ,I was using the authorize end point as:
https://mydomain/myapp/oauth/authorize?grant_type=authorization_code&client_id=clientid&redirect_uri=http://localhost:8181&response_type=code
but in the properties I was having :
server:
session:
cookie:
path: /appProxy
context-path: /myapp
port: 8081
After successful authorization when POST is done on it tries to fetch the session from /appProxy/myapp instead of /myapp and resulting in Cannot approve uninitialized authorization request.
So to solve this, I can either remove Session.cookie.path property or run Oauth server on https://mydomain/appProxy/myapp/oauth/authorize to make it consistent.

With ServiceStack Auth, is there a way to make the redirects always be HTTPS?

For website logins I am using ServiceStack's Authentication feature with the Authenticate attribute, the CredentialsAuthProvider and the UserAuth repository. It is working great, however, in production we put all IIS hosts behind a loadbalancer that serves only HTTPS by design (forwarding all requests on port 443 to port 80 on the IIS instances). This creates a problem - the Authenticate attribute redirects to the login page on HTTP port 80 only (I think because it sees only the proxied request Url, not the original). This results in a failed request in the browser because the load balancer does not do HTTP and does not redirect HTTP requests to HTTPS. We cannot configure IIS to also be HTTPS only.
Is there a way to make the redirects in the Auth provider always be HTTPS? Or, can the Auth provider look at the HTTP headers for the X-Forwarded-Proto to see that the login page request should be over HTTPS?
Here is the reply from Demis Bellot on the ServiceStack Customer Forums:
Some of the OAuth urls are specified in the configuration:
https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorization#oauth-configuration
For other autogenerated Urls you can set it to use https:
SetConfig(new HostConfig {
UseHttpsLinks = true
});
Which should modify the BaseUrl used in the latest v4.0.33 release.
You can also override AppHost. ResolveAbsoluteUrl method to
introspect/customize urls.
And my implementation of the AppHost.ResolveAbsoluteUrl override
public override string ResolveAbsoluteUrl(string virtualPath, IRequest httpReq)
{
virtualPath = virtualPath.SanitizedVirtualPath();
var absoluteUrl = httpReq.GetAbsoluteUrl(virtualPath);
return httpReq.Headers["X-Forwarded-Proto"] != null
&& httpReq.Headers["X-Forwarded-Proto"].Equals("https", StringComparison.InvariantCultureIgnoreCase)
? absoluteUrl.Replace("http://", "https://") : absoluteUrl;
}
(The loadbalancer and instances are at AWS by the way)

Access to a Client's Certificate from Inside an EJB?

Having client authentication set up on jBoss 7, I'd like to get access the client's certificate form inside an EJB, or inside a login module.
Is this possible?
Thanks
In case there is a servlet request (from JSF or ServletFilter) you can do:
ServletRequest servletRequest = (ServletRequest) facesContext.getExternalContext().getRequest();
X509Certificate[] x509Certificates = (X509Certificate[]) servletRequest.getAttribute("javax.servlet.request.X509Certificate");

Resources