Skip to content

Sprint 1 Refinement - User Login

Overview

A React application and a Java backend using RESTful APIs, the connection from Java to a Postgres database via JPA, the Spring Security layer that authenticates users based on their authorities, and generates JWT tokens through the /login route.

mmd

Explanation:

  1. React Application: The frontend that makes HTTP requests to the RESTful API.
  2. RESTful API (Spring Boot): The backend that processes requests from React and interacts with the security layer and the database.
  3. Security Layer (Spring Security): Manages user authentication and authorization.
    • /login: Endpoint where users send their credentials to obtain a JWT token.
    • Verifies Authority: Confirms if the user has the necessary authority to access specific resources.
  4. Postgres Database: Stores the application's data.
    • JPA: Used to interact with the database through entities.
  5. JPA Entities: Representations of database tables, allowing the application to interact with data in an object-oriented way.
  6. Authentication Flow: The user authenticates through the /login endpoint, and a JWT token is generated. This token is sent with subsequent requests to access protected resources.
  7. Access Granted/Access Denied: The system determines whether the request can proceed based on the user's authorities.

1. Environment Management

mmd

Docker

To run the systems, we already have Docker containers configured in the application. To learn more about Docker, read our Docker usage guide

Docker Compose

In addition to the Docker configurations (Dockerfile), we have a complete Docker Compose setup that allows running all microservices and their respective databases with a single command. We have setups for both a development environment and a production environment.

To learn more about Docker Compose, read our Docker Compose usage guide

Commands to run the application's Docker Compose

To run a compiled version as if it were a production environment, run the command normally:

docker compose up -d

To see the progress of the running services, use the command to view console logs:

docker compose logs -f

To run a development environment using Docker, run the command below. A configuration exclusive for development has been added to this project, which shares your local folder with the containers, therefore listening to your modifications and updating the container automatically.

docker compose -f docker-compose-dev.yml -d 

Running commands inside the container

The docker exec command plays a fundamental role in interacting with running containers, allowing developers and system administrators to execute additional commands inside an already active container. This functionality is especially useful for administration, debugging, and maintenance tasks, as it allows accessing a container's terminal or running scripts and processes without needing to restart the container or create a new instance. For example, by using docker exec -it <container_id> /bin/bash, the user can open an interactive shell session inside the container, making it easy to inspect the file system, check logs, or modify configurations in real-time. This ability to interact directly with the container's environment is essential for the efficient management of containerized applications, allowing for quick problem-solving and making updates or adjustments as needed.

mmd

2. ADMIN user registration story

This story requires the creation of the user entity with a "type" field that indicates the user type. This field will help us identify if the user is of the ADMIN type, which is the user type that can register other users.

«Entity»Username: stringemail: stringpassword: stringrole: stringtype: UserType«Enumerator»UserTypeADMINPDMCOLLABORATOR

Note

This is just an illustrative model; in practice, more fields will be necessary.

Command-line application

In a Spring application, we are also using the standard Java execution model, meaning the main method receives arguments normally, and deciding whether to run the Spring application can be optional:

@SpringBootApplication
public class UsermanagementApplication {

  public static void main(String[] args) {
    if (args.length > 0) {
      switch (args[0]) {
        case "register:admin":

          break;
      }
    } else {
      SpringApplication.run(UsermanagementApplication.class, args);
    }
  }

}

CommandLineRunner

CommandLineRunner is a Spring interface that allows the execution of specific code after the application has started, being called automatically by Spring Boot when the application is launched. By implementing the CommandLineRunner interface, developers can define logic that should be executed right after the application context is loaded, which is useful for tasks like data initialization, configuration checks, or service setup. The run(String... args) method is where this logic is placed, and the arguments passed to the application can be accessed through this method, allowing for customized application behavior based on startup parameters.

mmd

In our application, we can use it to register the admin user. Although this is not the exact purpose of this interface, if we use System.exit, we can register the admin and stop the application. In this case, we can perform the registration provided we have received the necessary parameters on the command line:

@Component
public class AdminRegistrationCommandLineRunner implements CommandLineRunner {

    @Autowired
    private UserService userService;

    @Autowired
    private ApplicationContext context;

    @Override
    public void run(String... args) throws Exception {
        // Check if the desired command was invoked
        if (args.length == 0 || !args[0].equals("register:admin")) {
            // if not, it means we are running the application normally, just exit the method
            return;
        }

        try {
            String name = args[1];
            String email = args[2];
            String password = args[3];

            // illustrative method call
            userService.cadastrarUsuario(name, email, password);

            System.exit(SpringApplication.exit(context, () -> 0));

        } catch (Exception e) {
            System.err.println("❌ Error registering user: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    }
}

Password Encryption

Spring Security provides built-in support for password encryption using the BCrypt algorithm, which is a secure and widely used approach to protect user passwords in applications. BCrypt not only encrypts the password but also adds a random salt, making the application more resistant to brute-force and dictionary attacks. To use BCrypt, you can inject an instance of PasswordEncoder into your service or controller class and use the encode() method to encrypt the user's password upon registration and the matches() method to verify the password when authenticating the user.

Example of BCrypt Usage in Spring

Here is an example of how to use BCrypt to encrypt user passwords in a Spring application:

  1. Configure the PasswordEncoder:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // Defines the encoder for BCrypt
    }
}
  1. Use the PasswordEncoder in the User Service:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class PasswordManager {

    @Autowired
    private PasswordEncoder passwordEncoder;

    public String registerUser(String username, String password) {
        // Encrypts the password using BCrypt
        String encryptedPassword = passwordEncoder.encode(password);

        return encryptedPassword;
    }

    public boolean validateUser(String rawPassword, String storedEncryptedPassword) {
        // Checks if the provided password matches the stored encrypted password
        return passwordEncoder.matches(rawPassword, storedEncryptedPassword);
    }
}

Code Explanation:

