Adventuretube Microservice Security Architecture

 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):

  1. Check if the incoming request targets a secured endpoint via RouterValidator.isSecured.

  2. If secured, extract the JWT token from the Authorization header.

  3. Validate the token (signature, claims, expiration).

  4. 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 (before UsernamePasswordAuthenticationFilter)

    • AuthenticationManager

2. CustomAuthenticationProvider

  • Extends DaoAuthenticationProvider.

  • Customizes credential validation.

  • Constructor injects:

    • CustomUserDetailService

    • PasswordEncoder

  • Overrides authenticate() method to manually verify user credentials.

  • Important: The customUserDetailService and passwordEncoder 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 into UserDetails object with username, password, and roles.

4. JwtAuthFilter

  • Extends OncePerRequestFilter.

  • Extracts JWT token from Authorization header.

  • Validates token using JwtUtil.

  • Sets authenticated UsernamePasswordAuthenticationToken in SecurityContextHolder.

  • 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:

LayerWhat It DoesExample
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

  1. Request hits Spring Security filter chain.

  2. JwtAuthFilter runs first:

    • Checks if the request path is an OPEN_ENDPOINT.

    • If yes, skips JWT validation.

  3. authorizeHttpRequests checks access rules:

    • Sees .permitAll() for the same path.

    • Allows the request to continue even without authentication.

  4. 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 given SecurityContext 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

  1. Login (Issue Token)

    • AuthController.issueToken() accepts login credentials.

    • AuthService.issueToken() validates Google ID token.

    • Calls AuthenticationManager.authenticate() using UsernamePasswordAuthenticationToken (email and googleId).

    • CustomAuthenticationProvider loads user via CustomUserDetailService and verifies password.

    • Upon success, generates JWT access and refresh tokens.

  2. 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.

  3. Access Secured Resources

    • JwtAuthFilter intercepts requests.

    • If Authorization header has a valid JWT, SecurityContext is set.

    • Role-based access enforced via SecurityFilterChain.

  4. 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 from MEMBER-SERVICE

  • A PasswordEncoder for credential verification

  • Optional AuthenticationProvider for custom credential handling

  • A 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.

Leave a Comment

Your email address will not be published. Required fields are marked *