How to Use Spring Cloud Gateway With OAuth 2.0 Patterns
Join the DZone community and get the full member experience.Join For Free
The reactive AI Gateway of the Spring Ecosystem—built on Spring Boot, WebFluz, and Project—is Spring Cloud Gateway. Spring Cloud Gateway’s job is to factor and route requests to services, as well as provide alternative concerns, like security, monitoring, and resilience. While Reactive models gain popularity, the microservices you use will most likely be a combination of Spring MVC blocking applications and Spring WebFlux non-blocking applications.
This article will show you how to use Spring Cloud Gateway for routing as well as traditional Servlet API microservices. You will also learn the necessary configurations for OpenID Configuration Authentication, Token Relay, and Client Credentials Grant, all of which are common OAuth2 patterns that use Okta as an authorization server.
Table of Contents
- Pattern 1: OpenID Connect Authentication
- Create a Eureka Discovery Service
- Create a Spring Cloud Gateway Application
- Pattern 2: Token Relay to Service
- Create a REST API Service
- Route the REST API Through Spring Cloud Gateway
- Pattern 3: Service-to-Service Client Credentials Grant
- Create a Microervice
- Secure the Micro Service using OAuth 2.0 Scopes
- Update the REST API to Call the Micro Service
- Putting it All Together
- Learn More About Building Secure Applications
Pattern 1: OpenID Connect Authentication
OpenID Connect defines a mechanism for end-user authentication based on the OAuth2 authorization code flow. In this pattern, the Authorization Server returns an Authorization Code to the application, which can then exchange it for an ID Token and an Access Token directly. The Authorization Server authenticates the application with a ClientId and ClientSecret before the exchange happens. As you can see in the diagram below, OpenID and OAuth2 patterns make extensive use of HTTP redirections, some of which have been omitted for clarity.
For testing this OAuth2 pattern, create the API Gateway with service discovery. First, create a base folder for all the projects:
Create a Eureka Discovery Service
With Spring Initializr, create an Eureka server:
Unzip the file:
As described in Spring Cloud Netflix documentation, the JAXB modules, which the Eureka server depends upon, were removed in JDK 11. If you are running Eureka server with JDK 11, edit the
pom.xml and add the following dependency:
EurekaServiceApplication to add
application.yml and add the following content:
Start the service:
http://localhost:8761 and you should see the Eureka home.
Create a Spring Cloud Gateway Application
Now let’s create an API Gateway with Spring Cloud Gateway, using Spring Initializr again.
Unzip the project:
SpringCloudGatewayApplication to add
Add a package
controller and the controller class
src/main/java/com/okta/developer/gateway/controller/GreetingController.java. The controller will allow us to test the login without having configured any routes yet.
Add a greeting template
application.yml and add the following properties:
src/main/java/com/okta/developer/gateway/OktaOAuth2WebSecurity.java to make the API Gateway a resource server with login enabled:
To keep things simple in this example, CSRF is disabled.
Now we need to create an authorization client in Okta. Log in to your Okta Developer account (or sign up if you don’t have an account).
- From the Applications page, choose Add Application.
- On the Create New Application page, select Web.
- Name your app Api Gateway, add
http://localhost:8080/login/oauth2/code/oktaas a Login redirect URI, select Refresh Token (in addition to Authorization Code), and click Done.
Copy the issuer (found under API > Authorization Servers), client ID, and client secret.
Start the gateway with:
http://localhost:8080/greeting. The gateway will redirect to Okta login page:
After the login, the idToken and accessToken will be displayed in the browser.
Pattern 2: Token Relay to Service
A Token Relay happens when an OAuth2 consumer, for example the API Gateway, acts as a Client and forwards the accessToken to the routed service.
Create a REST API Service
Let’s create a shopping cart service.
Unzip the file:
Before creating the entities for the service, add a
MonetaryAmountConverter for mapping the
MonetaryAmount type to the database. The conversion to and from BigDecimal for persistence allows using the built-in Hibernate BigDecimal to JDBC numeric mapping. Add
LineItem model classes under
src/main/java/com/okta/developer/cartservice/controller/CartNotFoundException.java for mapping the API 404 in the
CartController we will create after:
Configure the Jackson Money Datatype module. Add a
WebConfig class under
CartServiceApplication and add
application.yml and add the following values:
Route the REST API Through Spring Cloud Gateway
Go to the
api-gateway project and add a route for the cart service, edit
TokenRelayGatewayFilterFactory will find the accessToken from the registered OAuth2 client and include it in the outbound cart request.
Restart the gateway with:
http://localhost:8080/greeting and copy the accessToken. Then use the accessToken to make requests to the cart API through the gateway.
Pattern 3: Service-to-Service Client Credentials Grant
In this authorization pattern, the application requests an access token using only its own client credentials. This flow is suitable for machine-to-machine (M2M) or service-to-service authorizations.
Create a Microservice
For service-to-service authorization, create a
pricing Spring Boot service with Spring Initializr:
Unzip the file:
pom.xml and add Jackson Datatype Money dependency again.
src/main/java/com/okta/developer/pricing/model/LineItem.java model classes:
src/main/java/com/okta/developer/pricing/service/PricingService.java interface and a
src/main/java/com/okta/developer/pricing/service/DefaultPricingService implementation to calculate prices for the
PricingController to handle the pricing request. Add the class
Configure the Jackson Money Module in a
Secure the Micro Service Using OAuth 2.0 Scopes
Protect the pricing endpoint by requiring a custom scope
pricing in the accessToken. One way to do it is with
HttpSecurity configuration. Add a
src/main/java/com/okta/developer/pricing/WebSecurity.java class with the following:
application.yml and add the following:
Start the service:
Let’s try the pricing API without an accessToken:
-v verbose flag, you should see the request is rejected with 401 (Unauthorized).
Update the REST API to Call the Microservice
Now we are going to configure
cart-service to use the client credentials grant flow to request pricing.
First create a new authorization client in Okta.
- From the Applications page, choose Add Application
- On the Create New Application page, select Service
- Name your app Cart Service and click Done
Copy the new client ID, and client secret.
Create a custom scope to restrict what the
cart-service accessToken can access. From the menu bar select API -> Authorization Servers. Edit the authorization server by clicking on the edit pencil, then click Scopes -> Add Scope. Fill out the name field with
pricing and press Create.
We need to configure the OAuth2 client in the
cart-service application, for calling the
OAuth2RestTemplate is not available in Spring Security 5.3.x. According to Spring Security OAuth migration guides, the way to do this is by using RestTemplate interceptors or WebClient exchange filter functions. Since Spring 5,
RestTemplate is in maintenance mode, using WebClient (which supports sync, async, and streaming scenarios) is the suggested approach. So let’s configure a
WebClient for the pricing call.
First, add the
spring-webflux starter dependency to the
IMPORTANT: Adding both
spring-boot-starter-webflux modules results in Spring Boot auto-configuring Spring MVC, not WebFlux. This allows Spring MVC applications to use the reactive WebClient.
In the code above, we set a custom json decoder, from the
objectMapper that includes the
MoneyModule, so the monetary amounts are correctly serialized and deserialized. Also, we set
pricing-client as the default OAuth 2.0
registrationId. For service discovery, a
ReactorLoadBalancerExchangeFilterFunction must be added to the
Let’s now configure the OAuth 2.0 client registration. Edit the cart-service
application.yml and add security.oauth2 properties. The
cart-service is a resource server and an OAuth 2.0 client at the same time. The final configuration must be:
Ribbon load balancer has been disabled, otherwise the
ReactorLoadBalancer auto-configuration will fail.
Also, note the requested scope for the client_credentials grant is
pricing, the custom scope. Then, the accessTokens for this client will only have access to the
pricing-service. Adding a custom scope for the client_credentials flow is a best practice.
Spring Boot will auto-configure the application as an OAuth2 client because of the
client.registration presence in the YAML. Add a
src/main/java/com/okta/developer/cartservice/WebSecurity.java class to override the auto-configuration, and configure the application as an OAuth 2.0 resource server:
Create the class
src/main/java/com/okta/developer/cartservice/service/PricingException.java to return
HttpStatus.INTERNAL_SERVER_ERROR (HTTP status 500) when the cart cannot be priced due to an unexpected error.
Add a PricingService for the pricing implementation. Create the class
Note that the ‘WebClient’ is making a synchronous call, as we invoke
response.block() to get the pricing result. This is the expected approach for non-reactive applications.
CartController to request pricing when creating a cart:
Restart the cart-service:
Putting it All Together
Create a cart through the API Gateway again, make sure to have a valid accessToken from
You should get a priced cart as response:
Take a look at the System Log in the Okta Dashboard and you will see an entry indicating the Cart Service requested an access token:
Learn More About Building Secure Applications
In this tutorial, you learned how to create an API Gateway with Spring Cloud Gateway, and how to configure three common OAuth2 patterns (1. code flow, 2. token relay and 3. client credentials grant) using Okta Spring Boot Starter and Spring Security. . You can find all the code at GitHub.
If you like this blog post and want to see more like it, follow@oktadev on Twitter, subscribe to our YouTube channel, or follow us on LinkedIn. As always, please leave a comment below if you have any questions.
Published at DZone with permission of Jimena Garbarino, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.