I am currently using Spring Boot Starter and GraphQL Java Tools to use GraphQL in my Spring application. It works well together with my authorization filter, as long as i authorize the graphql endpoint. Now i would like to open certain mutations or queries to the public (thus no authorization required) and this is where i stumble. How can i open the graphql endpoint but still be able to use the @PreAuthorize
annotation of Spring security for method level authorization? In other words: Is it possible to have "optional" authorization on an endpoint?
This is my configuration:
@Override
protected void configure(HttpSecurity http) throws Exception {
log.debug("configureHttpSecurity");
// Only authorize the request if it is NOT in the permitAllEndpoints AND matches API_ROOT_URL OR
// MESSAGING_ROOT_URL
List<RequestMatcher> requestMatchers = new ArrayList<>();
requestMatchers.add(new SkipPathRequestMatcher(permitAllEndpointList, API_ROOT_URL));
requestMatchers.add(new AntPathRequestMatcher(MESSAGING_ROOT_URL));
OrRequestMatcher apiMatcher = new OrRequestMatcher(requestMatchers);
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(permitAllEndpointList.toArray(new String[0]))
.permitAll()
.and()
.authorizeRequests()
.antMatchers(API_ROOT_URL, MESSAGING_ROOT_URL)
.authenticated()
.and()
.addFilterBefore(new CustomCorsFilter(),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new AuthenticationFilter(authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new AuthorizationFilter(apiMatcher),
UsernamePasswordAuthenticationFilter.class);
}
The apiMatcher
is to open up certain REST endpoints.
This is my AuthorizationFilter
:
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException, ServletException {
try {
String authorization = httpServletRequest.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Bearer ")) {
return getAuthentication(authorization.replace("Bearer ", ""));
}
} catch (ExecutionException e) {
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN,"The provided token was either not valid or is already expired!");
return null;
} catch (IOException | InterruptedException e) {
httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,"There was a problem verifying the supplied token!");
return null;
}
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized");
return null;
}
If i don't send an error at the end of attemptAuthentication
i would be able to access REST endpoints which should not be open. Also if i just permit the GraphQL Endpoint then no authorization will happen and thus every @PreAuthorize
will fail, even if i provide a valid JWT.
It might be that my approach to this is already wrong. If this is the case then please let me know.