Easy OAuth2 in Microservices: Quick Setup with Spring Cloud Gateway & Spring Security

Arun Chalise
5 min readOct 21, 2024

--

In a MicroServices architecture, key security requirements include:

  • API access for external clients
  • Service-to-Service communication
  • Role Based Access Control for fine grained access to resources

OAuth2 and OpenID Connect provides a solution for this and especially for Spring applications Spring Security makes it remarkably easy by managing these security concerns separately from the core logic.

However testing OAuth2 flows in staging environments within an enterprise settings can be both challenging and cumbersome. One of the biggest obstacles arises when Authorization and Identity providers are centrally managed, making it difficult to create independent, isolated test environments.

This is where Spring’s flexibility really stands out. Being able to spin up your own Authorization Server and Identity Provider, with full control, is incredibly empowering. It allows you to populate test data, simulate OAuth2 flows, and verify security implementations — all without having to rely on centrally managed services.

In this example, we have used Authorization Server from Spring Security as our own OAuth2 Authorization Server and Identiy Provider to set up OAuth2 security for our microservices. The code for the demo is available at

High Level Architecture

We have two microservices in the example:

  1. User Service: External clients should be able to call the User Service to fetch user details and retrieve the associated account information. To achieve this, clients must be authenticated and authorized using OAuth2.
  2. Account Service: The Account Service should not be accessible from external clients. It should be restricted to internal service-to-service communication only. However, we still want to ensure that even internal services like the User Service can only access the Account Service if they present a valid access token. This ensures that all requests, even between internal services, are securely authenticated.

Implementing API Gateway with Spring Cloud Gateway

To handle authentication and route requests between services securely, we can implement the API Gateway pattern using Spring Cloud Gateway. The gateway will serve as an OAuth2 Resource Server as well as an OAuth2 client. Here’s how it works:

  • For unauthenticated incoming requests, the gateway will redirect users to an Identity Provider’s login page to complete the authorization flow.
  • After authorization, the gateway will forward the request to the underlying service (e.g., User Service) with a valid access token.

The key component here is the use of TokenRelayFilter, which will relay the access token when proxying requests to the User Service.

Key dependencies for the Gateway:

implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.cloud:spring-cloud-starter-gateway")
implementation("org.springframework.security:spring-security-oauth2-jose")

Registering the Gateway as an OAuth2 Client

To configure the gateway as an OAuth2 client, we define the following properties in application.yml for the gateway app:

spring.security.oauth2:
client:
registration:
user-accounts-app-oidc:
provider: spring
client-id: user-accounts-app
client-secret: secret
authorization-grant-type: authorization_code
redirect-uri: "http://127.0.0.1:8080/login/oauth2/code/{registrationId}"
scope: openid, profile, accounts.read
client-name: user-accounts-app-oidc
provider:
spring:
issuer-uri: http://localhost:9000
resourceserver:
jwt:
jwk-set-uri: http://localhost:9000/oauth2/jwks

Configuring the Authorization Server

In the Authorization Server, we need to onboard the gateway application as an OAuth2 client with the following configuration:

oauth2:
authorizationserver:
client:
user-accounts-app:
registration:
client-id: "user-accounts-app"
client-secret: "{noop}secret"
client-authentication-methods:
- "client_secret_basic"
authorization-grant-types:
- "authorization_code"
- "refresh_token"
- "client_credentials"
redirect-uris:
- "http://127.0.0.1:8080/login/oauth2/code/user-accounts-app-oidc"
- "http://127.0.0.1:8080/authorized"
post-logout-redirect-uris:
- "http://127.0.0.1:8080/logged-out"
scopes:
- "openid"
- "profile"
- "accounts.read"
- "accounts.write"
require-authorization-consent: true

Defining Access to the User Service in the Gateway

To enable the gateway to forward requests to the User Service with the relayed access token, we have configured the following:

spring:
cloud:
gateway:
default-filters:
- TokenRelay=
routes:
- id: user-service
uri: http://localhost:8084
predicates:
- Path=/user/**

Securing Resource Servers

Gateway, User Service and Account Service all of them act as Resource Servers and must define secured endpoints and validate access tokens before allowing access to them.

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange(auth -> auth.anyExchange().authenticated())
.oauth2Login(withDefaults())
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
http.csrf(ServerHttpSecurity.CsrfSpec::disable);
return http.build();
}

We can configure these reource servers to validate access tokens issued by the Authorization Server by providing the JWK Set URI of the Authorization Srever.

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9000/oauth2/jwks

Service-to-Service Communication

Since the User Service also needs to call the Account Service, it must include the access token in its requests. This can be achieved using the ServerBearerExchangeFilterFunction in the WebClient used to make the api calls to Account Service.

@Bean
WebClient webClient() {
return WebClient.builder()
.filter(new ServerBearerExchangeFilterFunction())
.baseUrl("http://localhost:8083")
.build();
}

Testing

  1. Start authorisation server
$ cd authserver
./gradlew bootRun

2. Start the Gateway

$ cd gateway
./gradlew bootRun

3. Start User Service

$ cd userservice
./gradlew bootRun

4. Start Account Service

$ cd accountservice
./gradlew bootRun

5. Navigate to http://localhost:8080 to get prompted to login

6. Give consent

7. Navigate to retrieve user details

8. Navigate to retrieve user details with accounts

Conclusion

Using Spring Security and Spring Cloud Gateway simplifies securing microservices with OAuth2. By spinning up your own OAuth2 Authorization Server and Identity Provider, we gain full control over authentication and authorization flows, making testing and verification easier. This setup ensures secure API access for external clients and service-to-service communication, while the API Gateway handles routing and token management efficiently. This approach provides a flexible and scalable solution for securing microservices.

--

--

Responses (1)