OAuth2 protecting Spring Boot Microservices with Swagger

Following on from my last posts on documenting a Spring Boot micro service, and setting up a Spring Boot OAuth2 server, this post focuses on putting OAuth2 protection on a micro service, and allowing Swagger to use OAuth2.

Adding OAuth2 protection

Spring Boot makes adding the OAuth2 protection fairly straightforward, assuming you:

  • Know the basics of how OAuth2 works
  • Have your OAuth2 authorisation server URLs handy
  • Have an OAuth2 client set up so your micro service can validate tokens
OAuth2 dependencies

First we need to add the oauth2 dependency (I was using spring-boot-starter-parent 1.3.3.RELEASE with this):

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.0.9.RELEASE</version>
</dependency>
Application properties

Then you’ll need to add some properties which cover the application client and secret, and how to validate requests (as per the Spring Boot security documentation).

security.oauth2.client.clientId=my-client
security.oauth2.client.clientSecret=my-secret
security.oauth2.resource.tokenInfoUri=http://localhost:8080/uaa/oauth/check_token
security.oauth2.resource.preferTokenInfo=true
swagger.oauth.url=http://localhost:8080/uaa/oauth/authorize

In the example above I have my client details, I have opted to use token validation, and I have provided the check_token endpoint so my micro service can validate tokens passed to it.

Finally, I have added a Swagger related property for OAuth2 authorisation, which I’ll mention later.

Resource Server Configuration

Then I need to set up a configuration class which will then control which endpoints are protected and public.

@Configuration
@EnableResourceServer
public class OAuthResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId("my-resource");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.anonymous().and()
                .authorizeRequests()
                .antMatchers("/", "/lib/*", "/images/*", "/css/*", "/swagger-ui.js","/swagger-ui.min.js", "/api-docs", "/fonts/*", "/api-docs/*", "/api-docs/default/*", "/o2c.html","index.html","/webjars/**","/hystrix/**").permitAll()
                .antMatchers(HttpMethod.GET, "/my").access("#oauth2.hasScope('my-resource.read')")
                .anyRequest().authenticated();
    }

}

In the above example I have protected /my with OAuth2 protection, and made it require the my-resource.read scope. I have also made the various Swagger components public so that they are accessible without authorisation.

Swagger authorisation

Then I need to update the Swagger Configuration to tell it to use OAuth2 when using the micro service.

@Configuration
@EnableSwagger
@EnableAutoConfiguration
public class SwaggerConfig {

    @Value("${swagger.oauth.url}")
    private String swaggerOAuthUrl;
    
    private SpringSwaggerConfig springSwaggerConfig;
 
    @Autowired
    public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
        this.springSwaggerConfig = springSwaggerConfig;
    }
    
    @Bean
    public SwaggerSpringMvcPlugin customImplementation() {
        return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
                //Root level documentation
                .apiInfo(new ApiInfo(
                        "My Service JSON API",
                        "This service provides a JSON API",
                        null,
                        null,
                        null,
                        null
                ))
                .useDefaultResponseMessages(false)
                //Map the specific URL patterns into Swagger
                .includePatterns("/my")
                .authorizationTypes(getAuthorizationTypes())
                .ignoredParameterTypes(OAuth2Authentication.class, Principal.class);
    }

    private List<AuthorizationType> getAuthorizationTypes()
    {
        List<AuthorizationType> authorizationTypes = new ArrayList<>();
        List<AuthorizationScope> scopes = new ArrayList<>();
        scopes.add(new AuthorizationScope("my-resource.read","Read access on the API"));

        List<GrantType> grantTypes = new ArrayList<>();
        ImplicitGrant implicitGrant = new ImplicitGrant(new LoginEndpoint(swaggerOAuthUrl),"access_code");
        grantTypes.add(implicitGrant);

        AuthorizationType oauth = new OAuthBuilder()
                .scopes(scopes)
                .grantTypes(grantTypes)
                .build();
        authorizationTypes.add(oauth);
        return authorizationTypes;
    }
    
}

In the above example I have mapped in /my as per normal, but also used .authorizationTypes to add details on an OAuth2 authorisation scope. I use the swagger.oauth.url I added to the application properties earlier to tell Swagger where the Authorisation endpoint is.

The end result

The end result is if I try and use the micro service without authorisation I am given a 401 unauthorised response:

Screen Shot 2016-05-10 at 08.46.48

And if I click the On/Off slider next to the endpoint, I am then given the option to select a scope and Authorise using the OAuth2 server.

Screen Shot 2016-05-10 at 08.47.30

Once I’m appropriately authenticated and authorised, I can try out the service as per normal.