diff --git a/pom.xml b/pom.xml
index e61d69e..c664f71 100644
--- a/pom.xml
+++ b/pom.xml
@@ -172,6 +172,11 @@
+
+ com.github.ben-manes.caffeine
+ caffeine
+ 3.1.8
+
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/controller/AuthController.java b/src/main/java/com/skycrate/backend/skycrateBackend/controller/AuthController.java
index b823bd4..55d017b 100644
--- a/src/main/java/com/skycrate/backend/skycrateBackend/controller/AuthController.java
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/controller/AuthController.java
@@ -24,12 +24,26 @@ public class AuthController {
}
@PostMapping("/login")
- public ResponseEntity> login(@RequestBody LoginRequest request) {
- authManager.authenticate(new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()));
+ public ResponseEntity> login(@RequestBody LoginRequest request, HttpServletRequest servletRequest) {
+ String ip = servletRequest.getRemoteAddr(); // or use request.getEmail() as key
+
+ if (rateLimiterService.isBlocked(ip)) {
+ return ResponseEntity.status(429).body("Too many login attempts. Please try again later.");
+ }
+
+ try {
+ authManager.authenticate(
+ new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword())
+ );
+ } catch (Exception ex) {
+ rateLimiterService.recordFailedAttempt(ip);
+ return ResponseEntity.status(401).body("Invalid credentials.");
+ }
User user = userRepository.findByEmail(request.getEmail())
.orElseThrow(() -> new RuntimeException("User not found"));
+ rateLimiterService.resetAttempts(ip);
String token = jwtService.generateToken(user);
return ResponseEntity.ok().body(token);
}
diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java b/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java
new file mode 100644
index 0000000..7aad7ee
--- /dev/null
+++ b/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java
@@ -0,0 +1,35 @@
+package com.skycrate.backend.skycrateBackend.security;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class RateLimiterService {
+
+ private final Cache attemptsCache;
+
+ private static final int MAX_ATTEMPTS = 5;
+
+ public RateLimiterService() {
+ this.attemptsCache = Caffeine.newBuilder()
+ .expireAfterWrite(1, TimeUnit.MINUTES)
+ .build();
+ }
+
+ public boolean isBlocked(String key) {
+ Integer attempts = attemptsCache.getIfPresent(key);
+ return attempts != null && attempts >= MAX_ATTEMPTS;
+ }
+
+ public void recordFailedAttempt(String key) {
+ int attempts = attemptsCache.getIfPresent(key) == null ? 0 : attemptsCache.getIfPresent(key);
+ attemptsCache.put(key, attempts + 1);
+ }
+
+ public void resetAttempts(String key) {
+ attemptsCache.invalidate(key);
+ }
+}
\ No newline at end of file