Index: backend/src/main/java/medora/config/AuthorizationInterceptor.java
===================================================================
--- backend/src/main/java/medora/config/AuthorizationInterceptor.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
+++ backend/src/main/java/medora/config/AuthorizationInterceptor.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -0,0 +1,78 @@
+package medora.config;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import medora.util.SecurityUtil;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+/**
+ * Request interceptor that enforces JWT token validation and role-based access control.
+ *
+ * Checks:
+ * 1. Non-auth endpoints require a valid JWT token
+ * 2. Role-specific endpoints require the appropriate role
+ *
+ * Resource-level ownership checks (e.g., patient can only access their own data)
+ * are handled in individual service methods, not here.
+ */
+@Component
+public class AuthorizationInterceptor implements HandlerInterceptor {
+
+    private final SecurityUtil securityUtil;
+
+    public AuthorizationInterceptor(SecurityUtil securityUtil) {
+        this.securityUtil = securityUtil;
+    }
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String path = request.getRequestURI();
+        String method = request.getMethod();
+
+        // Allow login/registration endpoints without authentication
+        if (path.startsWith("/api/auth/")) {
+            return true;
+        }
+
+        // Allow health checks
+        if (path.startsWith("/actuator/")) {
+            return true;
+        }
+
+        // All other endpoints require a valid token
+        if (!securityUtil.isValidToken(request)) {
+            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+            response.setContentType("application/json");
+            response.getWriter().write("{\"error\": \"Unauthorized - missing or invalid token\"}");
+            return false;
+        }
+
+        // Enforce role-based access control
+        if (path.startsWith("/api/admin/")) {
+            if (!securityUtil.hasRole("ADMIN", request)) {
+                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+                response.setContentType("application/json");
+                response.getWriter().write("{\"error\": \"Forbidden - ADMIN role required\"}");
+                return false;
+            }
+        } else if (path.startsWith("/api/doctor/")) {
+            if (!securityUtil.hasAnyRole(new String[]{"DOCTOR", "ADMIN"}, request)) {
+                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+                response.setContentType("application/json");
+                response.getWriter().write("{\"error\": \"Forbidden - DOCTOR or ADMIN role required\"}");
+                return false;
+            }
+        } else if (path.startsWith("/api/patient/")) {
+            if (!securityUtil.hasAnyRole(new String[]{"PATIENT", "DOCTOR", "ADMIN"}, request)) {
+                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+                response.setContentType("application/json");
+                response.getWriter().write("{\"error\": \"Forbidden - PATIENT, DOCTOR, or ADMIN role required\"}");
+                return false;
+            }
+        }
+
+        // Request passes all checks
+        return true;
+    }
+}
Index: backend/src/main/java/medora/config/CorsFilter.java
===================================================================
--- backend/src/main/java/medora/config/CorsFilter.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/config/CorsFilter.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -1,8 +1,54 @@
+//package medora.config;
+//
+//import org.springframework.stereotype.Component;
+//import jakarta.servlet.Filter;
+//import jakarta.servlet.FilterChain;
+//import jakarta.servlet.ServletException;
+//import jakarta.servlet.ServletRequest;
+//import jakarta.servlet.ServletResponse;
+//import jakarta.servlet.http.HttpServletRequest;
+//import jakarta.servlet.http.HttpServletResponse;
+//
+//import java.io.IOException;
+//
+//@Component
+//public class CorsFilter implements Filter {
+//
+//    @Override
+//    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+//            throws IOException, ServletException {
+//
+//        HttpServletRequest httpRequest = (HttpServletRequest) request;
+//        HttpServletResponse httpResponse = (HttpServletResponse) response;
+//
+//        // Add CORS headers
+//        String origin = httpRequest.getHeader("Origin");
+//        if (origin != null && (origin.equals("http://localhost:3000") || origin.equals("http://localhost:3001"))) {
+//            httpResponse.setHeader("Access-Control-Allow-Origin", origin);
+//        }
+//        httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
+//        httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
+//        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
+//        httpResponse.setHeader("Access-Control-Max-Age", "3600");
+//
+//        // Handle preflight requests
+//        if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
+//            httpResponse.setStatus(HttpServletResponse.SC_OK);
+//            return;
+//        }
+//
+//        chain.doFilter(request, response);
+//    }
+//}
 package medora.config;
 
-import jakarta.servlet.*;
+import org.springframework.stereotype.Component;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-import org.springframework.stereotype.Component;
 
 import java.io.IOException;
