Overriding AjaxAwareAuthenticationSuccessHandler.onAuthenticationSuccess Gotchas - grails

I wrote a custom success handler for our application because the client was to redirect to specific pages based on roles. In doing this, I was originally calling super.onAuthenticationSuccess but since it already does redirects, when I attempted to redirect again, obviously, there were state exceptions.
So what I'm doing now is checking roles and redirecting accordingly and falling back on super.onAuthenticationSuccess...
#Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response,
final Authentication authentication) throws ServletException, IOException {
if (goToSomePage) {
response.sendRedirect(request.contextPath + '/admin/index')
}else{
super.onAuthenticationSuccess(request, response, authentication)
}
}
I'm wondering if I'm going to run into issues later because something in the parent onAuthenticationSuccess method is required that I'm not doing.

Related

Spring authorization server authenticate for each client

I'm trying to build an Identity Provider using Spring authorization-server that third party applications are going to use for FIM (federated identity management).
We want each OAuth client to require authentication (if a user tries to login with a different client they would need to authenticate for each client).
Out of the box the flow looks like this:
So there's 2 issues.
The /oauth2/authorize endpoint just checks whether or not the sessions principal is authenticated, it doesn't care or know which client the principal was meant for.
There's just a single /login endpoint, so during authentication it doesn't know which client is used.
My best bet here is that I should:
Make the oauth2/authorize endpoint redirection to /login include the query parameter client_id
Create a custom AuthenticationFilter that also adds the client_id to the User principal
Override the authorizationRequestConverter for the oauth2/authorize endpoint and validate that the client in the request is the same as the client stored on the authenticated principal
Am I missing anything or do anyone know of a simpler way of doing this?
Based on your last comment, it seems one possibility is to simply require authentication every time, or at least every time an authorization is requested. In that case, you could clear out the authentication after the authorization code is issued to the client, using a Filter. This doesn't seem ideal and will result in a poor user experience, but may achieve your requirement.
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
#Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
// ...
// Add filter to remove the SecurityContext after successful authorization
http.addFilterAfter(new RemoveSecurityContextOnAuthorizationFilter(), LogoutFilter.class);
return http.build();
}
private static final class RemoveSecurityContextOnAuthorizationFilter extends OncePerRequestFilter {
private SecurityContextHolderStrategy securityContextHolderStrategy =
SecurityContextHolder.getContextHolderStrategy();
private final LogoutHandler logoutHandler = new CompositeLogoutHandler(
new CookieClearingLogoutHandler("JSESSIONID"),
new SecurityContextLogoutHandler()
);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} finally {
String locationHeader = response.getHeader(HttpHeaders.LOCATION);
if (locationHeader != null) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build();
if (uriComponents.getQueryParams().containsKey("code")) {
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
this.logoutHandler.logout(request, response, authentication);
}
}
}
}
}
// ...
}

How to append the same parameter for multiple identical login pages if authentication fails?

I have the same login form on different endpoints: /welcome,
/login
If I set failureUrl(/welcome?error) it will redirect the /login to /welcome?error as well if authentication fails instead of /login?error
I think I need to implement a failureHandler, but how can I extract the actual login endpoint from HttpServletRequest?
I would like to something like that:
response.sendRedirect(/[actual_login_endpoint]?error);
I solved it. I had to send a hidden attribute with the login forms. I don't think there is an easier solution.
#Override
public void onAuthenticationFailure(
final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException exception) throws IOException, ServletException {
if (request.getParameter("loginForm").equals("welcome")) {
response.sendRedirect("/welcome?error");
} else if (request.getParameter("loginForm").equals("login")) {
response.sendRedirect("/login?error");
}
}

Preflight request fails on Chrome, Safary, Opera

