DE
- Industries
- Finance
Nearshore software development for finance—secure, scalable, and compliant solutions for banking, payments, and APIs.
- Retail
Retail software development services—e-commerce, POS, logistics, and AI-driven personalization from nearshore engineering teams.
- Manufacturing
Nearshore manufacturing software development—ERP systems, IoT platforms, and automation tools to optimize industrial operations.
- Finance
- What we do
- Services
- Technologies
- Collaboration models
Explore collaboration models customized to your specific needs: Complete nearshoring teams, Local heroes from partners with the nearshoring team, or Mixed tech teams with partners.
- Way of work
Through close collaboration with your business, we create customized solutions aligned with your specific requirements, resulting in sustainable outcomes.
- About Us
- Who we are
We are a full-service nearshoring provider for digital software products, uniquely positioned as a high-quality partner with native-speaking local experts, perfectly aligned with your business needs.
- Meet our team
ProductDock’s experienced team proficient in modern technologies and tools, boasts 15 years of successful projects, collaborating with prominent companies.
- Our locations
We are ProductDock, a full-service nearshoring provider for digital software products, headquartered in Berlin, with engineering hubs in Lisbon, Novi Sad, Banja Luka, and Doboj.
- Why nearshoring
Elevate your business efficiently with our premium full-service software development services that blend nearshore and local expertise to support you throughout your digital product journey.
- Who we are
- Our work
- Career
- Life at ProductDock
We’re all about fostering teamwork, creativity, and empowerment within our team of over 120 incredibly talented experts in modern technologies.
- Open positions
Do you enjoy working on exciting projects and feel rewarded when those efforts are successful? If so, we’d like you to join our team.
- Candidate info guide
How we choose our crew members? We think of you as a member of our crew. We are happy to share our process with you!
- Life at ProductDock
- Newsroom
- News
Stay engaged with our most recent updates and releases, ensuring you are always up-to-date with the latest developments in the dynamic world of ProductDock.
- Events
Expand your expertise through networking with like-minded individuals and engaging in knowledge-sharing sessions at our upcoming events.
- News
- Blog
- Get in touch

18. Sep 2025 •7 minutes read
Securing your web apps: Spring security OAuth 2.0 + BFF pattern
Nemanja Vasić
Software Engineer
A complete implementation guide
Modern web applications face constant security challenges, especially when handling user authentication and tokens. In this guide, we’ll walk through a step-by-step implementation of OAuth 2.0 with the Backend for Frontend (BFF) pattern using:
- Spring security OAuth 2.0 authorization server
- Spring Cloud gateway as BFF
- Spring OAuth 2.0 resource server and
- React as a frontend client.
In order to understand parts of this blog post it helps to be familiar with basic OAuth 2.0 concepts and flows. The following blog posts may be useful:
Authorization Code Flow for Confidential Clients with BFF
In this blog post I will give you an example of how to implement OAuth 2.0 backend including BFF (Backend For Frontend) for browser-based applications using Spring Security OAuth 2.0 authorization server, Spring Cloud Gateway for BFF, Sprint OAuth 2.0 Resource Server and React as a frontend client.
Why BFF is recommended for browser-based applications
The Backend for Frontend (BFF) in simple terms is a server-side application that is designed to support different front-end clients. One of the benefits of BFF and its architecture is that it makes it easier to secure client applications. In the context of this blog post this pattern is strongly recommended for browser-based applications because all token management and sensitive credential handling are handled on the secure server-side environment. Instead of storing vulnerable access and refresh tokens in the browser where they’re exposed to Cross-Site Scripting (XSS) attacks, the BFF securely maintains these tokens server-side. The browser only receives a secure, HTTP-only session cookie for BFF communication, significantly reducing the application’s attack surface and aligning with OAuth 2.0 security best practices.
More on BFF architecture in context of OAuth 2.0
Implementation of OAuth 2.0 backend with BFF pattern
The complete code can be found in this GitHub repository. If you want to try it out just follow the steps in README.md.
Architecture overview
The architecture of the application consists of the following components:
auth-server
The auth-server component serves as an Authorization Server implemented with Spring Boot and the OAuth 2.0 authorization server library. It connects to MariaDB for storing OAuth 2.0 objects, including registered clients, consents, and authorizations.
If you run the app, you can access MariaDB on
http://localhost:8080/?server=oauth_db&username=oauth&db=oauth_db
All configurations are implemented via Spring DSL (Domain-Specific Language) in Java.The most important configuration is in AuthorizationServerConfiguration.java.
secure-resource
The secure-resource component serves as a Resource Server implemented with Spring Boot and the OAuth 2.0 resource server library. It manages and protects APIs by ensuring that access is granted only to authenticated and authorized clients. For demo purposes, it has one controller for getting /resource that just returns text with a currently authenticated user. Learn more about building secure API integrations for your digital ecosystem.
The gateway component serves as the Backend for Frontend (BFF) layer implemented using Spring Boot and Spring Cloud Gateway library. It acts as a secure gateway between the frontend client and backend services, as well as a confidential client on Authorization Server (auth-server). It handles token management and authentication while providing a secure session-based communication channel through HTTP-only cookies.This component is crucial for enhancing security by keeping sensitive OAuth tokens server-side instead of exposing them to the browser. Session data is persisted in Redis DB for reliable state management.
If you run the app, you can access Redis UI on
http://localhost:8082
fe-client
The fe-client is a Single Page Application (SPA) built using React that serves as the frontend interface for the system. It communicates exclusively with the gateway (BFF) component through HTTP requests, using HTTP-only cookies for session management and authentication. This client-side application focuses purely on the user interface and business logic, while delegating all security-sensitive operations, such as token management, to the BFF layer.
Sequence diagram

1. User initiates request through the frontend application
2. Gateway checks Redis for a valid session
3. If no valid session exists:
3.1 The user is redirected to the login page hosted on Authorization Server
3.2 After successful login, the consent screen is shown if the user did not previously give his consent
3.3 The authorization code is exchanged for access and refresh tokens
3.4 A session is established with tokens stored in Redis
4. With a valid session:
4.1 The request flow goes through gateway
4.2 The gateway retrieves tokens from Redis
4.3 Authenticated requests including access token are sent to resource server
4.4 Responses are returned to the frontend
Implementation parts
Let’s now go through the important parts of the code that are executed and reflect the flow in the sequence diagram above.
We start with the fe-client (React) code.
const axiosInstance = axios.create({
baseURL: backendBaseUrl,
withCredentials: true,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
});
Here we have a configuration for Axios. The baseUrl sets a BFF backend URL.withCredentials: true is important because it ensures that session cookies are sent with each request to the backend.
xsrfCookieName: ‘XSRF-TOKEN’ and xsrfHeaderName: ‘X-XSRF-TOKEN’ are the settings that are telling Axios to look for the CSRF token in the cookie named XSRF-TOKEN and to include that token in the HTTP header named X-XSRF-TOKEN. This ensures that requests are coming from the legitimate frontend.
Next, when client code executes in the user’s browser, the getUserInfo function is called.
const getUserInfo = async (): Promise<void> => {
try {
const response = await axiosInstance.get('/userinfo');
if (response.data) {
setIsAuthenticated(true);
setUserName(response?.data?.sub);
}
} catch (error) {
console.error('Error getting user info', error);
}
};
This will either return 401 Unauthorized if the user doesn’t have an active session, or it will return a response with the user data. If there is no active session for the user, the Login button will be present on the UI. When clicking on the Login button, following function is called.
const login = () => {
window.location.href = backendBaseUrl;
};
It sets the browser’s location to the BFF backend URL.This redirects the browser to the BFF gateway.
Why is this important, and why not use an XHR request to call the BFF backend?
The OAuth 2.0 Authorization Code flow, which will be initiated when BFF base URL is called, inherently relies on browser redirects to securely initiate and complete the authentication process. Redirects allow the browser to handle cross-origin navigation, display the identity provider’s login UI (in this case, the login hosted by the auth-server), and properly manage secure, HttpOnly cookies.
In contrast, XHR requests can’t follow redirects to external login pages, often run into CORS issues, and aren’t suitable for handling interactive authentication flows.
Next, let’s examine the gateway. This service is implemented with a reactive, non-blocking approach using Web Flux.
We have Route Configuration, which includes the customRouteLocator method where routes are defined. This method uses the TokenRelayGatewayFilterFactory instance to configure filters for defined routes. The tokenRelay is responsible for taking the cookie from the client request and matching it with the session, and taking an access_token from the session and adding it as a Bearer token in request to downstream services.
Another important part is .removeRequestHeader(“Cookie”) which will prevent session cookies from being forwarded to the downstream services. This ensures that BFF session cookies stay within the gateway.
In the Security Configuration, the main security rules are configured using a reactive approach. The main difference from the standard blocking approach in Spring Security here is the use of ServerHttpSecurity instead of HttpSecurity.
The securityFilterChain defines the following:
- CSRF Protection setup: Uses cookies for handling CSRF tokens. An important part here is setting withHttpOnlyFalse() for CSRF Token Repository, in order to make the CSRF token readable by JavaScript. This will allow React client applications to read the token and include it in the requests to the backend.
- CSRF Cookie Web Filter: Ensures CSRF token is included in the response.
- Authentication and Authorization setup: Requires all requests to be authenticated, and configures OAuth 2.0 login to be able to handle Authorization Code Flow with auth-server. It also configures the OAuth 2.0 client to be able to handle client credentials and token management.
- Logout Handler: Handles the OIDC logout flow and integrates with the auth-server logout flow, ensuring that the user session is also removed on the auth-server side. It also sets the post logout redirect URL which is the URL of fe-client React application.
Next, let’s look at the auth-server.
The authorizationServerSecurityFilterChain is the base security configuration for OAuth 2.0 authorization server endpoints. It configures essential services such as:
- authorizationService – Handles OAuth 2.0 authorizations.
- authorizationConsentService – Handles OAuth 2.0 user consents.
- authorizationClientRepository – Handles OAuth 2.0 client registrations.
- OIDC – Enables OpenID Connect support with default standard-compliant OIDC settings.
This gives us the /userInfo endpoint which is used by the gateway to get user information. Security configuration also includes rules that require all requests to OAuth 2.0 endpoints to be authenticated. It also configures how unauthenticated requests are handled; in this case, HTML requests are redirected to a custom login page.
Next, let’s examine the last service – secure-resource.
The securityFilterChain is the security configuration for the Resource Server. It sets up two authorization rules:
- The /resource endpoint requires the authority (OAuth2 scope) SCOPE_resource.read
- All other requests must be authenticated (as a fallback rule). The .oauth2ResourceServer configures this service as an OAuth 2.0 resource server, and defines the default JWT configuration. This configuration will create a secure endpoint /resource that will accept only valid JWT tokens and requires the SCOPE_resource.read scope to access protected resources. An important part in this service is jwk-set-uri which tells the Resource Server where to find the public keys for validating JWT tokens. This is a standard OAuth 2.0 endpoint that returns a JSON Web Key Set (JWKS) containing the public keys used to validate tokens. This endpoint is exposed by the Authorization Server.
Conclusion
In this guide, we’ve explored the implementation of a secure OAuth 2.0 backend utilizing the BFF pattern for browser-based applications.
This architecture demonstrates several key benefits:
Enhanced Security: By implementing the BFF pattern, we’ve moved sensitive token handling to the server side, significantly reducing the risk of token exposure through XSS attacks.
Clean Separation of Concerns: Each component (Authorization Server, Resource Server, Gateway/BFF, and Frontend) has well-defined responsibilities, making the system more maintainable and scalable.
Modern Technology Stack: The implementation leverages current best practices using Spring Security OAuth 2.0 authorization server, Spring Cloud Gateway, Spring OAuth 2.0 Resource Server, and React.
Production-Ready Features: The solution includes essential components like Redis for session management, CSRF protection, and proper token management.
The provided implementation serves as a practical reference for building secure, modern web applications following OAuth 2.0 security best practices. The complete source code is available in the GitHub repository. Feel free to fork or clone it to start building your own secure applications.
Remember, hands-on experience is the best way to learn.
Tags:Skip tags

Nemanja Vasić
Software EngineerNemanja is a seasoned Software Developer with over five years of professional experience in the information technology industry. His current tech stack comprises ReactJs, NextJs, Javascript, NodeJs, Java, Spring Boot, and MongoDB, among others.
He holds a degree from the Faculty of Technical Sciences, University of Novi Sad, and has a proven track record of delivering high-quality software solutions.