Index: pom.xml
===================================================================
--- pom.xml	(revision 103984275890b6224c562adab88e9c76e8333980)
+++ pom.xml	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -32,4 +32,9 @@
 	</properties>
 	<dependencies>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+			<version>3.18.0</version>
+		</dependency>
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
@@ -90,4 +95,10 @@
         </dependency>
 
+		<dependency>
+			<groupId>org.springdoc</groupId>
+			<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+			<version>2.5.0</version>
+		</dependency>
+
 		<!-- JWT (JJWT) -->
 		<dependency>
Index: src/main/java/mk/ukim/finki/synergymed/config/OpenApiConfig.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/config/OpenApiConfig.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
+++ src/main/java/mk/ukim/finki/synergymed/config/OpenApiConfig.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -0,0 +1,29 @@
+package mk.ukim.finki.synergymed.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class OpenApiConfig {
+
+    @Bean
+    public OpenAPI api() {
+        final String BEARER = "bearerAuth";
+        return new OpenAPI()
+                .info(new Info().title("SynergyMed API").version("v1"))
+                .addSecurityItem(new SecurityRequirement().addList(BEARER))
+                .components(new io.swagger.v3.oas.models.Components()
+                        .addSecuritySchemes(BEARER,
+                                new SecurityScheme()
+                                        .name(BEARER)
+                                        .type(SecurityScheme.Type.HTTP)
+                                        .scheme("bearer")
+                                        .bearerFormat("JWT")
+                        )
+                );
+    }
+}
Index: src/main/java/mk/ukim/finki/synergymed/config/SecurityConfig.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/config/SecurityConfig.java	(revision 103984275890b6224c562adab88e9c76e8333980)
+++ src/main/java/mk/ukim/finki/synergymed/config/SecurityConfig.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -1,8 +1,10 @@
 package mk.ukim.finki.synergymed.config;
 
-import lombok.RequiredArgsConstructor;
 import mk.ukim.finki.synergymed.security.JwtAuthFilter;
+import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.Customizer;
 import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -11,19 +13,42 @@
 import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
 
 @Configuration
 @EnableMethodSecurity
-@RequiredArgsConstructor
 public class SecurityConfig {
 
-    private final JwtAuthFilter jwtAuthFilter;  // your filter
+    private final JwtAuthFilter jwtAuthFilter;
+
+    public SecurityConfig(JwtAuthFilter jwtAuthFilter) {
+        this.jwtAuthFilter = jwtAuthFilter;
+    }
 
     @Bean
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         http
+                .cors(Customizer.withDefaults()) // enable CORS using the bean below
                 .csrf(AbstractHttpConfigurer::disable)
                 .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                 .authorizeHttpRequests(reg -> reg
-                        .requestMatchers("/auth/login", "/public/**", "/error").permitAll()
+                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
+
+                        // Static resources in /static, /public, /resources, /META-INF/resources
+                        .requestMatchers(String.valueOf(PathRequest.toStaticResources().atCommonLocations())).permitAll()
+                        .requestMatchers("/", "/login", "/login.html", "/index.html", "/favicon.ico").permitAll()
+
+                        // Public API/docs
+                        .requestMatchers(
+                                "/auth/login",
+                                "/error",
+                                "/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**",
+                                "/swagger-resources/**", "/webjars/**"
+                        ).permitAll()
+
+                        // Everything else requires JWT
                         .anyRequest().authenticated()
                 )
@@ -32,3 +57,18 @@
         return http.build();
     }
