Skip to content

Conversation

@OtavioXimarelli
Copy link
Owner

@OtavioXimarelli OtavioXimarelli commented Jun 10, 2025

Summary by CodeRabbit

  • New Features
    • Introduced user authentication with JWT-based login and registration endpoints.
    • Added secure password handling and role-based access control.
    • Environment variable support added for configuration via .env files.
  • Bug Fixes
    • Improved error handling for user authentication and repository lookups.
  • Documentation
    • Updated README to document authentication, security features, environment configuration, and new endpoints.
    • Clarified project status and next steps.
  • Chores
    • Updated dependencies and reorganized project configuration for improved security and maintainability.

OtavioXimarelli and others added 30 commits June 1, 2025 23:32
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 10, 2025

Walkthrough

This update introduces user authentication and security features using Spring Security and JWT. It adds new DTOs, a controller for authentication, a security filter, and a token service. The configuration is updated to support environment variables via dotenv, and the application properties and documentation are revised accordingly. Some legacy configuration files are removed.

Changes

File(s) Change Summary
README.MD Updated documentation to reflect new authentication/security features, JWT, dotenv usage, and revised endpoints.
pom.xml Reformatted, reordered, and added dependencies for security, dotenv, JWT; downgraded Flyway version.
.../AiFoodAppApplication.java Minor formatting change in annotation spacing.
.../config/CorsConfig.java
.../config/SecurityConfig.java
Removed legacy CORS and security configuration classes.
.../config/DotenvEnvironmentPostProcessor.java
resources/META-INF/spring.factories
Added dotenv-based environment post-processor and registered it with Spring Boot.
.../controller/AuthenticationController.java Added REST controller for login and registration endpoints.
.../dto/AuthenticationDTO.java
.../dto/LoginResponseDTO.java
.../dto/RegisterDTO.java
Added DTOs for authentication, login response, and registration.
.../model/User.java Added constructor for login/password/role; reformatted authority getter.
.../repository/UserRepository.java Changed findByLogin to return UserDetails directly (from Optional).
.../security/SecurityConfig.java New security configuration: stateless, JWT-based, custom filter, password encoder, authentication manager.
.../security/SecurityFilter.java New filter for extracting and validating JWT tokens from requests.
.../security/TokenService.java New service for generating and validating JWT tokens.
.../service/AuthorizationService.java Updated to use null-check instead of Optional for user lookup.
resources/application.properties Updated to use environment variables, added JWT secret property, enabled security TRACE logging.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AuthenticationController
    participant AuthenticationManager
    participant UserRepository
    participant TokenService

    Client->>AuthenticationController: POST /api/auth/login (login, password)
    AuthenticationController->>AuthenticationManager: authenticate(login, password)
    AuthenticationManager-->>AuthenticationController: Authentication result
    AuthenticationController->>TokenService: generateToken(user)
    TokenService-->>AuthenticationController: JWT token
    AuthenticationController-->>Client: LoginResponseDTO (token, login, role)

    Client->>AuthenticationController: POST /api/auth/register (login, password, role)
    AuthenticationController->>UserRepository: findByLogin(login)
    UserRepository-->>AuthenticationController: UserDetails/null
    AuthenticationController->>UserRepository: save(new User)
    AuthenticationController-->>Client: OK/Conflict
Loading
sequenceDiagram
    participant Client
    participant SecurityFilter
    participant TokenService
    participant UserRepository

    Client->>SecurityFilter: Request with Authorization: Bearer <token>
    SecurityFilter->>TokenService: validateToken(token)
    TokenService-->>SecurityFilter: username
    SecurityFilter->>UserRepository: findByLogin(username)
    UserRepository-->>SecurityFilter: UserDetails
    SecurityFilter-->>Client: Continue filter chain (request proceeds)
Loading

Poem

Hopping through code with security in mind,
Now JWT tokens are easy to find!
With dotenv and Spring, our secrets stay neat,
Users can register, login, and eat.
A safer burrow for all to see—
This rabbit’s proud of new security! 🥕🔒

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@OtavioXimarelli OtavioXimarelli changed the title Spring security implementation @coderabbitai Jun 10, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🔭 Outside diff range comments (1)
src/main/java/com/otavio/aifoodapp/model/User.java (1)

43-60: ⚠️ Potential issue

User does not compile – missing 4 mandatory UserDetails methods

User implements UserDetails but overrides only getUsername, getPassword, and getAuthorities.
The following abstract methods are still missing and compilation will fail:

boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();

Add trivial true implementations or proper flags:

+    @Override public boolean isAccountNonExpired() { return true; }
+    @Override public boolean isAccountNonLocked()  { return true; }
+    @Override public boolean isCredentialsNonExpired() { return true; }
+    @Override public boolean isEnabled() { return true; }
🧹 Nitpick comments (22)
src/main/java/com/otavio/aifoodapp/dto/RegisterDTO.java (2)

5-14: Consider replacing the verbose POJO with a Java record.

The class is an immutable data carrier and only overrides toString(). A Java record would auto-generate the constructor, accessors, equals, hashCode, and toString (which you can still customise to omit the password) while keeping the code concise and error-free.

-package com.otavio.aifoodapp.dto;
-
-import com.otavio.aifoodapp.enums.UserRoles;
-
-public class RegisterDTO {
-    private final String login;
-    private final String password;
-    private final UserRoles role;
-
-    public RegisterDTO(String login, String password, UserRoles role) {
-        this.login = login;
-        this.password = password;
-        this.role = role;
-    }
-    ...
-}
+package com.otavio.aifoodapp.dto;
+
+import com.otavio.aifoodapp.enums.UserRoles;
+
+public record RegisterDTO(String login, String password, UserRoles role) { }

21-25: toString() still exposes the identifier – ensure it never lands in logs.

Although the password is omitted, the login (which is often an e-mail) is still PII. Make sure log levels or exception traces do not inadvertently print this DTO.

src/main/resources/META-INF/spring.factories (1)

1-2: Line-continuation backslash is unnecessary and occasionally mis-parsed.

Spring Boot treats every line as a property entry; the backslash may be interpreted literally on some class-path scanners. Safer to keep it on a single line:

-org.springframework.boot.env.EnvironmentPostProcessor=\
-com.otavio.aifoodapp.config.DotenvEnvironmentPostProcessor
+org.springframework.boot.env.EnvironmentPostProcessor=com.otavio.aifoodapp.config.DotenvEnvironmentPostProcessor
src/main/java/com/otavio/aifoodapp/dto/AuthenticationDTO.java (1)

3-4: Optionally annotate fields to enforce validation.

Adding Jakarta Bean Validation annotations (@NotBlank, @Size) guards against empty credentials before reaching the service layer.

public record AuthenticationDTO(
        @NotBlank String login,
        @NotBlank String password) {}
src/main/java/com/otavio/aifoodapp/model/User.java (1)

53-57: Possible NPE when role is null

getAuthorities() dereferences this.role without a null-check.
Either validate role in constructors/setters or handle null here to prevent NullPointerException.

src/main/java/com/otavio/aifoodapp/service/AuthorizationService.java (1)

21-27: loadUserByUsername can be simplified and made null-safe

If UserRepository returns Optional<User> (see previous comment) the code becomes:

-UserDetails user = userRepository.findByLogin(username);
-if (user == null) {
-    throw new UsernameNotFoundException("User not found with username: " + username);
-}
-return user;
+return userRepository.findByLogin(username)
+        .orElseThrow(() ->
+            new UsernameNotFoundException("User not found: " + username));

Cleaner, null-safe, and avoids an extra local variable.

src/main/java/com/otavio/aifoodapp/config/DotenvEnvironmentPostProcessor.java (1)

15-22: Avoid adding an empty property source

When .env is absent or contains only comments, an empty MapPropertySource is still added, slightly polluting the environment. Guard against this:

-Map<String, Object> envMap = new HashMap<>();
-dotenv.entries().forEach(entry -> envMap.put(entry.getKey(), entry.getValue()));
-environment.getPropertySources().addFirst(new MapPropertySource("dotenv", envMap));
+Map<String, Object> envMap = new HashMap<>();
+dotenv.entries().forEach(entry -> envMap.put(entry.getKey(), entry.getValue()));
+if (!envMap.isEmpty()) {
+    environment.getPropertySources().addFirst(new MapPropertySource("dotenv", envMap));
+}
pom.xml (2)

97-111: reactor-core is redundant

reactor-core is already transitively provided by spring-boot-starter-webflux. Keeping the explicit dependency makes the POM noisier and risks version drift if Boot upgrades.

-                <dependency>
-                    <groupId>io.projectreactor</groupId>
-                    <artifactId>reactor-core</artifactId>
-                </dependency>

26-28: tomcat.version property is unused

No plugin or dependency refers to ${tomcat.version}. Remove the property or wire it into <spring-boot-maven-plugin> only if you plan to override Boot’s managed Tomcat version.

src/main/java/com/otavio/aifoodapp/security/TokenService.java (1)

47-49: Hard-coding ZoneOffset.of("-03:00") will break on servers running in other time-zones

Prefer ZoneOffset.UTC (with shorter TTL if required) or inject the offset via configuration.

src/main/java/com/otavio/aifoodapp/controller/AuthenticationController.java (3)

30-35: Inject PasswordEncoder, not concrete BCryptPasswordEncoder

Relying on the interface keeps the controller agnostic and eases algorithm swaps.

-    private final BCryptPasswordEncoder passwordEncoder;
-    public AuthenticationController(..., BCryptPasswordEncoder passwordEncoder) {
+    private final PasswordEncoder passwordEncoder;
+    public AuthenticationController(..., PasswordEncoder passwordEncoder) {

41-47: Expose principal info instead of echoing request payload

data.login() may differ from the authenticated principal (e.g., case-insensitive login). Use auth.getName() and return authorities as a collection, not a raw String.


49-58: Consider 201 Created and location header for successful registration

Returning 200 OK provides no clue where the new resource lives.

-        return ResponseEntity.ok().build();
+        return ResponseEntity.created(URI.create("/api/users/" + newUser.getId())).build();
README.MD (9)

19-30: Consider adding hyperlinks para bibliotecas externas
Poderíamos enriquecer a seção de tecnologias incluindo links diretos para o repositório ou documentação de Maritaca AI e dotenv-java, facilitando a referência para novos contribuidores.

🧰 Tools
🪛 LanguageTool

[grammar] ~19-~19: Possível erro de concordância de número.
Context: ...adas - Spring Boot: Framework para aplicações Java - Spring Data JPA: Persistência de ...

(GENERAL_NUMBER_AGREEMENT_ERRORS)


34-41: Incluir DotenvEnvironmentPostProcessor na visão arquitetural
A arquitetura já lista Config, mas vale explicitar a presença do processador de variáveis de ambiente introduzido (DotenvEnvironmentPostProcessor) na documentação para cobertura completa.


43-48: Especificar padrões de exclusão de endpoint no Spring Security
No momento mencionamos /api/auth/login e /api/auth/register, mas é recomendável usar /api/auth/** para cobrir possíveis futuros endpoints de autenticação.


51-67: Padronizar placeholders e convenções no exemplo de .env
Sugiro usar nomes de variáveis em uppercase sem espaços (por exemplo MARITACA_API_MODEL=MODEL_NAME) e indicar que aspas só são necessárias quando há espaços.


98-101: Adicionar exemplos de requisição e resposta para autenticação
Incluir payloads de exemplo e códigos de status HTTP esperados ajudará contribuidores a testar e integrar com mais facilidade.


112-113: Uniformizar nomeação de endpoint de receitas
Avalie renomear /api/recipes/gen para /api/recipes/generate para manter consistência sem abreviações.


118-127: Incluir instruções para Windows e permissão de execução
Para facilitar em ambientes Windows, mencione mvnw.cmd; e indique chmod +x mvnw em Unix antes de ./mvnw spring-boot:run.


133-142: Especificar uso de env_file no Docker Compose
Reforce que o .env deve ser referenciado no docker-compose.yml (via env_file) para garantir carregamento correto das variáveis.


165-171: Adicionar link para CONTRIBUTING e CÓDIGO DE CONDUTA
Para orientar novos contribuintes, considere referenciar um CONTRIBUTING.md e um CODE_OF_CONDUCT.md no repositório.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1db51c2 and 259fcb2.

📒 Files selected for processing (18)
  • README.MD (1 hunks)
  • pom.xml (1 hunks)
  • src/main/java/com/otavio/aifoodapp/AiFoodAppApplication.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/config/CorsConfig.java (0 hunks)
  • src/main/java/com/otavio/aifoodapp/config/DotenvEnvironmentPostProcessor.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/config/SecurityConfig.java (0 hunks)
  • src/main/java/com/otavio/aifoodapp/controller/AuthenticationController.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/dto/AuthenticationDTO.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/dto/LoginResponseDTO.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/dto/RegisterDTO.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/model/User.java (2 hunks)
  • src/main/java/com/otavio/aifoodapp/repository/UserRepository.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/security/SecurityConfig.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/security/SecurityFilter.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/security/TokenService.java (1 hunks)
  • src/main/java/com/otavio/aifoodapp/service/AuthorizationService.java (1 hunks)
  • src/main/resources/META-INF/spring.factories (1 hunks)
  • src/main/resources/application.properties (1 hunks)
💤 Files with no reviewable changes (2)
  • src/main/java/com/otavio/aifoodapp/config/CorsConfig.java
  • src/main/java/com/otavio/aifoodapp/config/SecurityConfig.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/com/otavio/aifoodapp/controller/AuthenticationController.java (1)
src/main/java/com/otavio/aifoodapp/dto/RegisterDTO.java (1)
  • RegisterDTO (5-27)
🪛 LanguageTool
README.MD

[grammar] ~19-~19: Possível erro de concordância de número.
Context: ...adas - Spring Boot: Framework para aplicações Java - Spring Data JPA: Persistência de ...

(GENERAL_NUMBER_AGREEMENT_ERRORS)

🔇 Additional comments (7)
src/main/java/com/otavio/aifoodapp/dto/RegisterDTO.java (1)

15-18: Accessor names break Java-Bean conventions – verify framework bindings.

Spring’s object-mapping (e.g., @RequestBody → DTO) works fine with records, but if you keep the class form, most libraries expect getLogin(), getPassword(), getRole(). Using the current login()/password()/role() style could silently break validation or Jackson serialisation in some edge cases.

src/main/java/com/otavio/aifoodapp/AiFoodAppApplication.java (1)

8-8: Whitespace-only change – no functional impact.

pom.xml (1)

30-41:

Details

❌ Incorrect review comment

Avoid mixing spring-boot-starter-web and spring-boot-starter-webflux unless you really need both

Having both starters on the class-path pulls in two servlet stacks (Spring MVC on Tomcat and Spring WebFlux on Reactor Netty).
Unless you explicitly run a hybrid application, this increases startup time, memory footprint and may lead to ambiguous handler mappings.

Run a quick grep to confirm no reactive controllers are present:

If nothing is returned, consider removing the WebFlux starter.


🏁 Script executed:

#!/bin/bash
fd --extension java | rg '@RestController' -A2 | rg 'org.springframework.web.reactive'

Length of output: 84


🏁 Script executed:

#!/bin/bash
# Search for any WebFlux or Reactor usage in the Java sources
rg -n --glob '*.java' 'import org.springframework.web.reactive'
rg -n --glob '*.java' 'import reactor.core.publisher'
rg -n --glob '*.java' '@EnableWebFlux'
rg -n --glob '*.java' 'RouterFunction'
rg -n --glob '*.java' 'Mono<'
rg -n --glob '*.java' 'Flux<'

Length of output: 2052


Reactive WebFlux usage detected – retaining both Web and WebFlux starters is required

The codebase includes multiple reactive components that depend on Spring WebFlux and Reactor:

  • WebClient usage in
    • src/main/java/com/otavio/aifoodapp/config/WebClientConfig.java
    • src/main/java/com/otavio/aifoodapp/config/MaritacaChatClient.java
  • Reactor types (Mono, Flux) in
    • src/main/java/com/otavio/aifoodapp/service/ChatService.java
    • src/main/java/com/otavio/aifoodapp/config/MaritacaChatClient.java
  • Reactive controller endpoints returning Mono<ResponseEntity<…>> in
    • src/main/java/com/otavio/aifoodapp/controller/RecipeController.java

Since you’re using WebFlux’s WebClient, Reactor’s Mono/Flux, and reactive controllers, you need both spring-boot-starter-web and spring-boot-starter-webflux on the classpath.

Likely an incorrect or invalid review comment.

src/main/java/com/otavio/aifoodapp/security/TokenService.java (1)

34-45: Unhandled JWTVerificationException bubbles up as 500

validateToken() throws RuntimeException; the filter catches nothing, so an invalid token becomes a 500 instead of 401. Either:

  1. Return null on verification failure, or
  2. Throw a custom exception and map it to HttpStatus.UNAUTHORIZED.
src/main/java/com/otavio/aifoodapp/security/SecurityConfig.java (1)

26-40: LGTM – concise stateless JWT config

Configuration is minimal yet sufficient: CSRF disabled, stateless, endpoints whitelisted, custom filter registered correctly.

README.MD (2)

15-15: Update project description to highlight security features
A descrição foi atualizada para mencionar autenticação e JWT; parece adequada e clara.


71-77: Verificar migração Flyway para tabela tb_user
Confirme se existe um script de migração que cria tb_user para evitar inconsistências entre a documentação e as migrations.

Comment on lines +3 to +4
public record LoginResponseDTO(String token, String login, String role) {
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use the enum type instead of plain String for role.

Returning UserRoles directly preserves type-safety and avoids downstream string/enum conversions.

-public record LoginResponseDTO(String token, String login, String role) {}
+import com.otavio.aifoodapp.enums.UserRoles;
+
+public record LoginResponseDTO(String token, String login, UserRoles role) {}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public record LoginResponseDTO(String token, String login, String role) {
}
import com.otavio.aifoodapp.enums.UserRoles;
public record LoginResponseDTO(String token, String login, UserRoles role) {}
🤖 Prompt for AI Agents
In src/main/java/com/otavio/aifoodapp/dto/LoginResponseDTO.java at lines 3 to 4,
the role field is currently a String, which reduces type safety. Change the type
of the role field from String to the enum type UserRoles to ensure type safety
and avoid unnecessary conversions. Update the record declaration accordingly to
use UserRoles for the role parameter.

Comment on lines +37 to +41
public User(String login, String password, UserRoles role) {
this.login = login;
this.password = password;
this.role = role;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Constructor leaves other non-nullable columns uninitialised

firstName, lastName, and email are declared without nullable = true/false on the column but stay null when this constructor is used.
If the DB columns are NOT NULL, inserts will fail at runtime.

 public User(String login, String password, UserRoles role
-           /* missing params */) {
-    this.login = login;
-    this.password = password;
-    this.role = role;
+           , String firstName, String lastName, String email) {
+    this.login = login;
+    this.password = password;
+    this.role = role;
+    this.firstName = firstName;
+    this.lastName  = lastName;
+    this.email     = email;
 }

Either supply the remaining fields or mark the DB columns nullable.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public User(String login, String password, UserRoles role) {
this.login = login;
this.password = password;
this.role = role;
}
public User(String login, String password, UserRoles role,
String firstName, String lastName, String email) {
this.login = login;
this.password = password;
this.role = role;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
🤖 Prompt for AI Agents
In src/main/java/com/otavio/aifoodapp/model/User.java around lines 37 to 41, the
constructor initializes only login, password, and role, leaving firstName,
lastName, and email uninitialized. Since these fields map to database columns
that are likely NOT NULL, this will cause insert failures. To fix this, either
extend the constructor to accept and initialize firstName, lastName, and email,
or update the entity annotations to mark these columns as nullable if they can
be null.

Comment on lines +3 to +11
import com.otavio.aifoodapp.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.security.core.userdetails.UserDetails;

import com.otavio.aifoodapp.model.User;

public interface UserRepository extends JpaRepository<User, Long>{
public interface UserRepository extends JpaRepository<User, Long> {


UserDetails findByLogin(String login);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Return concrete User and keep Optional to avoid null handling

Spring-Data query methods normally return the entity type. Returning UserDetails works (covariance) but loses type information and forces manual null checks downstream.

-UserDetails findByLogin(String login);
+Optional<User> findByLogin(String login);

This restores type‐safety and lets callers use orElseThrow, eliminating ad-hoc null checks.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/otavio/aifoodapp/repository/UserRepository.java around
lines 3 to 11, change the return type of the method findByLogin from UserDetails
to Optional<User>. This improves type safety by returning the concrete User
entity wrapped in an Optional, allowing callers to handle absence of a user more
cleanly with Optional methods like orElseThrow, instead of manual null checks.

Comment on lines +26 to +28
.withExpiresAt(genExperationDate())
.sign(algorithm);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Typo & public API leak: rename genExperationDate()generateExpirationDate()

Spelling errors hurt readability and leak into stack-traces. Rename the method and fix the call site.

-                    .withExpiresAt(genExperationDate())
+                    .withExpiresAt(generateExpirationDate())
...
-    private Instant genExperationDate() {
+    private Instant generateExpirationDate() {
         return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00"));
     }

Also applies to: 47-49

🤖 Prompt for AI Agents
In src/main/java/com/otavio/aifoodapp/security/TokenService.java around lines
26-28 and 47-49, the method name genExperationDate() contains a typo and is
publicly accessible, which can cause confusion and leak implementation details.
Rename the method to generateExpirationDate() and update all call sites
accordingly to fix the spelling and improve API clarity.

Comment on lines +46 to +50
private String recoverToken(HttpServletRequest request) {
var authHeader = request.getHeader("Authorization");
if (authHeader == null) return null;
return authHeader.replace("Bearer ", "");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

recoverToken should strictly check the prefix

-        var authHeader = request.getHeader("Authorization");
-        if (authHeader == null) return null;
-        return authHeader.replace("Bearer ", "");
+        var authHeader = request.getHeader("Authorization");
+        if (authHeader == null || !authHeader.startsWith("Bearer ")) return null;
+        return authHeader.substring(7);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private String recoverToken(HttpServletRequest request) {
var authHeader = request.getHeader("Authorization");
if (authHeader == null) return null;
return authHeader.replace("Bearer ", "");
}
private String recoverToken(HttpServletRequest request) {
var authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) return null;
return authHeader.substring(7);
}
🤖 Prompt for AI Agents
In src/main/java/com/otavio/aifoodapp/security/SecurityFilter.java around lines
46 to 50, the recoverToken method currently removes the "Bearer " prefix without
verifying it is present. Update the method to first check if the Authorization
header starts exactly with "Bearer " and only then remove the prefix and return
the token; otherwise, return null. This ensures the token is only extracted when
the header format is correct.

Comment on lines +31 to +43
var token = this.recoverToken(request);
if (token != null) {
var login = tokenService.validateToken(token);
if (login != null && !login.isEmpty()) {
var userExists = userRepository.findByLogin(login);
if (userExists != null) {
UserDetails user = userExists;
var authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Gracefully handle malformed / missing Bearer tokens

tokenService.validateToken() is invoked for any non-null Authorization header, even if it doesn’t start with Bearer . This throws and aborts the request pipeline.

-        var token = this.recoverToken(request);
-        if (token != null) {
+        var token = this.recoverToken(request);
+        if (token != null && !token.isBlank()) {
             var login = tokenService.validateToken(token);

Also consider surrounding validateToken with try/catch to convert verification failures into 401.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var token = this.recoverToken(request);
if (token != null) {
var login = tokenService.validateToken(token);
if (login != null && !login.isEmpty()) {
var userExists = userRepository.findByLogin(login);
if (userExists != null) {
UserDetails user = userExists;
var authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
// … earlier in the filter …
var token = this.recoverToken(request);
if (token != null && !token.isBlank()) {
var login = tokenService.validateToken(token);
if (login != null && !login.isEmpty()) {
var userExists = userRepository.findByLogin(login);
if (userExists != null) {
UserDetails user = userExists;
var authentication = new UsernamePasswordAuthenticationToken(
user,
null,
user.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
🤖 Prompt for AI Agents
In src/main/java/com/otavio/aifoodapp/security/SecurityFilter.java around lines
31 to 43, the code calls tokenService.validateToken() on any non-null token
without verifying it starts with "Bearer ", causing exceptions on malformed
tokens. To fix this, first check if the Authorization header starts with "Bearer
" before extracting and validating the token. Additionally, wrap the
validateToken call in a try/catch block to catch verification exceptions and
respond with a 401 Unauthorized status instead of aborting the request pipeline.

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use jakarta.validation.Valid with Spring Boot 3

Boot 3 ships with Jakarta EE 9; mixing javax.validation may lead to NoSuchMethodError at runtime if a legacy artifact sneaks in.

-import javax.validation.Valid;
+import jakarta.validation.Valid;
🤖 Prompt for AI Agents
In src/main/java/com/otavio/aifoodapp/controller/AuthenticationController.java
at line 18, replace the import statement from javax.validation.Valid to
jakarta.validation.Valid to ensure compatibility with Spring Boot 3 and avoid
runtime errors caused by mixing legacy javax packages with Jakarta EE 9.

Comment on lines +104 to 108
- `GET /api/foods/list`: Lista alimentos
- `GET /api/foods/list/{id}`: Busca alimento por ID
- `PUT /api/foods/update`: Atualiza alimento
- `DELETE /api/foods/delete/{id}`: Remove alimento

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Revisar convenção REST nos endpoints de alimentos
Em vez de usar /api/foods/create, considere POST /api/foods para seguir a convenção sem verbos na URL.

🤖 Prompt for AI Agents
In README.MD around lines 104 to 108, the REST API endpoints for foods use verbs
in the URL paths, which is against REST conventions. Update the endpoints to
remove verbs by using HTTP methods to indicate actions: replace
`/api/foods/create` with `POST /api/foods`, `/api/foods/update` with `PUT
/api/foods/{id}`, and `/api/foods/delete/{id}` with `DELETE /api/foods/{id}`.
Adjust the documentation accordingly to reflect these standard RESTful patterns.

@OtavioXimarelli OtavioXimarelli deleted the SpringSecurity-implementation branch June 11, 2025 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants