Skip to main content

Overview

The User Management System uses JSON Web Tokens (JWT) for stateless authentication. When users log in successfully, they receive a JWT token that must be included in subsequent requests to access protected endpoints.

How JWT Authentication Works

The authentication flow follows these steps:
1

User Login

The user sends their credentials (email and password) to the /auth/login endpoint.
2

Credential Verification

The system validates the credentials against the stored user data using BCrypt password hashing.
3

Token Generation

If credentials are valid, a JWT token is generated containing the username and role.
4

Token Usage

The client includes this token in the Authorization header for subsequent requests.
5

Token Validation

The JwtAuthenticationFilter intercepts requests, validates the token, and extracts user information.

Token Generation Process

Tokens are generated by the JwtUtil class when a user successfully logs in. The token includes:
  • Subject: The username
  • Role Claim: The user’s role (e.g., ROLE_USER or ROLE_ADMIN)
  • Expiration: Token expiry time based on the configured duration

Code Reference

The token generation happens in JwtUtil.java:20-26:
public String generateToken(String username, String role) {
    return JWT.create()
            .withSubject(username)
            .withClaim("role", role)
            .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_DATE))
            .sign(Algorithm.HMAC256(SECRET));
}
The service layer calls this method after verifying credentials in UserServiceImpl.java:59:
String token = jwtUtil.generateToken(existingUser.getUsername(), existingUser.getRole().name());
The JWT secret key and expiration time are configured in application.properties using the properties jwt.secret and jwt.expiration.

Token Validation and Extraction

When a request arrives with a JWT token, the JwtAuthenticationFilter validates and extracts information from it.

Filter Process

The JwtAuthenticationFilter extends Spring’s OncePerRequestFilter to ensure it runs exactly once per request. Here’s what happens in JwtAuthenticationFilter.java:23-44:
  1. Extract Token: The filter checks for an Authorization header with a Bearer prefix
  2. Validate Token: Uses JwtUtil to validate the token signature and expiration
  3. Extract Claims: Retrieves username and role from the token
  4. Set Authentication: Creates a Spring Security authentication object and stores it in the SecurityContext
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                FilterChain filterChain) throws ServletException, IOException {
    String authHeader = request.getHeader("Authorization");
    String token = null;

    if ( authHeader != null && authHeader.startsWith("Bearer ") ) {
        token = authHeader.substring(7);
    }

    if ( token != null && SecurityContextHolder.getContext().getAuthentication() == null) {
        String username = jwtUtil.getUsernameFromToken(token);
        String role = jwtUtil.getRoleFromToken(token);

        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                username,
                null,
                Collections.singletonList(new SimpleGrantedAuthority(role))
        );

        SecurityContextHolder.getContext().setAuthentication(authToken);
    }
    filterChain.doFilter(request, response);
}

Token Validation Logic

The validateToken method in JwtUtil.java:28-36 verifies the token signature using HMAC256:
public DecodedJWT validateToken(String token) {
    try {
        return JWT.require(Algorithm.HMAC256(SECRET))
                .build()
                .verify(token);
    } catch (JWTVerificationException exception) {
        return null;
    }
}
If the token is invalid, expired, or has an incorrect signature, the validateToken method returns null, and the request proceeds without authentication. Protected endpoints will then deny access.

How to Include Tokens in Requests

Once you receive a token from the login endpoint, include it in the Authorization header of your HTTP requests.

Example Login Request

curl -X POST http://localhost:8080/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "password123"
  }'

Response

{
  "id": 1,
  "username": "johndoe",
  "email": "[email protected]",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Using the Token

Include the token in subsequent requests:
curl -X GET http://localhost:8080/users/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
The token must be prefixed with Bearer (note the space after “Bearer”). The filter specifically looks for this format when extracting the token from the header.

Security Configuration

The authentication filter is integrated into Spring Security’s filter chain in SecurityConfig.java:32:
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
This ensures that JWT validation happens before Spring Security’s default authentication mechanisms.

Session Management

The system is configured for stateless authentication:
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
This means:
  • No server-side sessions are created
  • Each request is authenticated independently via the JWT token
  • The server doesn’t store any session state

Password Security

User passwords are hashed using BCrypt before storage. The UserServiceImpl uses Spring Security’s PasswordEncoder for secure password handling:
// During registration (UserServiceImpl.java:38)
.password(passwordEncoder.encode(createUserDTO.getPassword()))

// During login (UserServiceImpl.java:55)
if ( !passwordEncoder.matches(password, existingUser.getPassword()) ) {
    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Email o Contraseña incorrecto");
}
BCrypt is a one-way hashing function with built-in salt, making it highly resistant to rainbow table attacks and brute-force attempts.

Public Endpoints

The following endpoints are publicly accessible without authentication:
  • POST /auth/signup - User registration
  • POST /auth/login - User login
All other endpoints require a valid JWT token.

Next Steps

Authorization

Learn about role-based access control

Roles

Understand user roles and permissions

Build docs developers (and LLMs) love