Search code examples
javaspringspring-bootjwtspring-saml

Create JWT access token from a SAMLAuthenticationToken


In my application I have so far used the OAuth2 password grant flow to generate a JWT access token to clients providing their username and password using Spring Security and Spring OAuth. They then use that token in all requests to my Spring Boot REST API.

Some of my customers now want to use SAML authentication instead. My idea is to create a separate endpoint /saml/accessToken and secure it with Spring SAML. Once the SAML authentication is complete the user is redirected back to /saml/accessToken, now with a valid authentication, and is given a JWT which the client can use to further communicate with my REST API.

I need a controller method that accepts an authenticated SAMLAuthenticationToken, generates a JWT using it's credentials, and returns it to the client:

@RequestMapping(value = "/saml/accessToken")
public String getAccessToken(SAMLAuthenticationToken authentication) {
    return accessTokenFactory.create(authentication);
}

It is the accessTokenFactory in the above example I need help with. I would like to follow the Spring coding ecosystem as much as possible and avoid using a "hack" solution, so that I can make use of the already existing TokenEnhancers and so forth.


What is the best way to create a JWT access token from a SAMLAuthenticationToken?


Solution

  • As it turns out, the Authentication object for the SAML cookie resulting from a successful SAML authentication flow is actually an ExpiringUsernameAuthenticationToken, not a SAMLAuthenticationToken. The ExpiringUsernameAuthenticationToken#principal is the User implementation (lets call it CustomerUser) I set during the SAML authentication in my SAMLUserDetailsService implementation, which is the same type of User that I use in the OAuth2 password grant flow.

    Since I didn't find any way of using the default Spring OAuth way of creating a JWT for the ExpiringUsernameAuthenticationToken, I ended up writing a separate JwtFactory#create(ExpiringUsernameAuthenticationToken) using jjwt. This lead to a clean and easy solution.

    The main drawback of doing it this way is that the JwtFactory cannot make use of my TokenEnhancer beans, that are responsible for adding additional parameters to the JWT. Therefore there exists som level of code and logic duplication for adding additional JWT parameters, both in the TokenEnhancers (used by Spring OAuth) and the JwtFactory (used manually after SAML authentication). This is a code smell that should be avoided. But hacking Spring OAuth specific functionality into my custom JwtFactory seems even worse, so this is something I'll have to live with.