  • PasswordEncoder Configuration: The SecurityConfig class defines a bean of type PasswordEncoder, configured to use BCrypt.
  • User Registration: In the registerUser method of the PasswordManager class, the user's password is encrypted using passwordEncoder.encode(password), and the encrypted password can be stored in the database.
  • Password Validation: The validateUser method uses passwordEncoder.matches(rawPassword, storedEncryptedPassword) to check if the password provided by the user matches the stored encrypted password.

3. Login and Route Protection Filter (middleware) Stories

Protecting endpoints with the security lib

alt text

Enabling the module

The projects included in the challenge already have the Lib enabled, you just need to add it to the pom.xml

  <dependencies>
    <dependency>
      <groupId>com.ciandt.nextgen25</groupId>
      <artifactId>security</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>

Enable the functionality in the code

Just include the @EnableJwtSecurity annotation in a configuration class.

@Configuration
@EnableJwtSecurity
public class SecurityConfig
{

}

JWT token generation using JwtTokenService

The JwtTokenService is used in a Spring application to manage authentication based on JWT (JSON Web Tokens), enabling a secure and scalable approach to control access to resources. When authenticating a user, the service generates a token containing relevant information, such as the user's email and their permissions, signed with a secret key. This token is then returned to the client, who uses it in subsequent requests by sending it in the authorization header. The JwtTokenService also validates the token on each protected request, ensuring that the user is authenticated and authorized to access the requested resources. This approach favors the construction of RESTful APIs, where statelessness is essential, as each request can be authenticated independently through the token.

@PostMapping("/auth/login")
public ResponseEntity<String> login(@RequestBody UserLoginRequest userLoginRequest) {
    // Authenticates the user with the provided credentials
    authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(userLoginRequest.getUsername(), userLoginRequest.getPassword())
    );

    // Generates a JWT token after successful authentication
    String token = jwtTokenService.generateToken(userLoginRequest);
    return ResponseEntity.ok(token); // Returns the token in the response
}

Reference Guide

Here are the most important documents to read for this sprint:

  1. AI Driven Development

Keywords, Resources, and Prompt Examples

  1. React

    • Reference Link: React Documentation
    • AI Prompt: "Generate an example of a functional component in React that displays a list of users."
  2. Java

    • Reference Link: Java Documentation
    • AI Prompt: "Create a simple example of a Java class with methods to register and list users."
  3. Spring Boot

    • Reference Link: Spring Boot Documentation
    • AI Prompt: "Generate an example of a Spring Boot application that configures a REST endpoint for login."
  4. JPA (Java Persistence API)

    • Reference Link: JPA Documentation
    • AI Prompt: "Create an example of a JPA entity for the User class with the fields name, email, and password."
  5. PostgreSQL

  6. Spring Security

  7. JWT (JSON Web Token)

    • Reference Link: Introduction to JWT
    • AI Prompt: "Generate an example of how to create and validate a JWT token in a Spring Boot application."
  8. Docker

    • Reference Link: Docker Documentation
    • AI Prompt: "Generate a Dockerfile for a Spring Boot application."
  9. Docker Compose

    • Reference Link: Docker Compose Documentation
    • AI Prompt: "Create a docker-compose.yml file to orchestrate a Spring Boot application with a PostgreSQL database."
  10. CommandLineRunner

    • Reference Link: Spring Boot CommandLineRunner
    • AI Prompt: "Generate an example of using CommandLineRunner to initialize data in a Spring Boot application."
  11. BCrypt

  12. Middleware (Next.js)

    • Reference Link: Middleware in Next.js
    • AI Prompt: "Generate an example of middleware in Next.js that protects a route with JWT authentication."
  13. RESTful API

    • Reference Link: RESTful API Concepts
    • AI Prompt: "Create an example of a RESTful API in Spring Boot that implements CRUD operations for users."
  14. Docker Networking

    • Reference Link: Docker Networking
    • AI Prompt: "Generate an example of a Docker network configuration for communication between containers."
  15. JPA Entities

    • Reference Link: JPA Entities
    • AI Prompt: "Create an example of a JPA entity for a User object with the appropriate annotations."
  16. Exposing Ports in Docker

    • Reference Link: Exposing Ports in Docker
    • AI Prompt: "Create an example of a Docker command to start a container, exposing host port 8080 to container port 80."