Spring boot doen't retrieve form data from SAML auth - java

Well, I'm consuming SAML SSO created by enterprise where I work. Once user and password is typed SAML service redirects to given url as POST request, e.g.
https://some-saml-url.domine.com/idp/startSSO.ping
then fill up user and password fields and redirects to my app authentication url as POST with form data encrypted as SAMLResponse.
I'm using spring boot 2.4.5. Here my controller
#Controller
#CrossOrigin("*")
public class AuthenticationController {
#PostMapping(value = "/auth", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public String authentication(HttpServletRequest request, HttpServletResponse response) throws GasStationException {
//do something to retrieve SAMLResponse
return "redirect:/";
}
}
I'm able to catch request inside controller but not to get form data. I've tried multiple solutions I've found through internet but nothing. Hope somebody have an idea or solution that helps me.

Ok, if somebody have this kind of issue and is using logbook, well that's why. In logback file I have this one
<logger name="org.zalando.logbook" level="TRACE" />
And don't know why logbook takes form data values print them in console and delete them from request. After comment this line all works.

Related

Spring Security 5 - How to SSO from id_token (Google one-tap)

I'm working on an application with Spring Boot 5 and OIDC. I've configured OIDC with Google and it works fine. I'm redirected to login at Google and then it redirects me to the app creating a new session. Now I'm trying to use Google One-Tap. It works fine as well. When user clicks on the one-tap's modal to continue with his Google identity I receive a POST with the id_token. What I want to do is to create a Spring session from this POST as it is created when user is logged by OIDC.
I think the right way is to create a Filter extending AbstractAuthenticationProcessingFilter. I've found some references here and here but I don't have the access_token when receiving the one-tap POST:
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
...
OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
authenticationResult.getPrincipal(),
authenticationResult.getAuthorities(),
authenticationResult.getClientRegistration().getRegistrationId());
oauth2Authentication.setDetails(authenticationDetails);
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
authenticationResult.getClientRegistration(),
oauth2Authentication.getName(),
authenticationResult.getAccessToken(),
authenticationResult.getRefreshToken());
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);
return oauth2Authentication;
}
Has anyone tried to do it before ?
I've finally found a solution. I've shared the code here. Hope it helps to anyone dealing with the same issue.

How to return Jersey MVC Viewable after authentication redirect?

TLDR: My method requires 2 redirects/forwards to work (1 for authentication and 1 to serve the jsp page). How can I resolve both redirects/forwards (or make it a non-requirement) so as to not run into the error, java.lang.IllegalStateException: Cannot forward after response has been committed.
For more context:
I have a java servlet with a method that looks something like the following:
#GET
#Path("/test")
#Authenticate
public Viewable test(#Context HttpServletRequest request, #Context HttpServletResponse response) {
Map<String, Object> model = createModel();
return new Viewable("/somePath/jspFile", model);
}
The #Authenticate annotation intercepts the call to do some Open ID Connect type authentication which results in the user being forwarded to a different server for all authentication needs. If the user is authenticated, they are redirected back to my application.
However, when hitting the url for this method, I am getting java.lang.IllegalStateException: Cannot forward after response has been committed. I don't know too much about using this Viewable class, but based on the fact that I don't run into that error when returning String/void/whatever else, I assume returning a new Viewable needs to do some forwarding that results in the user seeing the jsp page.
I've read the main SO post about this error, but I am unsure how to apply the fixes to my current problem. For example, I don't know how I would apply something like the following fix:
protected void doPost() {
if (someCondition) {
sendRedirect();
} else {
forward();
}
}
The fix assumes that I can I can either redirect OR forward, but my current method needs a redirect for authentication AND a forward/redirect to serve the jsp page. Maybe there's an obvious fix I'm missing that doesn't require a complete rehaul of the current code?
Edit: It would be nice if I could check if the user was authenticated first, but I assume using this annotation at all automatically entails an initial redirect
Edit: It looks like the user is redirected for the initial login authentication, but does not need to be redirected again after being authenticated once due to SSO
Ok based on some preliminary testing, it seems like the following solution has worked for me:
Check if the user has already been authenticated
Return a Response rather than a Viewable.
Since the user only needs to be redirected the first time for authentication, I can return an empty/meaningless response as a placeholder. And then once the user has been authenticated and is returned to my app, I can return a Viewable wrapped in a Response object.
So the code would look something like the following:
#GET
#Path("/test")
#Authenticate
public Response test(#Context HttpServletRequest request, #Context HttpServletResponse
response) {
Map<String, Object> model = createModel();
if (userIsAuthenticated()) {
return Response.status(401).build();
} else {
return Response.ok(new Viewable("/somePath/jspFile", model)).build();
}
}

Cloud Endpoints: Access Paramters in Servlet Filter

