Index: pom.xml
===================================================================
--- pom.xml	(revision a6008b0dfce5c36ab5a8d18125330e3a372f0ef5)
+++ pom.xml	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
@@ -114,6 +114,11 @@
 			<scope>runtime</scope>
 		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</artifactId>
+		</dependency>
 
-    </dependencies>
+
+	</dependencies>
 
 	<build>
Index: src/main/java/mk/ukim/finki/synergymed/security/JwtAuthFilter.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/security/JwtAuthFilter.java	(revision a6008b0dfce5c36ab5a8d18125330e3a372f0ef5)
+++ src/main/java/mk/ukim/finki/synergymed/security/JwtAuthFilter.java	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
@@ -5,6 +5,7 @@
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import mk.ukim.finki.synergymed.service.jwt.JwtService;
+import mk.ukim.finki.synergymed.service.jwt.TokenBlacklistService;
 import org.springframework.lang.NonNull;
-import mk.ukim.finki.synergymed.service.jwt.JwtService;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -21,9 +22,13 @@
 
     private final JwtService jwtService;
-    private final UserDetailsService userDetailsService; // your AppUserDetails bean
+    private final UserDetailsService userDetailsService;
+    private final TokenBlacklistService tokenBlacklistService;
 
-    public JwtAuthFilter(JwtService jwtService, UserDetailsService userDetailsService){
+    public JwtAuthFilter(JwtService jwtService,
+                         UserDetailsService userDetailsService,
+                         TokenBlacklistService tokenBlacklistService) {
         this.jwtService = jwtService;
         this.userDetailsService = userDetailsService;
+        this.tokenBlacklistService = tokenBlacklistService;
     }
 
@@ -38,7 +43,15 @@
             String token = header.substring(7);
             try {
-                var claims = jwtService.parse(token).getPayload();
+                var jws = jwtService.parse(token);
+                var claims = jws.getPayload();
+
+                // check if token is blacklisted
+                String jti = claims.getId();
+                if (jti != null && tokenBlacklistService.contains(jti)) {
+                    res.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token has been revoked");
+                    return; // stop filter chain
+                }
+
                 var username = claims.getSubject();
-
                 if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                     UserDetails ud = userDetailsService.loadUserByUsername(username);
@@ -48,7 +61,8 @@
                 }
 
-                // TODO: 28.8.2025 HANDLE INVALID JWT TOKEN PROPERLY
-            } catch (Exception ignored) {
-                // invalid/expired token -> leave context unauthenticated
+            } catch (Exception e) {
+                // invalid/expired token -> block request
+                res.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid or expired token");
+                return;
             }
         }
Index: src/main/java/mk/ukim/finki/synergymed/service/jwt/JwtService.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/service/jwt/JwtService.java	(revision a6008b0dfce5c36ab5a8d18125330e3a372f0ef5)
+++ src/main/java/mk/ukim/finki/synergymed/service/jwt/JwtService.java	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
@@ -15,4 +15,5 @@
 import java.util.Date;
 import java.util.List;
+import java.util.UUID;
 
 @Component
@@ -40,9 +41,10 @@
 
         return Jwts.builder()
-                .subject(ud.getUsername())     // 0.12.x
+                .subject(ud.getUsername())
                 .claim("roles", roles)
+                .id(UUID.randomUUID().toString())
                 .issuedAt(now)
                 .expiration(exp)
-                .signWith(key)                 // <— let JJWT infer HS256/384/512 from key size
+                .signWith(key)
                 .compact();
     }
Index: src/main/java/mk/ukim/finki/synergymed/service/jwt/TokenBlacklistService.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/service/jwt/TokenBlacklistService.java	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
+++ src/main/java/mk/ukim/finki/synergymed/service/jwt/TokenBlacklistService.java	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
@@ -0,0 +1,26 @@
+package mk.ukim.finki.synergymed.service.jwt;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+
+@Component
+@RequiredArgsConstructor
+public class TokenBlacklistService {
+
+    private final StringRedisTemplate redis;
+
+    public void add(String jti, long expirationMillis) {
+        redis.opsForValue().set(
+                "blacklist:" + jti,
+                "revoked",
+                Duration.ofMillis(expirationMillis)
+        );
+    }
+
+    public boolean contains(String jti) {
+        return redis.hasKey("blacklist:" + jti);
+    }
+}
Index: src/main/java/mk/ukim/finki/synergymed/web/LogoutController.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/web/LogoutController.java	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
+++ src/main/java/mk/ukim/finki/synergymed/web/LogoutController.java	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
@@ -0,0 +1,36 @@
+package mk.ukim.finki.synergymed.web;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import mk.ukim.finki.synergymed.service.jwt.JwtService;
+import mk.ukim.finki.synergymed.service.jwt.TokenBlacklistService;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/logout")
+public class LogoutController {
+
+    private final JwtService jwtService;
+    private final TokenBlacklistService tokenBlacklistService;
+
+    public LogoutController(JwtService jwtService, TokenBlacklistService tokenBlacklistService) {
+        this.jwtService = jwtService;
+        this.tokenBlacklistService = tokenBlacklistService;
+    }
+
+    @PostMapping()
+    public String logout(@RequestHeader("Authorization") String authHeader) {
+        if (authHeader != null && authHeader.startsWith("Bearer ")) {
+            String token = authHeader.substring(7);
+
+            Jws<Claims> jws = jwtService.parse(token);
+            Claims claims = jws.getPayload();
+
+            long expirationMillis = claims.getExpiration().getTime() - System.currentTimeMillis();
+            if (expirationMillis > 0) {
+                tokenBlacklistService.add(claims.getId(), expirationMillis);
+            }
+        }
+        return "redirect:/login";
+    }
+}
Index: src/main/resources/application.properties
===================================================================
--- src/main/resources/application.properties	(revision a6008b0dfce5c36ab5a8d18125330e3a372f0ef5)
+++ src/main/resources/application.properties	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
@@ -8,6 +8,22 @@
 spring.datasource.driver-class-name=org.postgresql.Driver
 
-security.jwt.secret=REPLACE_WITH_A_RANDOM_32+_CHAR_SECRET_KEY_1234567890abcdef
-security.jwt.exp-min=60
+# ==========================
+# Redis connection
+# ==========================
+spring.data.redis.host=localhost
+spring.data.redis.port=6379
+# spring.data.redis.password=yourStrongPassword   # only if configured
+
+# Logical DB index
+spring.data.redis.database=0
+
+# Connection timeout (must be a duration, not raw ms)
+spring.data.redis.timeout=60s
+
+# ==========================
+# JWT (inject into @Value)
+# ==========================
+security.jwt.secret=ThisIsASuperSecretJwtKeyWith32CharsOrMore!
+security.jwt.exp-min=15
 
 spring.datasource.hikari.maximum-pool-size=5
Index: src/main/resources/templates/profile.html
===================================================================
--- src/main/resources/templates/profile.html	(revision a6008b0dfce5c36ab5a8d18125330e3a372f0ef5)
+++ src/main/resources/templates/profile.html	(revision a39e41777a0da9c35f1854394a3bf19ce03b8d7b)
@@ -144,5 +144,5 @@
             <a href="#" class="nav-link">Medical History</a>
             <a href="/allergies/manage" class="nav-link">Manage Allergies</a>
-            <a href="/login" class="logout-btn">Logout</a>
+            <a href="/logout" class="logout-btn">Logout</a>
         </div>
     </div>
