DEV Community

Bubu Tripathy
Bubu Tripathy

Posted on

Securing Spring Microservice with OAuth 2.0

This tutorial is about setting up an OAuth 2.0 Authorization Server and securing microservices with token-based authentication and scope-based authorization using Spring Boot.


Step 1: Setting up the OAuth 2.0 Authorization Server

An OAuth 2.0 Authorization Server centralizes security, issuing access tokens for client applications to authenticate and authorize requests to microservices.

1.1 Adding Dependencies

To begin, include the spring-security-oauth2 and spring-boot-starter-security dependencies in your pom.xml. These provide essential libraries for configuring OAuth 2.0 capabilities and securing your Spring Boot application.

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

1.2 Configuring Application Properties

Next, configure the server to run on port 8081 by adding a few settings in the application.properties file. Enabling the OAuth 2.0 Authorization Server is done via spring.security.oauth2.authorizationserver.enabled=true.

server.port=8081
spring.security.oauth2.authorizationserver.enabled=true
Enter fullscreen mode Exit fullscreen mode

This ensures the application starts on the specified port and sets up the authorization server.

1.3 Implementing the Authorization Server

Create a Java class annotated with @EnableAuthorizationServer to designate it as an authorization server. Extend AuthorizationServerConfigurerAdapter to configure clients and supported grant types. In this example, the client details are stored in memory using inMemory(). The client is identified by "client-id", secured with "client-secret", and supports the "password" and "refresh_token" grant types. It is further scoped to "read" and "write", defining the level of access the client has to resources.

import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;

@EnableAuthorizationServer
@Configuration
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("client-id")
            .secret("{noop}client-secret")
            .authorizedGrantTypes("password", "refresh_token")
            .scopes("read", "write");
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Securing a Microservice as a Resource Server

A Resource Server is a microservice that validates access tokens and enforces access control.

2.1 Adding Dependencies

Include the spring-security-oauth2 dependency in the pom.xml of your microservice:

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

2.2 Configuring the Resource Server

To secure your microservice, annotate a configuration class with @EnableResourceServer. Extend ResourceServerConfigurerAdapter and override the configure(HttpSecurity http) method. Here, we configure the microservice to permit all requests to /public/** but require authentication for other endpoints.

import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;

@EnableResourceServer
@Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .anyRequest().authenticated();
    }
}
Enter fullscreen mode Exit fullscreen mode

2.3 Validating Access Tokens

The resource server must validate the access tokens issued by the authorization server. Specify the token validation endpoint in the application.properties file:

security.oauth2.resource.token-info-uri=http://localhost:8081/oauth/check_token
Enter fullscreen mode Exit fullscreen mode

This points the resource server to the authorization server’s /check_token endpoint for token verification.


Step 3: Enforcing Scope-Based Authorization

OAuth 2.0 allows fine-grained access control by using scopes. Scopes represent the level of access permitted to a resource.

Configuring Scope-Based Access

Spring Security annotations like @PreAuthorize can enforce scope-based access control at the method level. For example, the following OrderController class uses @PreAuthorize to allow access to getOrders only if the token contains the "read" scope. Similarly, the createOrder method requires the "write" scope.

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/orders")
public class OrderController {

    @PreAuthorize("hasAuthority('SCOPE_read')")
    @GetMapping
    public List<Order> getOrders() {
        // Logic to fetch orders
        return List.of(new Order(1, "Sample Order"));
    }

    @PreAuthorize("hasAuthority('SCOPE_write')")
    @PostMapping
    public Order createOrder(@RequestBody Order order) {
        // Logic to create order
        return order;
    }
}

class Order {
    private int id;
    private String name;

    // Constructor, getters, and setters
    public Order(int id, String name) {
        this.id = id;
        this.name = name;
    }
}
Enter fullscreen mode Exit fullscreen mode

Ensure that tokens include the required scopes in their payload, as shown below:

{
  "scope": ["read", "write"],
  "exp": 1700000000,
  "client_id": "client-id"
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Testing the Setup

4.1 Obtaining an Access Token

A client can obtain an access token by sending a POST request to the authorization server’s /oauth/token endpoint. Use the following curl command:

curl -X POST \
  -d "grant_type=password&username=user&password=pass" \
  -u "client-id:client-secret" \
  http://localhost:8081/oauth/token
Enter fullscreen mode Exit fullscreen mode

The response includes the access_token and other token details:

{
  "access_token": "abc123",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "xyz789"
}
Enter fullscreen mode Exit fullscreen mode

4.2 Accessing a Secured Endpoint

To access a secured microservice endpoint, include the access token in the Authorization header:

curl -H "Authorization: Bearer abc123" http://localhost:8080/orders
Enter fullscreen mode Exit fullscreen mode

Conclusion

This tutorial demonstrates how to set up an OAuth 2.0 Authorization Server, secure microservices as resource servers, and enforce scope-based access control. By leveraging token-based authentication, you can centralize security management, improve scalability, and achieve fine-grained access control in a microservices architecture.

Top comments (0)