I must say I am very confused about the entire model and I need help gluing all the floating pieces together.
I am not doing Spring REST, just plain WebMVC controllers.
My mission:
I want a form login with a username + pass authentication. I want to authenticate against a 3rd party service. Upon success I want to return a cookie but NOT use the default cookie token mechanism. I want the cookie to have a JWT token instead. By leveraging the cookie mechanism every request will be sent with the JWT.
So to break it down I have the following modules to take care of:
- do authentication against a 3rd party service when doing a user + pas logi
n -
replace cookie session token with my custom implementation upon successful auth
-
upon every request parse the JWT from the cookie ( using a filter )
-
extract user details / data from the JWT to be accessible to the controllers
What's confusing? ( please correct me where I am wrong )
3rd party authentication
to authenticate against a 3rd party I will need to have a custom provider by extending AuthenticationProvider
public class JWTTokenAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate( Authentication authentication ) throws AuthenticationException {
// auth against 3rd party
// return Authentication
return new UsernamePasswordAuthenticationToken( name, password, new ArrayList<>() );
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals( UsernamePasswordAuthenticationToken.class );
}
}
Questions:
- is this provider executed upon successful authentication / login when user submits a form user + pass? if so that how is that related to AbstractAuthenticationProcessingFilter#successfulAuthentication?
- do I have to return an instance of UsernamePasswordAuthenticationToken?
- do I have to support UsernamePasswordAuthenticationToken to get user + pass here?
replace cookie token with a JWT
No idea how to do this gracefully, I can think of a number of ways but they not Spring Security ways and I don't want to break out of the flow. Would be thankful for any suggestions here!
parse the JWT with every request from a cookie
From what I understand I need to extend AbstractAuthenticationProcessingFilter like so
public class CookieAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
@Override
public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response )
throws AuthenticationException, IOException, ServletException {
String token = "";
// get token from a Cookie
// create an instance to Authentication
TokenAuthentication authentication = new TokenAuthentication(null, null);
return getAuthenticationManager().authenticate(tokenAuthentication);
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
super.doFilter(req, res, chain);
}
}
Questions:
- when is AbstractAuthenticationProcessingFilter#successfulAuthentication called? is it called with the user logs in or when the JWT token been validated successfully?
- is there any relation between this filter and the custom provider I posted previously? The manager will supposedly call the custom provider based on the token instance which is matched with what the provider supports via the support method?
It seems as if I have all the pieces I need, except the cookie session replacement, but I cannot put them into a single coherent model and I need from somebody who understand the mechanics well enough so I can glue all of this into a single module.
UPDATE 1
OK, I think I am getting where this is starting… https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilter.java
This Filter registers itself to POST -> "/login" and than creates an instance of UsernamePasswordAuthenticationToken and passes the control to the next filter.
Question is where the cookie session is set….
UPDATE 2
This section of the dos gives the top level flow that I was missing, for whoever is going through this take a look here… http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#tech-intro-authentication
This section regarding the AuthenticationProvider… http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#core-services-authentication-manager
UPDATE 3 – working case, is this the best way??
So after digging through the Spring Security docs and their sources I got the initial model to work. Now, doing this, I realized there is more than one way to do it. Any advice of why picking this way VS what Denys proposed below?
Working example below…
Best Answer
To get this to work the way described in the original post, this is what needs to happen...
Custom Filter
Authentication Provider
attach the provider to the UsernamePasswordAuthenticationToken which is generated by UsernamePasswordAuthenticationFilter, which attaches itself to "/login" POST. For formlogin with POST to "/login" will generate UsernamePasswordAuthenticationToken and your provider will be triggered
Custom Authentication object
For the JWT we want to have our own Authentication token object to carry the data that we want along the stack.
Authentication Success Handler
This is getting called when our custom provider did it's job by authenticating the user against a 3rd party and generating a JWT token, This is the place where the Cookie gets into the Response.
}
Hooking This All Together