I'm building a Spring Boot authorization server which needs to generate Oauth2 tokens with two different auth methods. I want to have a different endpoint for each method, but by default Spring only creates /oauth/token
, and while it can be changed, I don't think it is possible to have two different paths for it.
As an alternative, I'm trying to create two methods in a controller which do an internal forward to /oauth/token
, adding a parameter to the request so I can know where it came from.
I have something like this:
@RequestMapping(value = "/foo/oauth/token", method = RequestMethod.POST)
public ModelAndView fooOauth(ModelMap model) {
model.addAttribute("method", "foo");
return new ModelAndView("forward:/oauth/token", model);
}
This performs the forward correctly, but the auth fails with:
There is no client authentication. Try adding an appropriate authentication filter.
The same request works correctly when sent to /oauth/token
directly, so I'm guessing that the problem is that the BasicAuthenticationFilter is not running after the forward.
How can I make it work?
Looking carefully to the filter chains created for the Oauth endpoints, and for the forwarding controllers, it's easy to see that the latter are missing the BasicAuthenticationFilter, because they aren't authenticated, and auth isn't performed again after the forward.
To solve it, I created a new config like this:
@Configuration
public class ForwarderSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
@Autowired
private FooClientDetailsService fooClientDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
for (AuthorizationServerConfigurer configurerBit : configurers) configurerBit.configure(configurer);
http.apply(configurer);
http
.authorizeRequests()
.antMatchers("/foo/oauth/token").fullyAuthenticated()
.and()
.requestMatchers()
.antMatchers("/foo/oauth/token");
http.setSharedObject(ClientDetailsService.class, fooClientDetailsService);
}
}
This code mimics what Spring Oauth does behind the scenes (here), running identical filter chains with the same authentication options on both endpoints.
When the /oauth/token
endpoint finally runs, it finds the auth results that it expects, and everything works.
Finally, if you want to run a different ClientDetailsService on two forwarding endpoints, you just have to create two configuration classes like this one, and replace the ClientDetailsService on the setSharedObject
call in each of them. Note that for this, you'll have to set different @Order
values in each class.