+
+    @Bean
+    public CorsConfigurationSource corsConfigurationSource() {
+        CorsConfiguration c = new CorsConfiguration();
+        // be strict in prod (e.g., setAllowedOrigins(List.of("http://localhost:8080")))
+        c.setAllowedOriginPatterns(List.of("*"));
+        c.setAllowedMethods(List.of("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
+        c.setAllowedHeaders(List.of("Authorization", "Content-Type", "Accept", "Origin", "X-Requested-With"));
+        c.setExposedHeaders(List.of("Authorization"));
+        c.setAllowCredentials(true);
+
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", c);
+        return source;
+    }
 }
Index: src/main/java/mk/ukim/finki/synergymed/models/User.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/models/User.java	(revision 103984275890b6224c562adab88e9c76e8333980)
+++ src/main/java/mk/ukim/finki/synergymed/models/User.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -34,5 +34,5 @@
 
     @Column(name = "e_mail", nullable = false)
-    private String eMail;
+    private String email;
 
     @Column(name = "gender", length = 50)
@@ -49,5 +49,4 @@
     private boolean isCredentialsNonExpired = true;
     private boolean isEnabled = true;
-
     @Override
     public boolean isAccountNonExpired() {
Index: src/main/java/mk/ukim/finki/synergymed/repositories/UserRepository.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/repositories/UserRepository.java	(revision 103984275890b6224c562adab88e9c76e8333980)
+++ src/main/java/mk/ukim/finki/synergymed/repositories/UserRepository.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -3,4 +3,5 @@
 import mk.ukim.finki.synergymed.models.User;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.query.Param;
 
 import java.util.Optional;
@@ -8,5 +9,5 @@
 public interface UserRepository extends JpaRepository<User, Integer> {
     Optional<User> findByUsername(String username);
-    Optional<User> findByEMail(String email);
+    Optional<User> findByEmail(String email);
     boolean existsByUsername(String username);
 }
Index: src/main/java/mk/ukim/finki/synergymed/security/JwtAuthFilter.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/security/JwtAuthFilter.java	(revision 103984275890b6224c562adab88e9c76e8333980)
+++ src/main/java/mk/ukim/finki/synergymed/security/JwtAuthFilter.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -6,5 +6,4 @@
 import jakarta.servlet.http.HttpServletResponse;
 import org.springframework.lang.NonNull;
-import lombok.RequiredArgsConstructor;
 import mk.ukim.finki.synergymed.service.jwt.JwtService;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -19,9 +18,13 @@
 
 @Component
-@RequiredArgsConstructor
 public class JwtAuthFilter extends OncePerRequestFilter {
 
     private final JwtService jwtService;
     private final UserDetailsService userDetailsService; // your AppUserDetails bean
+
+    public JwtAuthFilter(JwtService jwtService, UserDetailsService userDetailsService){
+        this.jwtService = jwtService;
+        this.userDetailsService = userDetailsService;
+    }
 
     @Override
Index: src/main/java/mk/ukim/finki/synergymed/web/HomeController.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/web/HomeController.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
+++ src/main/java/mk/ukim/finki/synergymed/web/HomeController.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -0,0 +1,4 @@
+package mk.ukim.finki.synergymed.web;
+
+public class HomeController {
+}
Index: src/main/java/mk/ukim/finki/synergymed/web/LoginController.java
===================================================================
--- src/main/java/mk/ukim/finki/synergymed/web/LoginController.java	(revision 103984275890b6224c562adab88e9c76e8333980)
+++ src/main/java/mk/ukim/finki/synergymed/web/LoginController.java	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -3,28 +3,57 @@
 import lombok.RequiredArgsConstructor;
 import mk.ukim.finki.synergymed.service.jwt.JwtService;
-import org.springframework.http.ResponseEntity;
 import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.*;
 
-@RestController
-@RequestMapping("/auth")
+import jakarta.servlet.http.HttpSession;
+
+@Controller
+@RequestMapping("/login")
 @RequiredArgsConstructor
 public class LoginController {
 
-    private final AuthenticationManager authManager;
-    private final JwtService jwt;
+    private final AuthenticationManager authenticationManager;
+    private final JwtService jwtService;
 
-    public record LoginRequest(String username, String password) {}
-    public record LoginResponse(String token) {}
+    @GetMapping
+    public String getLoginPage() {
+        return "login";
+    }
 
-    @PostMapping("/login")
-    public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest req) {
-        var auth = authManager.authenticate(
-                new UsernamePasswordAuthenticationToken(req.username(), req.password())
-        );
-        var ud = (UserDetails) auth.getPrincipal();
-        return ResponseEntity.ok(new LoginResponse(jwt.generate(ud)));
+    @PostMapping
+    public String login(@RequestParam String username,
+                        @RequestParam String password,
+                        HttpSession session,
+                        Model model) {
+        try {
+            var authentication = authenticationManager.authenticate(
+                    new UsernamePasswordAuthenticationToken(username, password)
+            );
+
+            var userDetails = (UserDetails) authentication.getPrincipal();
+            String token = jwtService.generate(userDetails);
+
+            // store jwt in session
+            session.setAttribute("jwt_token", token);
+            session.setAttribute("username", username);
+            session.setAttribute("user", userDetails);
+
+            // redirect to home after a successful login
+            return "redirect:/home";
+
+        } catch (BadCredentialsException e) {
+            model.addAttribute("error", "Invalid username or password");
+            model.addAttribute("username", username);
+            return "login";
+        } catch (Exception e) {
+            model.addAttribute("error", "An error occurred during login");
+            model.addAttribute("username", username);
+            return "login";
+        }
     }
 }
Index: src/main/resources/application.properties
===================================================================
--- src/main/resources/application.properties	(revision 103984275890b6224c562adab88e9c76e8333980)
+++ src/main/resources/application.properties	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -4,2 +4,24 @@
 security.jwt.secret=REPLACE_WITH_A_RANDOM_32+_CHAR_SECRET_KEY_1234567890abcdef
 security.jwt.exp-min=60
+
+# --- Datasource ---
+spring.datasource.url=jdbc:postgresql://localhost:5432/synergymed
+spring.datasource.username=postgres
+spring.datasource.password=postgres
+# driver is auto-detected but this is fine to add:
+spring.datasource.driver-class-name=org.postgresql.Driver
+
+# Keep RAM low
+spring.datasource.hikari.maximum-pool-size=5
+
+# --- JPA/Hibernate ---
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.show-sql=true
+spring.jpa.properties.hibernate.format_sql=true
+
+# Your entities use schema="synergymed"
+spring.jpa.properties.hibernate.default_schema=synergymed
+
+# temporarily disable docker-compose till implemented
+spring.docker.compose.enabled=false
Index: src/main/resources/templates/login.html
===================================================================
--- src/main/resources/templates/login.html	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
+++ src/main/resources/templates/login.html	(revision 58f18014eb031b2da8cc131f37bb9591e1637b43)
@@ -0,0 +1,284 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>SynergyMed - Login</title>
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+
+    body {
+      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+      background: linear-gradient(135deg, #a4ecba 0%, #fefeff 100%);
+      min-height: 100vh;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 20px;
+    }
+
+    .login-container {
+      background: white;
+      border-radius: 20px;
+      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+      overflow: hidden;
+      width: 100%;
+      max-width: 400px;
+      backdrop-filter: blur(10px);
+    }
+
+    .login-header {
+      background: linear-gradient(135deg, #20b2aa, #48d1cc);
+      padding: 40px 30px;
+      text-align: center;
+      color: white;
+    }
+
+    .login-header h1 {
+      font-size: 2rem;
+      font-weight: 300;
+      margin-bottom: 8px;
+    }
+
+    .login-header p {
+      opacity: 0.9;
+      font-size: 0.9rem;
+    }
+
+    .login-form {
+      padding: 40px 30px;
+    }
+
+    .form-group {
+      margin-bottom: 25px;
+      position: relative;
+    }
+
+    .form-group label {
+      display: block;
+      margin-bottom: 8px;
+      color: #555;
+      font-weight: 500;
+      font-size: 0.9rem;
+    }
+
+    .form-control {
+      width: 100%;
+      padding: 15px 20px;
+      border: 2px solid #e1e5e9;
+      border-radius: 12px;
+      font-size: 1rem;
+      transition: all 0.3s ease;
+      background: #f8f9fa;
+    }
+
+    .form-control:focus {
+      outline: none;
+      border-color: #20b2aa;
+      background: white;
+      box-shadow: 0 0 0 3px rgba(32, 178, 170, 0.1);
+    }
+
+    .btn-login {
+      width: 100%;
+      padding: 15px;
+      background: linear-gradient(135deg, #20b2aa, #48d1cc);
+      color: white;
+      border: none;
+      border-radius: 12px;
+      font-size: 1rem;
+      font-weight: 600;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      text-transform: uppercase;
+      letter-spacing: 1px;
+    }
+
+    .btn-login:hover {
+      transform: translateY(-2px);
+      box-shadow: 0 10px 20px rgba(32, 178, 170, 0.3);
+    }
+
+    .btn-login:active {
+      transform: translateY(0);
+    }
+
+    .alert {
+      padding: 15px;
+      border-radius: 8px;
+      margin-bottom: 20px;
+      font-size: 0.9rem;
+    }
+
+    .alert-danger {
+      background-color: #fee;
+      color: #c33;
+      border: 1px solid #fcc;
+    }
+
+    .alert-success {
+      background-color: #efe;
+      color: #363;
+      border: 1px solid #cfc;
+    }
+
+    .forgot-password {
+      text-align: center;
+      margin-top: 20px;
+    }
+
+    .forgot-password a {
+      color: #20b2aa;
+      text-decoration: none;
+      font-size: 0.9rem;
+      transition: color 0.3s ease;
+    }
+
+    .forgot-password a:hover {
+      color: #1a9999;
+      text-decoration: underline;
+    }
+
+    .loading {
+      display: none;
+      position: relative;
+    }
+
+    .loading::after {
+      content: '';
+      position: absolute;
+      width: 20px;
+      height: 20px;
+      margin: auto;
+      border: 2px solid transparent;
+      border-top-color: #ffffff;
+      border-radius: 50%;
+      animation: spin 1s linear infinite;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+    }
+
+    @keyframes spin {
+      0% { transform: translate(-50%, -50%) rotate(0deg); }
+      100% { transform: translate(-50%, -50%) rotate(360deg); }
+    }
+
+    .form-footer {
+      text-align: center;
+      padding: 20px 30px;
+      background-color: #f8f9fa;
+      border-top: 1px solid #e9ecef;
+    }
+
+    .form-footer p {
+      color: #6c757d;
+      font-size: 0.85rem;
+    }
+
+    @media (max-width: 480px) {
+      .login-container {
+        margin: 10px;
+        border-radius: 15px;
+      }
+
+      .login-header {
+        padding: 30px 20px;
+      }
+
+      .login-form {
+        padding: 30px 20px;
+      }
+
+      .login-header h1 {
+        font-size: 1.7rem;
+      }
+    }
+  </style>
+</head>
+<body>
+<div class="login-container">
+  <div class="login-header">
+    <h1>SynergyMed</h1>
+    <p>Welcome back! Please sign in to your account.</p>
+  </div>
+
+  <div class="login-form">
+    <!-- Error message -->
+    <div th:if="${error}" class="alert alert-danger">
+      <span th:text="${error}">Invalid credentials</span>
+    </div>
+
+    <!-- Success message -->
+    <div th:if="${message}" class="alert alert-success">
+      <span th:text="${message}">Success message</span>
+    </div>
+
+    <form th:action="@{/login}" method="post" id="loginForm">
+      <div class="form-group">
+        <label for="username">Username</label>
+        <input type="text"
+               id="username"
+               name="username"
+               class="form-control"
+               th:value="${username}"
+               placeholder="Enter your username"
+               required>
+      </div>
+
+      <div class="form-group">
+        <label for="password">Password</label>
+        <input type="password"
+               id="password"
+               name="password"
+               class="form-control"
+               placeholder="Enter your password"
+               required>
+      </div>
+
+      <button type="submit" class="btn-login" id="loginBtn">
+        <span class="btn-text">Sign In</span>
+        <div class="loading"></div>
+      </button>
+    </form>
+
+    <div class="forgot-password">
+      <a href="#" th:href="@{/forgot-password}">Forgot your password?</a>
+    </div>
+  </div>
+
+  <div class="form-footer">
+    <p>&copy; 2024 SynergyMed. All rights reserved.</p>
+  </div>
+</div>
+
+<script>
+  document.getElementById('loginForm').addEventListener('submit', function() {
+    const btn = document.getElementById('loginBtn');
+    const btnText = btn.querySelector('.btn-text');
+    const loading = btn.querySelector('.loading');
+
+    btnText.style.display = 'none';
+    loading.style.display = 'block';
+    btn.disabled = true;
+  });
+
+  // Add input focus animations
+  document.querySelectorAll('.form-control').forEach(input => {
+    input.addEventListener('focus', function() {
+      this.parentElement.classList.add('focused');
+    });
+
+    input.addEventListener('blur', function() {
+      if (!this.value) {
+        this.parentElement.classList.remove('focused');
+      }
+    });
+  });
+</script>
+</body>
+</html>