I´m struggling with CORS requests on an Springsecurity backen project + Angulajs frontend.
CORS requests are working fine on IE (also with curl, wget and python requests) but miserably fail on Chrome and Safary because of Preflight bad request.
I know that those Browsers are blocking CORS POSTs, making the request empty as soon as the reach the backend, in fact I don’t see any data when I log out the request from backend. I tried every possible combination of:
Frontend side:
1) $http(method: POST)
2) $http.post(
3) Adding flags: Access-Control-Allow-Origin, Access-Control-Expose, etc.
4) Adding all possible header combination: ‘Content–Type’:’application/
Browser side:
1) Start chrome with flag: --disable-web-security
2) Installing Chrome extension CORS
Backend side:
1) Spring Security Disable csfr
2) Spring Security Permit all
3) Spring Security HttpMethod.OPTION
4) Set up a CORS Filter that accept all origins: “*”
5) Activated CORS framework for spring extending WebMvcConfigurerAdapter class.
Nothing, NHOTING worked for me!
I discussed this issue in another post: CORS POST request fails on Chrome, Safari and Firefox
I´m still unable to perform CORS requests, this is now I major issue and I suspect the problem is in LoginFilter:
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
private TokenAuthenticationService tokenAuthenticationService;
public JWTLoginFilter(String url, AuthenticationManager
authenticationManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authenticationManager);
tokenAuthenticationService = new TokenAuthenticationService();
}
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, ServletException {
try {
ServletInputStream inputStream = httpServletRequest.getInputStream();
httpServletRequest.getCharacterEncoding();
AccountCredentials credentials = null;
ObjectMapper mapper = new ObjectMapper();
credentials = mapper.readValue(inputStream, AccountCredentials.class);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
return getAuthenticationManager().authenticate(token);
} catch (JsonMappingException e) {
e.printStackTrace();
throw e;
} catch (JsonParseException e) {
e.printStackTrace();
throw e;
} catch (AuthenticationException e) {
e.printStackTrace();
throw e;
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication)
throws IOException, ServletException {
AccountCredentials cred = (AccountCredentials) authentication.getPrincipal();
tokenAuthenticationService.addAuthentication(response, cred);
}
}
EDIT
the exact error on Google Chrome is:
:8000/#!/login:1 XMLHttpRequest cannot load http://localhost:8080/myApp/login. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 403.
So I found that it does not has ANYTHING to do with the request headers, but the problems are the response headers.
To make the preflight passing through, all response headers have to be mapped, as example:
response.addHeader("Access-Control-Expose-Headers", "xsrf-token, Authorization, Barer, Token");
The preflight request is sent AUTOMATICALLY with verb option by browser itself BEFORE the real request is sent.
You must configure your server to send response with some headers when this preflight request is sent. With spring security you can use :
#Provider
#Component
public class CrossDomainContainerResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext,
ContainerResponseContext containerResponseContext) throws IOException {
containerResponseContext.getHeaders().add("Access-Control-Allow-Origin", "YOUR FRONTEND URI");
containerResponseContext.getHeaders().add("Access-Control-Allow-Headers",
"Access-Control-Allow-Origin");
containerResponseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
containerResponseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
If you are using XML config, you can also use the <cors /> tag.
The --disable-web-security never worked for me on Chrome. But it worked on Vivaldi Browser.

log access denied events with Spring Security and J2EE container authentication

I've got spring security configured as
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = false)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.jee()
.mappableRoles("ROLE1", "ROLE2");
}
}
And then #Secured annotations with roles on the rest endpoints.
Doesn't matter what I do I don't seem to be able to create a custom handler for authorization (i.e. a user logged in successfully but doesn't have the right role to access a particular endpoint) error events.
What I tried was:
An exception handler with #ExceptionHandler(value = AccessDeniedException.class) - doesn't get called. I understand that's by design, ok.
AuthenticationEntryPoint configured as
http.exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint())
#Component( "restAuthenticationEntryPoint" )
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence( HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException ) throws IOException {
// logging
}
}
-doesn't get called
ApplicationListener - I can see it's getting called on context closed, so it's registered correctly but not called on authorization error.
All I need is a simple handler to log unsuccessful authorization events.
It completely slipped my mind that the allowed roles are listed in web.xml as well for j2ee container authentication to work. So any user without a least one of those roles was just being rejected by the container.
Otherwise the first, simplest, method works fine. Hopefully my mistake will help someone in the future

What is the method for gracefully handling session timeout?

The problem: I am on some page of my application and go away for a while. Coming back and clicking on a link I get a "Unable to restore viewID" message. Same on hitting refresh.
I can start a new session, but I have to manually edit the URL as follows:
Active address window:
http://localhost:8080/myapp/index.xhtml?windowId=e9d
into
http://localhost:8080/myapp/index.xhtml
Then a new session is established, and the user has to log in again which is what I want.
In researching how to deal with this, I see a lot of "solutions" that basically keep the session alive by using client-side Javascript to send requests periodically to keep the session alive. Personally I do not consider this a desirable solution.
What I want is when the session times out, all subsequent requests to any non-public page needs to be directed to index.xhtml. References to pages that don't require login should go through with a new session object. Preferably this would be handled using only JSF 2 defined facilities, but I don't mind writing a Servlet filter if that is what it takes.
Can anyone provide a link to a how-to that I missed?
Do it in a Filter, yes. You could use HttpServletRequest#getRequestedSessionId() to check if the client has sent a session cookie and HttpServletRequest#isRequestedSessionIdValid() to check if it is still valid (i.e. the session hasn't been expired in the server side):
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletRequest res = (HttpServletResponse) response;
if (req.getRequestedSessionId() != null && !req.isRequestedSessionIdValid()) {
res.sendRedirect(req.getContextPath() + "/index.xhtml");
} else {
chain.doFilter(request, response);
}
}
But, that brings up another question, how exactly are you filtering logged-in users? If the session is expired, then the user is not logged-in anymore, right? You could instead also just check in the filter if the user is logged-in or not.

Resources