Using JWT with Spring OAuth2

1. Overview

In this tutorial, we’ll secure a REST API with OAuth2 and consume it from a simple client. The application we’re going to build out will consist of three modules:

  • Authorization Server
  • Resource Server
  • Web Application

The simple project uses the implicit grant. The diagram below represents the flow used int this case:

I assume that you are familiar with the Spring and you know how to make the basic Spring application 🙂

2. The Authorization Server

Let’s start setting up an Authorization Server as a simple Spring Boot application. In order to do that we need to add some dependencies to our pom.xml file.

2.1. Maven Configuration

We’ll set up the following set of dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
</dependency>

2.2. @EnableAuthorizationServer

Next, we will configure our authorization server to use JwtTokenStore – as follows:

@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    private final AuthenticationManager authenticationManager;

    @Autowired
    public OAuth2Config(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore())
                .tokenEnhancer(jwtTokenEnhancer())
                .authenticationManager(authenticationManager);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtTokenEnhancer());
    }

    @Bean
    protected JwtAccessTokenConverter jwtTokenEnhancer() {
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory
                (new ClassPathResource("jwt.jks"), "mySecretKey".toCharArray());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt"));
        return converter;
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("dummy-client")
                .secret("dummy-secret")
                .authorizedGrantTypes("client_credentials")
                .scopes("read", "write")
                .accessTokenValiditySeconds(60);
    }

}

Note that the current OAuth2Config is working in memory, for a realistic use-case we may need to integrate it with Hazelcast or JDBC to access persistent data.

3. The Resource Server

Once the Authentication Server is up and running, we want to create a Service that only allows access to authenticated users. So, we will create another simple Spring Boot application.

3.1 The endpoint for getting the public key

In the application.properties file, we can set the endpoint for getting the public key of the certificate used to sign the token:

security.oauth2.resource.jwt.keyUri=http://localhost:8081/spring-oauth-server/oauth/token_key

3.2 @RestController

Next, let’s implement a simple controller exposing some resource.

With spring-oauth and spring-security we can use two different approaches for access control:

  • by ROLES → .access(“hasRole(‘ROLE_RS_WRITE’)”)
  • by SCOPES → .access(“#oauth2.hasScope(‘read’)”)
@PreAuthorize("#oauth2.hasScope('read')")
@RequestMapping(value = "/", method = RequestMethod.GET)
public Map<String, String> home() {
    return Collections.singletonMap("message", message);
}

4. Web App – Implicit Grant

Let’s take a look at our web (client) application that uses the implicit grant. Our web application is a separate module that tries to access the resources server after obtaining an access token from the authorization server using implicit grant flow.

4.1 The applcation.properties

spring.aop.proxy-target-class=true

security.oauth2.resource.jwt.keyUri=http://localhost:8081/spring-oauth-server/oauth/token_key

resourceServerClient.accessTokenUri=http://localhost:8081/spring-oauth-server/oauth/token
resourceServerClient.clientId=dummy-client
resourceServerClient.clientSecret=dummy-secret

4.2 OAuthClientConfiguration

@Configuration
public class OAuthClientConfiguration {

    @Bean
    @ConfigurationProperties("resourceServerClient")
    public ClientCredentialsResourceDetails getClientCredentialsResourceDetails() {
        return new ClientCredentialsResourceDetails();
    }

    @Bean
    public OAuth2RestTemplate restTemplate() {
        AccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest();
        return new OAuth2RestTemplate(getClientCredentialsResourceDetails(), new DefaultOAuth2ClientContext(accessTokenRequest));
    }
}

4.3 @RestController

@RestController
public class MessageController {

    private final OAuth2RestTemplate resourceServerProxy;

    @Value("${resource.server.url}")
    private String resourceServer;

    @Autowired
    public MessageController(OAuth2RestTemplate resourceServerProxy) {
        this.resourceServerProxy = resourceServerProxy;
    }

    @RequestMapping(value = "/api/message", method = RequestMethod.GET)
    public Map getMessage() {
        return resourceServerProxy.getForObject(resourceServer, Map.class);
    }

    @RequestMapping(value = "/api/message", method = RequestMethod.POST)
    public void saveMessage(@RequestBody String newMessage) {
        resourceServerProxy.postForLocation(resourceServer, newMessage);
    }
}

5. Conclusion

In this quick article we focused on setting up our Spring Security OAuth2 project to use JSON Web Tokens (JWT). The full implementation of this tutorial can be found in the github project.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *