From a9fc5ef537e914ff8826125d268c9e065f1f9e6f Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Wed, 1 Apr 2026 23:52:54 +0200 Subject: [PATCH 1/9] feat(auth): add initial login flow with controller, service and password configuration --- .../example/alfs/config/PasswordConfig.java | 16 +++++++++ .../alfs/controllers/AuthController.java | 32 +++++++++++++++++ .../example/alfs/services/AuthService.java | 34 +++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 src/main/java/org/example/alfs/config/PasswordConfig.java create mode 100644 src/main/java/org/example/alfs/controllers/AuthController.java create mode 100644 src/main/java/org/example/alfs/services/AuthService.java diff --git a/src/main/java/org/example/alfs/config/PasswordConfig.java b/src/main/java/org/example/alfs/config/PasswordConfig.java new file mode 100644 index 0000000..e319407 --- /dev/null +++ b/src/main/java/org/example/alfs/config/PasswordConfig.java @@ -0,0 +1,16 @@ +package org.example.alfs.config; + +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 PasswordConfig { + + // Makes password encoder available in the whole app + @Bean + public PasswordEncoder passwordEncoder(){ + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/org/example/alfs/controllers/AuthController.java b/src/main/java/org/example/alfs/controllers/AuthController.java new file mode 100644 index 0000000..dd35fd9 --- /dev/null +++ b/src/main/java/org/example/alfs/controllers/AuthController.java @@ -0,0 +1,32 @@ +package org.example.alfs.controllers; + +import org.example.alfs.entities.User; +import org.example.alfs.services.AuthService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@RestController +@RequestMapping("/auth") +public class AuthController { + + private final AuthService authService; + + public AuthController(AuthService authService) { + this.authService = authService; + } + + @PostMapping("/login") + public String login(@RequestBody Map body) { + + String username = body.get("username"); + String password = body.get("password"); + + User user = authService.login(username, password); + + return "Login success: " + user.getUsername(); + } +} diff --git a/src/main/java/org/example/alfs/services/AuthService.java b/src/main/java/org/example/alfs/services/AuthService.java new file mode 100644 index 0000000..b544a26 --- /dev/null +++ b/src/main/java/org/example/alfs/services/AuthService.java @@ -0,0 +1,34 @@ +package org.example.alfs.services; + +import org.example.alfs.entities.User; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +public class AuthService { + + private final PasswordEncoder passwordEncoder; + + public AuthService(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + public User login(String username, String password) { + + + // hash av "1234" + String hashedPassword = passwordEncoder.encode("1234"); + + // fake user until userRepository exist + User user = new User(); + user.setUsername("adam"); + user.setPasswordHash(hashedPassword); + System.out.println(hashedPassword); + + if (!passwordEncoder.matches(password, user.getPasswordHash())) { + throw new RuntimeException("Invalid username or password"); + } + + return user; + } +} From 35b2d10534c407599853607becb6bd332f06f56a Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Thu, 2 Apr 2026 13:29:27 +0200 Subject: [PATCH 2/9] Add findByUsername method to userRepository --- src/main/java/org/example/alfs/repositories/UserRepository.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/example/alfs/repositories/UserRepository.java b/src/main/java/org/example/alfs/repositories/UserRepository.java index 2b82879..d31ffe9 100644 --- a/src/main/java/org/example/alfs/repositories/UserRepository.java +++ b/src/main/java/org/example/alfs/repositories/UserRepository.java @@ -7,4 +7,5 @@ public interface UserRepository extends JpaRepository { + Optional findByUsername(String username); } From 3e31486aab7783eb90931a829892c52e2fdbcead Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Thu, 2 Apr 2026 13:30:41 +0200 Subject: [PATCH 3/9] Adds LoginRequestDTO and LoginResponseDTO --- .../org/example/alfs/dto/auth/LoginRequestDTO.java | 12 ++++++++++++ .../org/example/alfs/dto/auth/LoginResponseDTO.java | 13 +++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java create mode 100644 src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java diff --git a/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java b/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java new file mode 100644 index 0000000..2dd7a4d --- /dev/null +++ b/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java @@ -0,0 +1,12 @@ +package org.example.alfs.dto.auth; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class LoginRequestDTO { + private String username; + private String password; + +} diff --git a/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java b/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java new file mode 100644 index 0000000..fb78abd --- /dev/null +++ b/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java @@ -0,0 +1,13 @@ +package org.example.alfs.dto.auth; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@AllArgsConstructor +public class LoginResponseDTO { + + private String username; + private String role; +} From 13745ee872628c7139e8213a8ba220a9611fcc1c Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Thu, 2 Apr 2026 13:35:42 +0200 Subject: [PATCH 4/9] Refactor AuthService to use UserRepository via dependency injection and adds http status codes --- .../example/alfs/services/AuthService.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/example/alfs/services/AuthService.java b/src/main/java/org/example/alfs/services/AuthService.java index b544a26..fb624ae 100644 --- a/src/main/java/org/example/alfs/services/AuthService.java +++ b/src/main/java/org/example/alfs/services/AuthService.java @@ -1,32 +1,35 @@ package org.example.alfs.services; import org.example.alfs.entities.User; +import org.example.alfs.repositories.UserRepository; +import org.springframework.http.HttpStatus; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.web.server.ResponseStatusException; @Service public class AuthService { private final PasswordEncoder passwordEncoder; + private final UserRepository userRepository; - public AuthService(PasswordEncoder passwordEncoder) { + public AuthService(PasswordEncoder passwordEncoder, UserRepository userRepository) { this.passwordEncoder = passwordEncoder; + this.userRepository = userRepository; } public User login(String username, String password) { - - // hash av "1234" - String hashedPassword = passwordEncoder.encode("1234"); - - // fake user until userRepository exist - User user = new User(); - user.setUsername("adam"); - user.setPasswordHash(hashedPassword); - System.out.println(hashedPassword); - + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new ResponseStatusException( + HttpStatus.UNAUTHORIZED, + "Invalid username or password" + )); if (!passwordEncoder.matches(password, user.getPasswordHash())) { - throw new RuntimeException("Invalid username or password"); + throw new ResponseStatusException( + HttpStatus.UNAUTHORIZED, + "Invalid username or password" + ); } return user; From c13970781470cac0a07cb7f0fdf309532b22beed Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Thu, 2 Apr 2026 13:36:42 +0200 Subject: [PATCH 5/9] refactor(auth): use DTOs for login request and response --- .../alfs/controllers/AuthController.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/example/alfs/controllers/AuthController.java b/src/main/java/org/example/alfs/controllers/AuthController.java index dd35fd9..13cc81b 100644 --- a/src/main/java/org/example/alfs/controllers/AuthController.java +++ b/src/main/java/org/example/alfs/controllers/AuthController.java @@ -1,5 +1,7 @@ package org.example.alfs.controllers; +import org.example.alfs.dto.auth.LoginRequestDTO; +import org.example.alfs.dto.auth.LoginResponseDTO; import org.example.alfs.entities.User; import org.example.alfs.services.AuthService; import org.springframework.web.bind.annotation.PostMapping; @@ -20,13 +22,16 @@ public AuthController(AuthService authService) { } @PostMapping("/login") - public String login(@RequestBody Map body) { + public LoginResponseDTO login(@RequestBody LoginRequestDTO request) { - String username = body.get("username"); - String password = body.get("password"); + User user = authService.login( + request.getUsername(), + request.getPassword() + ); - User user = authService.login(username, password); - - return "Login success: " + user.getUsername(); + return new LoginResponseDTO( + user.getUsername(), + user.getRole().name() + ); } } From 14a0cb24aa56bf5f09be7571476b80883df8ac73 Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Thu, 2 Apr 2026 14:15:18 +0200 Subject: [PATCH 6/9] Adds bean validation to LoginRequestDTO --- src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java b/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java index 2dd7a4d..e318715 100644 --- a/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java +++ b/src/main/java/org/example/alfs/dto/auth/LoginRequestDTO.java @@ -1,12 +1,17 @@ package org.example.alfs.dto.auth; +import jakarta.validation.constraints.NotBlank; import lombok.Getter; import lombok.Setter; @Getter @Setter public class LoginRequestDTO { + + @NotBlank(message = "Username is required") private String username; + + @NotBlank(message = "Password is required") private String password; } From 425dbf6cab1281747460321172282289e95ffb7d Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Thu, 2 Apr 2026 14:16:11 +0200 Subject: [PATCH 7/9] docs(auth): add JavaDoc for AuthController and AuthService --- .../java/org/example/alfs/controllers/AuthController.java | 8 +++++--- src/main/java/org/example/alfs/services/AuthService.java | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/example/alfs/controllers/AuthController.java b/src/main/java/org/example/alfs/controllers/AuthController.java index 13cc81b..fdf7562 100644 --- a/src/main/java/org/example/alfs/controllers/AuthController.java +++ b/src/main/java/org/example/alfs/controllers/AuthController.java @@ -8,8 +8,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; - -import java.util.Map; +import jakarta.validation.Valid; @RestController @RequestMapping("/auth") @@ -21,8 +20,11 @@ public AuthController(AuthService authService) { this.authService = authService; } + /** + * Handles user login by validating credentials and returning user details. + */ @PostMapping("/login") - public LoginResponseDTO login(@RequestBody LoginRequestDTO request) { + public LoginResponseDTO login(@Valid @RequestBody LoginRequestDTO request) { User user = authService.login( request.getUsername(), diff --git a/src/main/java/org/example/alfs/services/AuthService.java b/src/main/java/org/example/alfs/services/AuthService.java index fb624ae..c227f2f 100644 --- a/src/main/java/org/example/alfs/services/AuthService.java +++ b/src/main/java/org/example/alfs/services/AuthService.java @@ -18,6 +18,9 @@ public AuthService(PasswordEncoder passwordEncoder, UserRepository userRepositor this.userRepository = userRepository; } + /** + * Authenticates a user by verifying username and password. + */ public User login(String username, String password) { User user = userRepository.findByUsername(username) From bdcbf0566b8acf052cf0abe03f0bf9f0dd1f5313 Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Thu, 2 Apr 2026 14:17:56 +0200 Subject: [PATCH 8/9] removed unused import --- src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java b/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java index fb78abd..350e6eb 100644 --- a/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java +++ b/src/main/java/org/example/alfs/dto/auth/LoginResponseDTO.java @@ -2,7 +2,6 @@ import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.Setter; @Getter @AllArgsConstructor From 6e958388603612f895a8c1edc103f415be44a09c Mon Sep 17 00:00:00 2001 From: Adam Ottosson Date: Thu, 2 Apr 2026 14:31:47 +0200 Subject: [PATCH 9/9] Makes role not nullable. If role is null set role as reporter. --- src/main/java/org/example/alfs/entities/User.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/example/alfs/entities/User.java b/src/main/java/org/example/alfs/entities/User.java index 43f85d3..3925335 100644 --- a/src/main/java/org/example/alfs/entities/User.java +++ b/src/main/java/org/example/alfs/entities/User.java @@ -27,6 +27,14 @@ public class User { private String passwordHash; @Enumerated(EnumType.STRING) + @Column(nullable = false) private Role role; + @PrePersist + public void prePersist() { + if (role == null) { + role = Role.REPORTER; + } + } + }