Separation of Authentication responsibility
This document explains the security architecture of the Adventuretube platform, detailing how requests are authenticated and authorized across the API Gateway and individual microservices and why need a seperation concept in Gatway-service with dedicated Auth-service.
A key design principle in this system is the separation of authentication responsibilities:
The API Gateway acts as a global interceptor, validating whether requests are secure and forwarding them to the correct microservice if valid.
A dedicated auth-service handles actual authentication, credential validation, and JWT token issuance.
Unlike traditional architectures that embed Spring Security directly into the gateway, Adventuretube try “Lightweight Gateway Design with Reactive JWT filtering “due to a core architectural mismatch:
Spring Cloud Gateway is built on Reactive WebFlux (asynchronous, non-blocking).
Spring Security is traditionally built on Servlet-based architecture (blocking, synchronous).
To address this, authentication logic is centralized in a dedicated servlet-based auth-service, while the gateway only performs stateless JWT validation.
Gateway-Service : Lightweight Gateway Authentication Flow (Stateless)
In the AuthenticationFilter
(used in GatewayConfig):
Check if the incoming request targets a secured endpoint via
RouterValidator.isSecured
.If secured, extract the JWT token from the
Authorization
header.Validate the token (signature, claims, expiration).
If valid, forward the request to the respective microservice with the original path (minus the
/xxx-service
prefix).
Note: The gateway does not perform user authentication—it only validates token authenticity and structure.
if (validator.isSecured.test(request)) {
// Check Authorization header
// Extract and validate token
// If valid, continue filter chain
}
Auth-Service : Building a Custom JDBC AuthenticationProvider and Security Configuration in Spring Security for Microservices with JWT Tokens
Job To Do here
The auth-service
is responsible for:
Authenticating users with credentials (email/password)
Issuing JWT access and refresh tokens
Validating and parsing JWT tokens
Providing secure access to
/auth/**
endpoints
Security in this module is implemented using Spring Security with a fully servlet-based architecture. JWT tokens are handled via a custom filter, and user identity is authenticated against a member service.
📊 Key Components in auth-service
Overview
When the auth-service initializes, the first step of custom security configuration is handled by AuthServiceConfig. This setup is based on two distinct concepts:
1. Spring Security with JDBC handles the authentication process for each incoming request, whether the user is authenticated or not. It leverages a CustomUserDetailService
, configured inside CustomAuthenticationProvider
, to interact with the database and manage the login process through a traditional relational database system.
2. Spring Security Filter with JWT intercepts each request to perform additional tasks before authentication occurs. Specifically, it uses a custom JwtAuthFilter
to handle JWT token extraction, validation, and setup of the security context for authenticated requests.
1. AuthServiceConfig (Spring Security Configuration)
Defines
SecurityFilterChain
for/auth/**
endpoints.Public endpoints:
/auth/token
,/auth/users
,/auth/refreshToken
,/auth/logout
, and Swagger documentation.Protected endpoints: Require
ADMIN
role.Registers:
CustomAuthenticationProvider
JwtAuthFilter
(beforeUsernamePasswordAuthenticationFilter
)AuthenticationManager
2. CustomAuthenticationProvider
Extends
DaoAuthenticationProvider
.Customizes credential validation.
Constructor injects:
CustomUserDetailService
PasswordEncoder
Overrides
authenticate()
method to manually verify user credentials.Important: The
customUserDetailService
andpasswordEncoder
are passed explicitly to the superclass and not stored directly in subclass fields.
3. CustomUserDetailService
Implements
UserDetailsService
.Calls MEMBER-SERVICE using
RestTemplate
to retrieve user information.Converts the remote
MemberDTO
intoUserDetails
object with username, password, and roles.
4. JwtAuthFilter
Extends
OncePerRequestFilter
.Extracts JWT token from
Authorization
header.Validates token using
JwtUtil
.Sets authenticated
UsernamePasswordAuthenticationToken
inSecurityContextHolder
.Skips filtering for public endpoints (defined in
SecurityConstants
).Important: Even public endpoints still pass through
JwtAuthFilter
, but the filter detects them early and immediately forwards the request without token validation.
5. JwtUtil
Utility class for:
Generating access and refresh JWT tokens.
Parsing and validating JWT claims.
Extracting username and roles from tokens.
🔥 Differences Between JwtAuthFilter and .permitAll()
They are both needed, but they serve different layers of security responsibility:
Layer | What It Does | Example |
JwtAuthFilter (Filter Layer) | Decide whether to extract and validate a JWT token | “Should I even bother checking the Authorization header?” |
.authorizeHttpRequests().permitAll() | Decide whether a request must be authenticated | “Even if no token, am I allowed to reach the endpoint?” |
📈 Request Handling Sequence
Request hits Spring Security filter chain.
JwtAuthFilter runs first:
Checks if the request path is an OPEN_ENDPOINT.
If yes, skips JWT validation.
authorizeHttpRequests checks access rules:
Sees
.permitAll()
for the same path.Allows the request to continue even without authentication.
Controller handles the request.
🧠 In simpler words:
JwtAuthFilter = “Should I validate your JWT token?” (low-level token inspection)
.permitAll() = “Even without any authentication, should you be allowed to access this endpoint?” (high-level access control)
💡 Why SecurityFilterChain (Filter Layer) comes first before Authorization Layer?
Spring Security always processes filters first before reaching the authorization rules:
Filters operate at the HTTP request level, modifying or inspecting the request (e.g., setting SecurityContext).
Authorization rules (like
.permitAll()
) decide, after filters, whether a givenSecurityContext
allows access.
Thus, JwtAuthFilter
must complete first, so that when Spring checks .authorizeHttpRequests()
, it knows whether an authenticated user exists—or if the request should just be allowed without one.
🔄 Authentication and Authorization Flow
Login (Issue Token)
AuthController.issueToken()
accepts login credentials.AuthService.issueToken()
validates Google ID token.Calls
AuthenticationManager.authenticate()
usingUsernamePasswordAuthenticationToken
(email and googleId).CustomAuthenticationProvider
loads user viaCustomUserDetailService
and verifies password.Upon success, generates JWT access and refresh tokens.
User Registration
AuthController.registerUser()
handles user sign-up.AuthService.createUser()
verifies Google ID token, checks for email duplication.Saves user via MEMBER-SERVICE.
Issues JWT tokens after successful registration.
Access Secured Resources
JwtAuthFilter
intercepts requests.If Authorization header has a valid JWT,
SecurityContext
is set.Role-based access enforced via
SecurityFilterChain
.
Logout and Refresh Token
AuthController.revokeRefreshToken()
invalidates stored refresh tokens.AuthController.refreshAccessToken()
issues a new access and refresh token pair.
Summary
The authentication workflow in your auth-service
is structured around standard Spring Security components, extended with:
A custom
UserDetailsService
fetching users fromMEMBER-SERVICE
A
PasswordEncoder
for credential verificationOptional
AuthenticationProvider
for custom credential handlingA
JwtUtil
for secure token issuance and claim validation
This setup ensures a clean, extensible, and secure architecture for handling user authentication and issuing JWT tokens.