Index: backend/src/main/java/medora/controller/AuthController.java
===================================================================
--- backend/src/main/java/medora/controller/AuthController.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/java/medora/controller/AuthController.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,71 @@
+package medora.controller;
+
+import medora.dto.LoginRequest;
+import medora.service.AuthService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/auth")
+@CrossOrigin(origins = "*", maxAge = 3600)
+public class AuthController {
+
+    private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
+
+    private final AuthService authService;
+
+    public AuthController(AuthService authService) {
+        this.authService = authService;
+    }
+
+    /**
+     * UC002 – User Login
+     * Authenticate user and return JWT token
+     */
+    @PostMapping("/login")
+    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
+        try {
+            if (loginRequest.getUsername() == null || loginRequest.getUsername().isBlank()) {
+                return ResponseEntity.badRequest()
+                        .body(Map.of("error", "Username is required"));
+            }
+            if (loginRequest.getPassword() == null || loginRequest.getPassword().isBlank()) {
+                return ResponseEntity.badRequest()
+                        .body(Map.of("error", "Password is required"));
+            }
+
+            Map<String, Object> response = authService.login(loginRequest.getUsername(), loginRequest.getPassword());
+            return ResponseEntity.ok(response);
+        } catch (RuntimeException e) {
+            logger.error("Login error: {}", e.getMessage());
+            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
+                    .body(Map.of("error", e.getMessage()));
+        } catch (Exception e) {
+            logger.error("Unexpected error during login: {}", e.getMessage());
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                    .body(Map.of("error", "Login failed: " + e.getMessage()));
+        }
+    }
+
+    /**
+     * UC003 – User Logout
+     * Client-side logout (token is discarded)
+     */
+    @PostMapping("/logout")
+    public ResponseEntity<?> logout() {
+        return ResponseEntity.ok(Map.of("message", "Logged out successfully"));
+    }
+
+    /**
+     * Health check endpoint
+     */
+    @GetMapping("/health")
+    public ResponseEntity<?> health() {
+        return ResponseEntity.ok(Map.of("status", "healthy"));
+    }
+}
Index: backend/src/main/java/medora/dto/LoginRequest.java
===================================================================
--- backend/src/main/java/medora/dto/LoginRequest.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/java/medora/dto/LoginRequest.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,29 @@
+package medora.dto;
+
+public class LoginRequest {
+    private String username;
+    private String password;
+
+    public LoginRequest() {}
+
+    public LoginRequest(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}
Index: backend/src/main/java/medora/models/domain/User.java
===================================================================
--- backend/src/main/java/medora/models/domain/User.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/java/medora/models/domain/User.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,56 @@
+package medora.models.domain;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Entity
+@Table(name = "users")
+public class User {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "user_id")
+    private Long userId;
+
+    @Column(name = "username", nullable = false, unique = true)
+    private String username;
+
+    @Column(name = "password", nullable = false)
+    private String password;
+
+    @Column(name = "role", nullable = false)
+    private String role; // PATIENT, DOCTOR, LAB_TECHNICIAN, ADMIN
+
+    @Column(name = "first_name")
+    private String firstName;
+
+    @Column(name = "last_name")
+    private String lastName;
+
+    @Column(name = "is_active", nullable = false)
+    private Boolean isActive = true;
+
+    // Foreign key to patient (only for PATIENT role)
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "patient_id")
+    private Patient patient;
+
+    // Foreign key to doctor (only for DOCTOR role)
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "doctor_id")
+    private Doctors doctor;
+
+    public User() {}
+
+    public User(String username, String password, String role, String firstName, String lastName) {
+        this.username = username;
+        this.password = password;
+        this.role = role;
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.isActive = true;
+    }
+}
Index: backend/src/main/java/medora/repository/UserRepository.java
===================================================================
--- backend/src/main/java/medora/repository/UserRepository.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/java/medora/repository/UserRepository.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,13 @@
+package medora.repository;
+
+import medora.models.domain.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface UserRepository extends JpaRepository<User, Long> {
+    Optional<User> findByUsername(String username);
+    boolean existsByUsername(String username);
+}
Index: backend/src/main/java/medora/service/AuthService.java
===================================================================
--- backend/src/main/java/medora/service/AuthService.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/java/medora/service/AuthService.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,84 @@
+package medora.service;
+
+import medora.models.domain.User;
+import medora.repository.UserRepository;
+import medora.util.JwtUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+@Service
+public class AuthService {
+
+    private static final Logger logger = LoggerFactory.getLogger(AuthService.class);
+
+    private final UserRepository userRepository;
+    private final JwtUtil jwtUtil;
+
+    public AuthService(UserRepository userRepository, JwtUtil jwtUtil) {
+        this.userRepository = userRepository;
+        this.jwtUtil = jwtUtil;
+    }
+
+    @Transactional(readOnly = true)
+    public Map<String, Object> login(String username, String password) {
+        if (username == null || username.isBlank()) {
+            throw new IllegalArgumentException("Username is required");
+        }
+        if (password == null || password.isBlank()) {
+            throw new IllegalArgumentException("Password is required");
+        }
+
+        Optional<User> userOpt = userRepository.findByUsername(username);
+        if (userOpt.isEmpty()) {
+            logger.warn("Login attempt with non-existent username: {}", username);
+            throw new RuntimeException("Invalid username or password");
+        }
+
+        User user = userOpt.get();
+
+        if (!user.getIsActive()) {
+            logger.warn("Login attempt with inactive user: {}", username);
+            throw new RuntimeException("User account is inactive");
+        }
+
+        // Simple password check (in production, use BCrypt)
+        if (!user.getPassword().equals(password)) {
+            logger.warn("Failed login attempt for user: {}", username);
+            throw new RuntimeException("Invalid username or password");
+        }
+
+        // Generate JWT token with patientId for patients
+        Long patientId = user.getPatient() != null ? user.getPatient().getPatientId() : null;
+        String token = jwtUtil.generateToken(user.getUsername(), user.getRole(), user.getUserId(), patientId);
+
+        // Return response
+        Map<String, Object> response = new HashMap<>();
+        response.put("token", token);
+        response.put("userId", user.getUserId());
+        response.put("patientId", user.getPatient() != null ? user.getPatient().getPatientId() : null);
+        response.put("username", user.getUsername());
+        response.put("role", user.getRole());
+        response.put("firstName", user.getFirstName());
+        response.put("lastName", user.getLastName());
+
+        logger.info("User logged in successfully: {}", username);
+        return response;
+    }
+
+    @Transactional
+    public void createUser(String username, String password, String role, String firstName, String lastName) {
+        if (userRepository.existsByUsername(username)) {
+            throw new RuntimeException("Username already exists");
+        }
+
+        User user = new User(username, password, role, firstName, lastName);
+        userRepository.save(user);
+        logger.info("User created: {} with role: {}", username, role);
+    }
+}
Index: backend/src/main/java/medora/util/JwtUtil.java
===================================================================
--- backend/src/main/java/medora/util/JwtUtil.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/java/medora/util/JwtUtil.java	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,34 @@
+package medora.util;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.util.Date;
+
+@Component
+public class JwtUtil {
+
+    @Value("${jwt.secret:your-secret-key-change-this-in-production}")
+    private String jwtSecret;
+
+    @Value("${jwt.expiration:86400000}")
+    private long jwtExpirationMs;
+
+    public String generateToken(String username, String role, Long userId, Long patientId) {
+        SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes());
+
+        return Jwts.builder()
+                .subject(username)
+                .claim("role", role)
+                .claim("userId", userId)
+                .claim("patientId", patientId)
+                .issuedAt(new Date())
+                .expiration(new Date(System.currentTimeMillis() + jwtExpirationMs))
+                .signWith(key, SignatureAlgorithm.HS256)
+                .compact();
+    }
+}
Index: backend/src/main/resources/db.migration/V1.1__Create_Daily_Billing_View.sql
===================================================================
--- backend/src/main/resources/db.migration/V1.1__Create_Daily_Billing_View.sql	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/resources/db.migration/V1.1__Create_Daily_Billing_View.sql	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,28 @@
+-- Create materialized view for daily patient billing totals
+CREATE MATERIALIZED VIEW IF NOT EXISTS daily_patient_billing_totals AS
+SELECT
+    p.patient_id,
+    CAST(pp.procedure_date AS DATE) as service_date,
+    COALESCE(SUM(pr.cost), 0) as procedure_cost,
+    0::decimal as lab_test_cost,
+    COALESCE(SUM(pr.cost), 0) as total_cost
+FROM patients p
+LEFT JOIN performed_procedures pp ON p.patient_id = pp.patient_id
+LEFT JOIN procedures pr ON pp.procedure_id = pr.procedure_id
+GROUP BY p.patient_id, CAST(pp.procedure_date AS DATE)
+
+UNION ALL
+
+SELECT
+    p.patient_id,
+    CAST(plt.test_date AS DATE) as service_date,
+    0::decimal as procedure_cost,
+    COALESCE(SUM(lt.cost), 0) as lab_test_cost,
+    COALESCE(SUM(lt.cost), 0) as total_cost
+FROM patients p
+LEFT JOIN performed_lab_tests plt ON p.patient_id = plt.patient_id
+LEFT JOIN lab_tests lt ON plt.test_id = lt.test_id
+GROUP BY p.patient_id, CAST(plt.test_date AS DATE);
+
+-- Create index for better query performance
+CREATE INDEX IF NOT EXISTS idx_daily_billing_patient_date ON daily_patient_billing_totals(patient_id, service_date);
Index: backend/src/main/resources/db.migration/V1.2__Create_Users_Table.sql
===================================================================
--- backend/src/main/resources/db.migration/V1.2__Create_Users_Table.sql	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/resources/db.migration/V1.2__Create_Users_Table.sql	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,32 @@
+-- Create users table
+CREATE TABLE IF NOT EXISTS users (
+    user_id SERIAL PRIMARY KEY,
+    username VARCHAR(255) NOT NULL UNIQUE,
+    password VARCHAR(255) NOT NULL,
+    role VARCHAR(50) NOT NULL,
+    first_name VARCHAR(100),
+    last_name VARCHAR(100),
+    patient_id BIGINT,
+    doctor_id BIGINT,
+    is_active BOOLEAN DEFAULT true,
+    FOREIGN KEY (patient_id) REFERENCES patients(patient_id),
+    FOREIGN KEY (doctor_id) REFERENCES doctors(doctor_id)
+);
+
+-- Create index for username lookup
+CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
+
+-- Insert admin user (password: admin123)
+INSERT INTO users (username, password, role, first_name, last_name, is_active)
+VALUES ('admin', 'admin123', 'ADMIN', 'System', 'Administrator', true)
+ON CONFLICT (username) DO NOTHING;
+
+-- Insert sample patient users (password: password123 for all)
+-- These will be linked to existing patients by EMBG
+INSERT INTO users (username, password, role, first_name, last_name, patient_id, is_active)
+SELECT p.embg, 'password123', 'PATIENT', p.first_name, p.last_name, p.patient_id, true
+FROM patients p
+WHERE NOT EXISTS (
+    SELECT 1 FROM users u WHERE u.username = p.embg
+)
+LIMIT 39;
Index: backend/src/main/resources/db.migration/V2__Add_appointment_fields_to_referrals.sql
===================================================================
--- backend/src/main/resources/db.migration/V2__Add_appointment_fields_to_referrals.sql	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ backend/src/main/resources/db.migration/V2__Add_appointment_fields_to_referrals.sql	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,7 @@
+-- Add appointment_date and appointment_time columns to referrals table
+ALTER TABLE referrals ADD COLUMN IF NOT EXISTS appointment_date DATE;
+ALTER TABLE referrals ADD COLUMN IF NOT EXISTS appointment_time TIME;
+
+-- Set default values for existing rows
+UPDATE referrals SET appointment_date = referral_date + INTERVAL '1 day', appointment_time = '10:00:00'
+WHERE appointment_date IS NULL;
Index: build.log
===================================================================
--- build.log	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
+++ build.log	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -0,0 +1,60 @@
+﻿[INFO] Scanning for projects...
+[INFO] 
+[INFO] ---------------------------< medora:medora4 >---------------------------
+[INFO] Building medora5 0.0.1-SNAPSHOT
+[INFO]   from pom.xml
+[INFO] --------------------------------[ jar ]---------------------------------
+[INFO] 
+[INFO] --- clean:3.4.1:clean (default-clean) @ medora4 ---
+[INFO] Deleting C:\Users\User\Downloads\medora5\target
+[INFO] 
+[INFO] --- resources:3.3.1:resources (default-resources) @ medora4 ---
+[INFO] Copying 4 resources from backend\src\main\resources to target\classes
+[INFO] 
+[INFO] --- compiler:3.14.1:compile (default-compile) @ medora4 ---
+[INFO] Recompiling the module because of changed source code.
+[INFO] Compiling 213 source files with javac [debug parameters release 21] to target\classes
+[INFO] Annotation processing is enabled because one or more processors were found
+  on the class path. A future release of javac may disable annotation processing
+  unless at least one processor is specified by name (-processor), or a search
+  path is specified (--processor-path, --processor-module-path), or annotation
+  processing is enabled explicitly (-proc:only, -proc:full).
+  Use -Xlint:-options to suppress this message.
+  Use -proc:none to disable annotation processing.
+[INFO] -------------------------------------------------------------
+[ERROR] COMPILATION ERROR : 
+[INFO] -------------------------------------------------------------
+[ERROR] /C:/Users/User/Downloads/medora5/backend/src/main/java/medora/service/AuthService.java:[5,19] cannot find symbol
+  symbol:   class JwtUtil
+  location: package medora.util
+[ERROR] /C:/Users/User/Downloads/medora5/backend/src/main/java/medora/service/AuthService.java:[21,19] cannot find symbol
+  symbol:   class JwtUtil
+  location: class medora.service.AuthService
+[ERROR] /C:/Users/User/Downloads/medora5/backend/src/main/java/medora/service/AuthService.java:[23,55] cannot find symbol
+  symbol:   class JwtUtil
+  location: class medora.service.AuthService
+[INFO] 3 errors 
+[INFO] -------------------------------------------------------------
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD FAILURE
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time:  6.298 s
+[INFO] Finished at: 2026-05-23T17:09:26+02:00
+[INFO] ------------------------------------------------------------------------
+[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.14.1:compile (default-compile) on project medora4: Compilation failure: Compilation failure: 
+[ERROR] /C:/Users/User/Downloads/medora5/backend/src/main/java/medora/service/AuthService.java:[5,19] cannot find symbol
+[ERROR]   symbol:   class JwtUtil
+[ERROR]   location: package medora.util
+[ERROR] /C:/Users/User/Downloads/medora5/backend/src/main/java/medora/service/AuthService.java:[21,19] cannot find symbol
+[ERROR]   symbol:   class JwtUtil
+[ERROR]   location: class medora.service.AuthService
+[ERROR] /C:/Users/User/Downloads/medora5/backend/src/main/java/medora/service/AuthService.java:[23,55] cannot find symbol
+[ERROR]   symbol:   class JwtUtil
+[ERROR]   location: class medora.service.AuthService
+[ERROR] -> [Help 1]
+[ERROR] 
+[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
+[ERROR] Re-run Maven using the -X switch to enable full debug logging.
+[ERROR] 
+[ERROR] For more information about the errors and possible solutions, please read the following articles:
+[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
Index: pom.xml
===================================================================
--- pom.xml	(revision daacc57464af180dd43d4e099a91295abd27a397)
+++ pom.xml	(revision 43e476aafc15990f73b0a6b6411d3aa22acc770a)
@@ -12,6 +12,6 @@
     <artifactId>medora4</artifactId>
     <version>0.0.1-SNAPSHOT</version>
-    <name>medora4</name>
-    <description>medora4</description>
+    <name>medora5</name>
+    <description>medora5</description>
     <url/>
     <licenses>
@@ -92,4 +92,23 @@
             <version>7.2.5</version>
         </dependency>
+
+        <!-- JWT for authentication -->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.12.3</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.12.3</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.12.3</version>
+            <scope>runtime</scope>
+        </dependency>
     </dependencies>
 
@@ -114,4 +133,2 @@
 
 </project>
-
-