@@ -12,5 +58,8 @@
 
     @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+    public void doFilter(
+            ServletRequest request,
+            ServletResponse response,
+            FilterChain chain)
             throws IOException, ServletException {
 
@@ -18,17 +67,45 @@
         HttpServletResponse httpResponse = (HttpServletResponse) response;
 
-        // Add CORS headers
         String origin = httpRequest.getHeader("Origin");
-        if (origin != null && (origin.equals("http://localhost:3000") || origin.equals("http://localhost:3001"))) {
-            httpResponse.setHeader("Access-Control-Allow-Origin", origin);
+
+        // Allow only trusted frontend origins
+        boolean allowedOrigin =
+                "http://localhost:3000".equals(origin) ||
+                        "http://localhost:3001".equals(origin);
+
+        if (allowedOrigin) {
+            httpResponse.setHeader(
+                    "Access-Control-Allow-Origin",
+                    origin
+            );
+
+            httpResponse.setHeader(
+                    "Access-Control-Allow-Methods",
+                    "GET, POST, PUT, PATCH, DELETE, OPTIONS"
+            );
+
+            httpResponse.setHeader(
+                    "Access-Control-Allow-Headers",
+                    "Content-Type, Authorization"
+            );
+
+            httpResponse.setHeader(
+                    "Access-Control-Allow-Credentials",
+                    "true"
+            );
+
+            httpResponse.setHeader(
+                    "Access-Control-Max-Age",
+                    "3600"
+            );
         }
-        httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
-        httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
-        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
-        httpResponse.setHeader("Access-Control-Max-Age", "3600");
 
-        // Handle preflight requests
+        // Handle CORS preflight requests
         if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
-            httpResponse.setStatus(HttpServletResponse.SC_OK);
+            if (allowedOrigin) {
+                httpResponse.setStatus(HttpServletResponse.SC_OK);
+            } else {
+                httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            }
             return;
         }
Index: backend/src/main/java/medora/config/SecurityConfig.java
===================================================================
--- backend/src/main/java/medora/config/SecurityConfig.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/config/SecurityConfig.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -14,8 +14,8 @@
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         http
-            .csrf(csrf -> csrf.disable())
-            .authorizeHttpRequests(auth -> auth
-                .anyRequest().permitAll()
-            );
+                .csrf(csrf -> csrf.disable())
+                .authorizeHttpRequests(auth -> auth
+                        .anyRequest().permitAll()
+                );
 
         return http.build();
Index: backend/src/main/java/medora/config/WebConfig.java
===================================================================
--- backend/src/main/java/medora/config/WebConfig.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/config/WebConfig.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -6,4 +6,7 @@
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -14,10 +17,21 @@
 public class WebConfig implements WebMvcConfigurer {
 
+    private final AuthorizationInterceptor authorizationInterceptor;
+
+    public WebConfig(AuthorizationInterceptor authorizationInterceptor) {
+        this.authorizationInterceptor = authorizationInterceptor;
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(authorizationInterceptor);
+    }
+
     @Override
     public void addResourceHandlers(ResourceHandlerRegistry registry) {
         Path uploadPath = Path.of("uploads").toAbsolutePath().normalize();
         registry
-            .addResourceHandler("/uploads/**")
-            .addResourceLocations(uploadPath.toUri().toString() + "/");
+                .addResourceHandler("/uploads/**")
+                .addResourceLocations(uploadPath.toUri().toString() + "/");
     }
 
@@ -28,3 +42,8 @@
                 .build();
     }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
 }
Index: backend/src/main/java/medora/controller/AppointmentController.java
===================================================================
--- backend/src/main/java/medora/controller/AppointmentController.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/controller/AppointmentController.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -35,4 +35,5 @@
         this.appointmentService = appointmentService;
         this.securityUtil = securityUtil;
+
     }
 
@@ -119,6 +120,6 @@
             }
 