I'm trying to build an api with Google Cloud Endpoints.
As Cloud Endpoints does not provide authentication beside Googles own OAuth I try to build my own. Therefore I want to access the parameters provided for the API (for example #Named("token") token) inside a servlet filter.
Unfortunately I cannot find any of the provided information inside the httpRequest. Is that normal? Is there a possibility to access the parameters?
I would appreciate if someone could help me!
UPDATE:
With the infos from jirungaray I tried to build an authentication using headers but ran into the same problem. Used a REST-Client to send some headers as I could not figure out how to do this with the API Explorer. Inside my filter I try to access the token from the headers:
#Override
public void doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader(Constants.AUTH_TOKEN);
...
chain.doFilter(request, response);
}
The reason why I try to do something like this, is that I'm using Guice for Dependency Injection and want my token to be injected inside another object.
With Guice I have the following Provider using the token to inject a FacebookClient (using the token) per request.
#Provides
public FacebookClient getFacebookClientProvider(#Named("fbToken") Provider<String> fbToken) {
return new DefaultFacebookClient(fbToken.get(), Version.VERSION_2_2);
}
As described in the Guice wiki (SevletModule) this uses a sevlet filter to get the information from the request.
Is there any solution to achieve this kind of DI with Cloud Endpoints?
Philip,
Yes, it does makes sense you are getting an empty request. Your endpoint calls are first handled by Google (they receive the API calls) and then those are processed and sent to a handler in your app. As this is all done in the background it's very easy to miss that your endpoints aren't actually getting the same request you sent, they get a completely different request sent from Google's infrastructure.
Even though your approach should work including tokens info in url makes them easier to sniff, even if you use SSL or encrypt your params the token is there in plain sight.
For what you are trying to achieve I recommend you include the token as a header in your request and retrieve that header by accessing the HTTPRequest directly on the endpoint, this is injected automatically if you include an HTTPServletRequest param in you endpoint method.
eg.
public APIResponse doSomething(SomeComplexRquestModel request,
HttpServletRequest rawRequest) {
}
If you still feel you should go with your original approach just comment and I'll help you debug the issue.

Redirect to the original URL after signin using Spring Social, Spring security?

Spring social and security experts,
I have the following use case:
User requests a specific URL like http://www.foobar.com/foo/1001
Anonymous user can see this page but cannot post a comment in this page. Comment posting part is secured by Spring security.
User clicks login. It pops up a login window or redirect it to login page.
The user chooses to sign in with Facebook ID.
After user signed in using FB, it redirects the user to http://www.foobar.com/foo/1001 with authorization.
I was wondering how I can do that using Spring Social. Thanks a lot!!
Have you actually tried it to see if it works? Spring security does this automatically.
If you are an anonymous user and attempt to access a resource that requires a certain permission, spring security will store the attempted URL and redirect you to the login page. After successful login it fetches the attempted URL back and redirects you there.
I have only managed to do this by copy/pasting code which spring security uses to do this job.
CAVEAT: I am using ProviderSignInController to provision accounts and sign in.
ProviderSignInController permits the use of a SignInAdapter to manually login local user account based on the SocialConnection
In the signIn method:
public String signIn(String userId, Connection<?> connection, NativeWebRequest request)
I have the following code:
HttpServletRequest req = (HttpServletRequest)request.getNativeRequest();
HttpServletResponse resp = (HttpServletResponse)request.getNativeResponse();
RequestCache rc = new HttpSessionRequestCache();
SavedRequest savedRequest = rc.getRequest(req, resp);
String targetUrl = savedRequest.getRedirectUrl();
if(targetUrl != null){
log.info("Redirecting to DefaultSavedRequest Url: " + targetUrl);
new DefaultRedirectStrategy().sendRedirect(req, resp, targetUrl);
hasSentRedirect = true;
}
I had to dig through the code of SavedRequestAwareAuthenticationSuccessHandler to see how this works as part of spring security.
I am disappointed that ProviderSignInController isn't really linked to Spring Security at all. And so we must hack our way around to get the same functionality.
I have also found that login events aren't posted either.
I have asked about this recently: Should I use both SocialAuthenticationFilter and ProviderSignInController together

Spring security really strange behaviour in IE

I'm having the weirdest problem I have ever seen before.
The application I am working on uses spring security 3.1.3 to provide authentication support. There is a custom login form for which I have implemented a custom authenticationmanager / successhandler and failurehandler.
For some reason on internet explorer I always get the error message "Please fill in all mandatory fields". This is caused by appending /login?error=1 to the end of my url which can only be accessed through the following code (the redirectAndAddError method):
public class TideUserNamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public TideUserNamePasswordAuthenticationFilter() {
super();
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter(SPRING_SECURITY_FORM_USERNAME_KEY);
String password = request.getParameter(SPRING_SECURITY_FORM_PASSWORD_KEY);
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
redirectAndAddError(response);
return null;
}
return super.attemptAuthentication(request, response);
}
private void redirectAndAddError(HttpServletResponse response) {
try {
response.sendRedirect("/tide/login?error=1");
} catch (IOException e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
}
So what I tried was using Fiddler2, a web debugging proxy to view if one of the two parameters are actually empty. The strange thing is that when this program is running the error does not occur anymore and I can log on successfully.
Had anyone had a similar problem before? I think it's not related to my code as running the tool suddenly "solves" the problem.
This problem only occurs in internet explorer which makes it even more strange.
Edit
I have used another tool to watch the requests and this is what happens in IE:
First a POST request is sent to the uri /authenticate, I have set this myself like this:
<beans:property name="filterProcessesUrl" value="/authenticate"/>
The response of that request has http status code 302, moved temporarily and returns that the new location is at /login?error=1 (my form with the mandatory fields required error).
After that a GET request occurs to /login?error=1 with status code 401: Unauthorized. The intercept-url is set up like this:
<intercept-url pattern="/login**" access="permitAll"/>
The next request is a GET request to /login?error=1 again, this time the status code is showing: ERROR_INTERNET_CONNECTION_RESET, which looks like it could be a problem.
In Google Chrome the following request is made:
POST to /authenticate, result is a 302: moved temporarily to the dashboard page (which I display after logging on)
Someone on my team finally figured out what the problem was after finding this issue in the chromium bugtracker:
https://code.google.com/p/chromium/issues/detail?id=62687
The problem has been resolved by adding this in our login controller
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String doLogin() throws ServletException, IOException {
return "forward:/authenticate";
}
and changing the url that the form posts to to this one instead of the authentication url that spring security provides (we are redirecting to it manually now)

Categories

Resources