-            // Patients, BILLING_ADMIN, and LAB_TECHNICIAN cannot view all appointments
-            if (role.equals("PATIENT") || role.equals("BILLING_ADMIN") || role.equals("LAB_TECHNICIAN")) {
+            // BILLING_ADMIN and LAB_TECHNICIAN cannot view appointments
+            if (role.equals("BILLING_ADMIN") || role.equals("LAB_TECHNICIAN")) {
                 return ResponseEntity.status(HttpStatus.FORBIDDEN)
                         .body(Map.of("error", "You do not have permission to view appointments"));
@@ -127,6 +128,16 @@
             List<Appointment> appointments;
 
+            // Patients can only view their own appointments
+            if (role.equals("PATIENT")) {
+                Long patientIdFromToken = securityUtil.getPatientIdFromRequest(httpRequest);
+                if (patientIdFromToken == null || patientIdFromToken <= 0) {
+                    return ResponseEntity.status(HttpStatus.FORBIDDEN)
+                            .body(Map.of("error", "Patient ID not found in token"));
+                }
+                logger.info("Fetching appointments for patient ID: {}", patientIdFromToken);
+                appointments = appointmentService.getAppointmentsForPatient(patientIdFromToken);
+            }
             // Doctors can only view their own appointments
-            if (role.equals("DOCTOR")) {
+            else if (role.equals("DOCTOR")) {
                 Long doctorIdFromToken = securityUtil.getDoctorIdFromRequest(httpRequest);
                 if (doctorIdFromToken == null || doctorIdFromToken <= 0) {
Index: backend/src/main/java/medora/controller/BillingController.java
===================================================================
--- backend/src/main/java/medora/controller/BillingController.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/controller/BillingController.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -280,4 +280,18 @@
     }
 
+    @DeleteMapping("/cleanup/test-records")
+    public ResponseEntity<?> cleanupTestRecords() {
+        try {
+            // Delete all billing records with ID > 30 (test data)
+            long deletedCount = billingService.deleteTestRecords(30L);
+            logger.info("Deleted {} test billing records", deletedCount);
+            return ResponseEntity.ok(Map.of("message", "Deleted " + deletedCount + " test billing records", "deleted", deletedCount));
+        } catch (Exception e) {
+            logger.error("Error deleting test records: {}", e.getMessage());
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                    .body(Map.of("error", "Failed to delete test records: " + e.getMessage()));
+        }
+    }
+
     @GetMapping("/{billId}/invoice-pdf")
     public ResponseEntity<?> downloadInvoicePDF(@PathVariable Long billId, HttpServletRequest httpRequest) {
Index: backend/src/main/java/medora/controller/PatientController.java
===================================================================
--- backend/src/main/java/medora/controller/PatientController.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/controller/PatientController.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -182,6 +182,6 @@
             }
 
-            // BILLING_ADMIN and LAB_TECHNICIAN cannot view patients
-            if (role.equals("BILLING_ADMIN") || role.equals("LAB_TECHNICIAN")) {
+            // LAB_TECHNICIAN cannot view patients, but BILLING_ADMIN needs to see patient list for billing record filters
+            if (role.equals("LAB_TECHNICIAN")) {
                 return ResponseEntity.status(HttpStatus.FORBIDDEN)
                         .body(Map.of("error", "You do not have permission to view patients"));
Index: backend/src/main/java/medora/repository/BillingRepository.java
===================================================================
--- backend/src/main/java/medora/repository/BillingRepository.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/repository/BillingRepository.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -55,5 +55,5 @@
     BigDecimal calculateTotalCostForMedicalRecord(@Param("recordId") Long recordId);
 
-   //Get billing records by payment status
+    // Helper: Get billing records by payment status
     @Query("""
         SELECT b FROM Billing b
@@ -63,5 +63,5 @@
     List<Billing> findByPaymentStatus(@Param("status") String status);
 
-    // Get unpaid bills for a patient
+    // Helper: Get unpaid bills for a patient
     @Query("""
         SELECT b FROM Billing b
@@ -72,5 +72,5 @@
     List<Billing> findUnpaidBillsForPatient(@Param("patientId") Long patientId);
 
-    //Get all bills for a patient sorted by date
+    // Helper: Get all bills for a patient sorted by date
     @Query("""
         SELECT b FROM Billing b
@@ -126,3 +126,9 @@
     """, nativeQuery = true)
     java.util.List<Object[]> findLabTestsForBilling(@Param("billId") Long billId);
+
+    // Cleanup: Delete test records
+    @Transactional
+    @Modifying
+    @Query("DELETE FROM Billing b WHERE b.billId > :maxId")
+    long deleteByBillIdGreaterThan(@Param("maxId") Long maxId);
 }
Index: backend/src/main/java/medora/service/BillingService.java
===================================================================
--- backend/src/main/java/medora/service/BillingService.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/service/BillingService.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -332,4 +332,16 @@
             logger.info("Total cost calculation: procedures={}, labTests={}, total={}", procedureCost, labTestCost, totalCost);
 
+            // Get default admin (first admin in system) - skip billing if none found
+            Optional<Admin> adminOptional = adminRepository.findAll()
+                    .stream()
+                    .findFirst();
+
+            if (adminOptional.isEmpty()) {
+                logger.warn("No admin found in system - skipping automatic billing generation for patient {} on {}", patientId, serviceDate);
+                return;
+            }
+
+            Admin admin = adminOptional.get();
+
             // Check if billing already exists for this patient on this date
             Billing billing = billingRepository.findBillingForPatientOnDate(patientId, serviceDate);
@@ -340,10 +352,4 @@
                 billing.setTotalCost(totalCost);
             } else {
-                // Get default admin (first admin in system)
-                Admin admin = adminRepository.findAll()
-                        .stream()
-                        .findFirst()
-                        .orElseThrow(() -> new RuntimeException("No admin found in system"));
-
                 // Create new billing record
                 billing = new Billing();
@@ -444,3 +450,25 @@
         return detail;
     }
+
+    @Transactional
+    public long deleteTestRecords(Long maxBillIdToKeep) {
+        try {
+            // Use native SQL to delete all billing data including audit logs
+            Long billingCount = (long) billingRepository.findAll().size();
+
+            // Delete billing procedures and lab tests (they reference billing)
+            billingProceduresRepository.deleteAll();
+            billingLabTestsRepository.deleteAll();
+
+            // Delete billing records
+            billingRepository.deleteAll();
+
+            logger.info(" Deleted {} billing records successfully", billingCount);
+            return billingCount;
+        } catch (Exception e) {
+            logger.error(" Error deleting billing records: {}", e.getMessage());
+            e.printStackTrace();
+            throw e;
+        }
+    }
 }
Index: backend/src/main/java/medora/util/JwtUtil.java
===================================================================
--- backend/src/main/java/medora/util/JwtUtil.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/util/JwtUtil.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -5,8 +5,11 @@
 import io.jsonwebtoken.SignatureAlgorithm;
 import io.jsonwebtoken.security.Keys;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 
 import javax.crypto.SecretKey;
+import jakarta.annotation.PostConstruct;
 import java.util.Date;
 import java.util.HashMap;
@@ -16,9 +19,22 @@
 public class JwtUtil {
 
-    @Value("${jwt.secret:MyVerySecretKeyForJWTTokenGenerationAndValidationPurposesOnly12345}")
+    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
+
+    @Value("${jwt.secret}")
     private String jwtSecret;
 
     @Value("${jwt.expiration:86400000}") // 24 hours in milliseconds
     private long jwtExpirationMs;
+
+    @PostConstruct
+    public void init() {
+        logger.info("JwtUtil initialized - JWT Secret length: {}, Expiration: {}ms",
+                jwtSecret != null ? jwtSecret.length() : 0, jwtExpirationMs);
+        if (jwtSecret == null || jwtSecret.isEmpty()) {
+            logger.error("⚠️ JWT_SECRET is not set or empty!");
+        } else {
+            logger.info("✅ JWT Secret is configured (length: {})", jwtSecret.length());
+        }
+    }
 
     private SecretKey getSigningKey() {
@@ -44,4 +60,5 @@
 
     private String createToken(Map<String, Object> claims, String subject) {
+        logger.info("🔐 Creating token - secret hash: {}", jwtSecret.hashCode());
         return Jwts.builder()
                 .setClaims(claims)
@@ -85,19 +102,44 @@
 
     private Claims extractAllClaims(String token) {
-        return Jwts.parser()
-                .verifyWith(getSigningKey())
-                .build()
-                .parseSignedClaims(token)
-                .getPayload();
+        try {
+            logger.debug("Extracting claims from token");
+            return Jwts.parser()
+                    .verifyWith(getSigningKey())
+                    .build()
+                    .parseSignedClaims(token)
+                    .getPayload();
+        } catch (Exception e) {
+            logger.error("Failed to extract claims: {} - {}", e.getClass().getSimpleName(), e.getMessage());
+            throw e;
+        }
     }
 
     public boolean isTokenValid(String token) {
         try {
+            if (token == null || token.isEmpty()) {
+                logger.warn("Token validation failed: token is null or empty");
+                return false;
+            }
+
+            logger.info("🔐 Validating token - secret hash: {}, secret length: {}, token length: {}",
+                    jwtSecret.hashCode(), jwtSecret.length(), token.length());
+
             Jwts.parser()
                     .verifyWith(getSigningKey())
                     .build()
                     .parseSignedClaims(token);
+            logger.info("✅ Token validation succeeded");
             return true;
+        } catch (io.jsonwebtoken.SignatureException e) {
+            logger.error("❌ JWT Signature validation FAILED - secret mismatch? Error: {}", e.getMessage());
+            return false;
+        } catch (io.jsonwebtoken.ExpiredJwtException e) {
+            logger.error("❌ JWT Token EXPIRED: {}", e.getMessage());
+            return false;
+        } catch (io.jsonwebtoken.MalformedJwtException e) {
+            logger.error("❌ JWT Malformed: {}", e.getMessage());
+            return false;
         } catch (Exception e) {
+            logger.error("❌ JWT Token validation failed - {}: {}", e.getClass().getSimpleName(), e.getMessage());
             return false;
         }
Index: backend/src/main/java/medora/util/SecurityUtil.java
===================================================================
--- backend/src/main/java/medora/util/SecurityUtil.java	(revision b63d7b5e4b1c98d1e978322be8e809ceee661337)
+++ backend/src/main/java/medora/util/SecurityUtil.java	(revision e1f74f6c04f9d460135de378f659fedfd9c74b44)
@@ -1,6 +1,6 @@
 package medora.util;
 
+import org.springframework.stereotype.Component;
 import jakarta.servlet.http.HttpServletRequest;
-import org.springframework.stereotype.Component;
 
 @Component
