Index: ckend/env
===================================================================
--- backend/env	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ 	(revision )
@@ -1,54 +1,0 @@
-# ============================================
-# REQUIRED FOR STARTUP - Database Connection
-# ============================================
-# PostgreSQL connection parameters
-# Make sure your SSH tunnel is running (tunnel_scripta.sh) before starting the app
-SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:9999/db_202526z_va_prj_trekr
-SPRING_DATASOURCE_USERNAME=db_202526z_va_prj_trekr_owner
-SPRING_DATASOURCE_PASSWORD=39ee816617c3
-
-# ============================================
-# REQUIRED FOR STARTUP - JWT Secret Key
-# ============================================
-# Used for signing/verifying JWT tokens in authentication
-# Generate a secure random string (at least 256 bits/32 characters)
-# For development, you can use this temporary value, but CHANGE IT in production!
-# To generate: openssl rand -base64 32
-JWT_CONFIG_SECRET=dev-temporary-secret-key-change-in-production-min-32-chars
-
-# ============================================
-# OPTIONAL - AWS S3 (File Storage)
-# ============================================
-# Only needed if you're storing files/images in AWS S3
-# Get these from: AWS Console -> IAM -> Users -> Create Access Key
-# Or: AWS Console -> S3 -> Your Bucket -> Properties
-AWS_S3_REGION=
-AWS_S3_BUCKET_NAME=
-AWS_S3_ACCESS_KEY=
-AWS_S3_SECRET_KEY=
-
-# ============================================
-# OPTIONAL - Email Service (SMTP)
-# ============================================
-# Only needed if you're sending emails (password reset, notifications, etc.)
-# Options:
-#   - Gmail: smtp.gmail.com, port 587
-#   - Outlook: smtp-mail.outlook.com, port 587
-#   - SendGrid: smtp.sendgrid.net, port 587
-#   - Mailtrap (testing): smtp.mailtrap.io, port 2525
-EMAIL_HOST=
-EMAIL_PORT=
-EMAIL_USERNAME=
-EMAIL_PASSWORD=
-
-# ============================================
-# OPTIONAL - Google OAuth (Social Login)
-# ============================================
-# Only needed if you want users to sign in with Google
-# Get these from: https://console.cloud.google.com/
-#   1. Create a project
-#   2. Enable Google+ API
-#   3. Create OAuth 2.0 credentials
-#   4. Add authorized redirect URI: http://localhost:8080/login/oauth2/code/google
-GOOGLE_CLIENT_ID=
-GOOGLE_CLIENT_SECRET=
Index: backend/pom.xml
===================================================================
--- backend/pom.xml	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/pom.xml	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -99,4 +99,10 @@
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
Index: backend/src/main/java/com/trekr/backend/BackendApplication.java
===================================================================
--- backend/src/main/java/com/trekr/backend/BackendApplication.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/BackendApplication.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -12,12 +12,12 @@
         // This ensures system properties are available for ${} placeholder resolution
         Dotenv dotenv = loadEnvironmentVariables();
-        
+
         // Create SpringApplication instance
         SpringApplication app = new SpringApplication(BackendApplication.class);
-        
+
         // Set system properties and default properties from .env file
         if (dotenv != null) {
             java.util.Map<String, Object> defaultProps = new java.util.HashMap<>();
-            
+
             dotenv.entries().forEach(entry -> {
                 String key = entry.getKey();
@@ -30,5 +30,5 @@
                 }
             });
-            
+
             // Set all default properties at once
             if (!defaultProps.isEmpty()) {
@@ -36,53 +36,42 @@
             }
         }
-        
+
         app.run(args);
     }
-    
+
     private static Dotenv loadEnvironmentVariables() {
         Dotenv dotenv = null;
-        try {
-            // Try current directory (backend/)
-            dotenv = Dotenv.configure()
-                    .directory("./")
-                    .ignoreIfMissing()
-                    .load();
-        } catch (Exception e) {
-            // Try parent directory if current doesn't work
+
+        // Prefer a standard ".env" file, but also support the existing "env" file.
+        // We search multiple locations so it works whether the app is launched from:
+        // - backend/ (common in IDE)
+        // - repo root (common when running a built JAR)
+        String[][] candidates = new String[][] {
+                { "./", ".env" },
+                { "./", "env" },
+                { "./backend", ".env" },
+                { "./backend", "env" },
+                { "../", ".env" },
+                { "../", "env" },
+                { "../backend", ".env" },
+                { "../backend", "env" },
+        };
+
+        for (String[] candidate : candidates) {
             try {
-                dotenv = Dotenv.configure()
-                        .directory("../")
+                Dotenv loaded = Dotenv.configure()
+                        .directory(candidate[0])
+                        .filename(candidate[1])
                         .ignoreIfMissing()
                         .load();
-            } catch (Exception e2) {
-                System.err.println("WARNING: Could not load .env file. Make sure .env exists in backend/ directory.");
-                System.err.println("Error: " + e2.getMessage());
-                return null;
+                if (loaded != null && !loaded.entries().isEmpty()) {
+                    dotenv = loaded;
+                    break;
+                }
+            } catch (Exception ignored) {
+                // keep searching
             }
         }
-        
-        // Verify critical properties are loaded
-        if (dotenv != null) {
-            String url = dotenv.get("SPRING_DATASOURCE_URL");
-            String username = dotenv.get("SPRING_DATASOURCE_USERNAME");
-            String password = dotenv.get("SPRING_DATASOURCE_PASSWORD");
-            
-            if (url != null && !url.trim().isEmpty()) {
-                System.out.println("✓ Database URL loaded from .env");
-            } else {
-                System.err.println("✗ ERROR: SPRING_DATASOURCE_URL not found in .env file!");
-            }
-            if (username != null && !username.trim().isEmpty()) {
-                System.out.println("✓ Database username loaded from .env");
-            } else {
-                System.err.println("✗ ERROR: SPRING_DATASOURCE_USERNAME not found in .env file!");
-            }
-            if (password != null && !password.trim().isEmpty()) {
-                System.out.println("✓ Database password loaded from .env");
-            } else {
-                System.err.println("✗ ERROR: SPRING_DATASOURCE_PASSWORD not found in .env file!");
-            }
-        }
-        
+
         return dotenv;
     }
Index: backend/src/main/java/com/trekr/backend/config/SecurityConfig.java
===================================================================
--- backend/src/main/java/com/trekr/backend/config/SecurityConfig.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/config/SecurityConfig.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -3,4 +3,5 @@
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -8,4 +9,9 @@
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
 
 @Configuration
@@ -17,9 +23,23 @@
         http
                 .csrf(AbstractHttpConfigurer::disable)
+                .cors(Customizer.withDefaults())
                 .authorizeHttpRequests(auth -> auth
                         .requestMatchers("/api/auth/**").permitAll()
-                        .anyRequest().authenticated()
-                );
+                        .anyRequest().authenticated());
         return http.build();
+    }
+
+    @Bean
+    public CorsConfigurationSource corsConfigurationSource() {
+        CorsConfiguration config = new CorsConfiguration();
+        config.setAllowedOrigins(List.of(
+                "http://localhost:5173",
+                "http://127.0.0.1:5173"));
+        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
+        config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
+
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", config);
+        return source;
     }
 
Index: backend/src/main/java/com/trekr/backend/controller/AuthController.java
===================================================================
--- backend/src/main/java/com/trekr/backend/controller/AuthController.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/controller/AuthController.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -6,5 +6,4 @@
 import com.trekr.backend.service.AuthService;
 import jakarta.validation.Valid;
-import lombok.RequiredArgsConstructor;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -13,9 +12,12 @@
 @RestController
 @RequestMapping("/api/auth")
-@RequiredArgsConstructor
 public class AuthController {
-    
+
     private final AuthService authService;
-    
+
+    public AuthController(AuthService authService) {
+        this.authService = authService;
+    }
+
     @PostMapping("/register")
     public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {
@@ -23,5 +25,5 @@
         return ResponseEntity.status(HttpStatus.CREATED).body(response);
     }
-    
+
     @PostMapping("/login")
     public ResponseEntity<AuthResponse> login(@Valid @RequestBody LoginRequest request) {
Index: backend/src/main/java/com/trekr/backend/dto/auth/AuthResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/auth/AuthResponse.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/dto/auth/AuthResponse.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,11 +1,4 @@
 package com.trekr.backend.dto.auth;
 
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@NoArgsConstructor
-@AllArgsConstructor
 public class AuthResponse {
     private String token;
@@ -14,3 +7,54 @@
     private String username;
     private String email;
+
+    public AuthResponse() {
+    }
+
+    public AuthResponse(String token, String type, Long userId, String username, String email) {
+        this.token = token;
+        this.type = type;
+        this.userId = userId;
+        this.username = username;
+        this.email = email;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
 }
Index: backend/src/main/java/com/trekr/backend/dto/auth/LoginRequest.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/auth/LoginRequest.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/dto/auth/LoginRequest.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -2,13 +2,30 @@
 
 import jakarta.validation.constraints.NotBlank;
-import lombok.Data;
 
-@Data
 public class LoginRequest {
-    
+
     @NotBlank(message = "Username or email is required")
     private String usernameOrEmail;
-    
+
     @NotBlank(message = "Password is required")
     private String password;
+
+    public LoginRequest() {
+    }
+
+    public String getUsernameOrEmail() {
+        return usernameOrEmail;
+    }
+
+    public void setUsernameOrEmail(String usernameOrEmail) {
+        this.usernameOrEmail = usernameOrEmail;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
 }
Index: backend/src/main/java/com/trekr/backend/dto/auth/RegisterRequest.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/auth/RegisterRequest.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/dto/auth/RegisterRequest.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -4,19 +4,44 @@
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.Size;
-import lombok.Data;
 
-@Data
 public class RegisterRequest {
-    
+
     @NotBlank(message = "Email is required")
     @Email(message = "Email must be valid")
     private String email;
-    
+
     @NotBlank(message = "Username is required")
     @Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
     private String username;
-    
+
     @NotBlank(message = "Password is required")
     @Size(min = 6, message = "Password must be at least 6 characters")
     private String password;
+
+    public RegisterRequest() {
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    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/com/trekr/backend/entity/User.java
===================================================================
--- backend/src/main/java/com/trekr/backend/entity/User.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/entity/User.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -2,30 +2,63 @@
 
 import jakarta.persistence.*;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
 
 @Entity
 @Table(name = "USERS", schema = "trekr")
-@Getter
-@Setter
-@NoArgsConstructor
-@AllArgsConstructor
 public class User {
-    
+
     @Id
-    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
-    @SequenceGenerator(name = "user_seq", sequenceName = "user_id_seq", allocationSize = 1)
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Column(name = "user_id")
     private Long userId;
-    
+
     @Column(name = "email", nullable = false, unique = true)
     private String email;
-    
+
     @Column(name = "username", nullable = false, unique = true)
     private String username;
-    
+
     @Column(name = "password", nullable = false)
     private String password;
+
+    public User() {
+    }
+
+    public User(Long userId, String email, String username, String password) {
+        this.userId = userId;
+        this.email = email;
+        this.username = username;
+        this.password = password;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    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/com/trekr/backend/resources/application.properties
===================================================================
--- backend/src/main/java/com/trekr/backend/resources/application.properties	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/resources/application.properties	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,66 +1,4 @@
-# application.properties
+### NOTE
+### This file is intentionally NOT used by Spring Boot.
+### The active config is in: src/main/resources/application.properties
 
-# General app config
-spring.application.name=trekr
-
-#spring.datasource.hikari.data-source-properties.useServerPrepStmts=false
-#spring.datasource.hikari.data-source-properties.prepareThreshold=0
-
-logging.level.org.springframework.security=DEBUG
-
-# JPA & Hibernate
-spring.jpa.hibernate.ddl-auto=update
-spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
-spring.jpa.show-sql=false
-spring.jpa.properties.hibernate.format_sql=true
-spring.jpa.properties.hibernate.default_schema=trekr
-
-#logging.level.org.hibernate.SQL=DEBUG
-#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
-
-
-# HikariCP settings
-spring.datasource.hikari.pool-name=trekrHikariPool
-
-# Maximum connections in the pool
-# Rule of thumb: (CPU cores × 2) + effective_spindle_count
-# For most apps: 10-20 is good
-spring.datasource.hikari.maximum-pool-size=20
-
-# Minimum idle connections always ready
-# Keep at least 5 ready for quick response
-spring.datasource.hikari.minimum-idle=5
-
-# How long to wait for a connection (before blocking the request) - 30sec
-spring.datasource.hikari.connection-timeout=30000
-
-# How long a connection can sit idle (before being closed) - 10min
-spring.datasource.hikari.idle-timeout=600000
-
-# Maximum lifetime of a connection (before being replaced with a new one) - 30min
-# Prevents stale connections
-spring.datasource.hikari.max-lifetime=1800000
-
-
-# JWT Configuration (supports JWT_SECRET or JWT_CONFIG_SECRET from .env)
-jwt.secret=${JWT_SECRET:${JWT_CONFIG_SECRET:dev-fallback-secret-min-32-chars-required}}
-
-# Base API path
-api.base.path=/api
-api.admin.path=/api/admin
-
-#spring.profiles.active=dev
-
-# Max file size
-server.tomcat.max-part-count=50
-
-logging.level.org.springframework.web=DEBUG
-logging.level.org.apache.coyote.http11=DEBUG
-
-
-# PostgreSQL connection
-spring.datasource.driver-class-name=org.postgresql.Driver
-spring.datasource.url=${SPRING_DATASOURCE_URL}
-spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
-spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
-
Index: backend/src/main/java/com/trekr/backend/security/UserPrincipal.java
===================================================================
--- backend/src/main/java/com/trekr/backend/security/UserPrincipal.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/security/UserPrincipal.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -2,5 +2,4 @@
 
 import lombok.Getter;
-import lombok.RequiredArgsConstructor;
 
 import java.io.Serializable;
@@ -11,8 +10,15 @@
  */
 @Getter
-@RequiredArgsConstructor
 public class UserPrincipal implements Serializable {
+    private static final long serialVersionUID = 1L;
+
     private final Long userId;
     private final String username;
     private final String email;
+
+    public UserPrincipal(Long userId, String username, String email) {
+        this.userId = userId;
+        this.username = username;
+        this.email = email;
+    }
 }
Index: backend/src/main/java/com/trekr/backend/service/AuthService.java
===================================================================
--- backend/src/main/java/com/trekr/backend/service/AuthService.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/service/AuthService.java	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -6,5 +6,4 @@
 import com.trekr.backend.entity.User;
 import com.trekr.backend.repository.UserRepository;
-import lombok.RequiredArgsConstructor;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
@@ -12,5 +11,4 @@
 
 @Service
-@RequiredArgsConstructor
 public class AuthService {
 
@@ -18,4 +16,10 @@
     private final PasswordEncoder passwordEncoder;
     private final JwtService jwtService;
+
+    public AuthService(UserRepository userRepository, PasswordEncoder passwordEncoder, JwtService jwtService) {
+        this.userRepository = userRepository;
+        this.passwordEncoder = passwordEncoder;
+        this.jwtService = jwtService;
+    }
 
     @Transactional
Index: backend/src/main/resources/application.properties
===================================================================
--- backend/src/main/resources/application.properties	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/resources/application.properties	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,1 +1,42 @@
-spring.application.name=backend
+spring.application.name=trekr
+
+# ============================
+# JPA & Hibernate
+# ============================
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.show-sql=false
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.default_schema=trekr
+spring.jpa.properties.hibernate.hbm2ddl.create_namespaces=true
+
+# ============================
+# PostgreSQL connection (loaded from backend/.env or backend/env)
+# ============================
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.datasource.url=${SPRING_DATASOURCE_URL}
+spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
+spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
+
+# ============================
+# JWT Configuration (supports JWT_SECRET or JWT_CONFIG_SECRET)
+# ============================
+jwt.secret=${JWT_SECRET:${JWT_CONFIG_SECRET:dev-fallback-secret-min-32-chars-required}}
+
+# ============================
+# Logging
+# ============================
+logging.level.org.springframework.security=DEBUG
+
+# ============================
+# HikariCP
+# ============================
+spring.datasource.hikari.pool-name=trekrHikariPool
+spring.datasource.hikari.maximum-pool-size=20
+spring.datasource.hikari.minimum-idle=5
+spring.datasource.hikari.connection-timeout=30000
+spring.datasource.hikari.idle-timeout=600000
+spring.datasource.hikari.max-lifetime=1800000
+
+# Max multipart parts
+server.tomcat.max-part-count=50
Index: backend/src/test/resources/application.properties
===================================================================
--- backend/src/test/resources/application.properties	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
+++ backend/src/test/resources/application.properties	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -0,0 +1,21 @@
+# Test profile: use in-memory DB so tests don't require external Postgres
+
+spring.datasource.url=jdbc:h2:mem:trekr;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE
+spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+
+spring.jpa.hibernate.ddl-auto=create-drop
+spring.jpa.show-sql=false
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.default_schema=trekr
+spring.jpa.properties.hibernate.hbm2ddl.create_namespaces=false
+
+# Ensure schema exists before Hibernate runs DDL
+spring.sql.init.mode=always
+
+# JWT secret for tests
+jwt.secret=test-secret-min-32-chars-000000000000
+
+# Keep noise down in tests
+logging.level.org.springframework.security=INFO
Index: backend/src/test/resources/schema.sql
===================================================================
--- backend/src/test/resources/schema.sql	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
+++ backend/src/test/resources/schema.sql	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -0,0 +1,2 @@
+CREATE SCHEMA IF NOT EXISTS trekr;
+SET SCHEMA trekr;
Index: db-scripts/ddl.sql
===================================================================
--- db-scripts/ddl.sql	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ db-scripts/ddl.sql	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -20,5 +20,5 @@
 
 CREATE TABLE USERS (
-    user_id BIGINT PRIMARY KEY,
+    user_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
     email TEXT NOT NULL,
     username TEXT NOT NULL,
Index: db-scripts/dml.sql
===================================================================
--- db-scripts/dml.sql	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ db-scripts/dml.sql	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -32,4 +32,12 @@
 (29, 'petra@example.com', 'petra_kostova', '1234'),
 (30, 'vlado@example.com', 'vlado_ilievski', '1234');
+
+-- If USERS.user_id is an IDENTITY column, ensure its backing sequence is advanced
+-- past the inserted dummy ids so future inserts (e.g., app registration) don't
+-- try to reuse ids and fail with duplicate key errors.
+SELECT setval(
+	pg_get_serial_sequence('trekr.users', 'user_id'),
+	(SELECT COALESCE(MAX(user_id), 0) FROM trekr.users)
+);
 
 INSERT INTO FINANCE_USERS (user_id, spending_budget, saving_budget, investing_budget, donation_budget, credit) VALUES
Index: frontend/src/App.jsx
===================================================================
--- frontend/src/App.jsx	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ frontend/src/App.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,6 +1,34 @@
+import { Navigate, Route, Routes } from "react-router-dom";
+
 import LandingPage from "./pages/LandingPage/LandingPage.jsx";
+import Login from "./pages/Login/Login.jsx";
+import Register from "./pages/Register/Register.jsx";
+import Dashboard from "./pages/Dashboard.jsx";
+
+function RequireAuth({ children }) {
+  const token = localStorage.getItem("authToken");
+  if (!token) return <Navigate to="/login" replace />;
+  return children;
+}
 
 function App() {
-  return <LandingPage />;
+  return (
+    <div data-theme="forest" className="min-h-screen w-full">
+      <Routes>
+        <Route path="/" element={<LandingPage />} />
+        <Route path="/login" element={<Login />} />
+        <Route path="/register" element={<Register />} />
+        <Route
+          path="/dashboard"
+          element={
+            <RequireAuth>
+              <Dashboard />
+            </RequireAuth>
+          }
+        />
+        <Route path="*" element={<Navigate to="/" replace />} />
+      </Routes>
+    </div>
+  );
 }
 
Index: frontend/src/api/axios.js
===================================================================
--- frontend/src/api/axios.js	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ frontend/src/api/axios.js	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,6 +1,16 @@
 import axios from "axios";
 
-export default axios.create({
-  baseURL: "http://localhost:8080/api",
-  withCredentials: true, // important if using cookies
+const api = axios.create({
+  baseURL: import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8080/api",
 });
+
+api.interceptors.request.use((config) => {
+  const token = localStorage.getItem("authToken");
+  if (token) {
+    config.headers = config.headers ?? {};
+    config.headers.Authorization = `Bearer ${token}`;
+  }
+  return config;
+});
+
+export default api;
Index: frontend/src/main.jsx
===================================================================
--- frontend/src/main.jsx	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ frontend/src/main.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,10 +1,14 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './index.css'
-import App from './App.jsx'
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import { BrowserRouter } from "react-router-dom";
 
-createRoot(document.getElementById('root')).render(
+import "./index.css";
+import App from "./App.jsx";
+
+createRoot(document.getElementById("root")).render(
   <StrictMode>
-    <App />
+    <BrowserRouter>
+      <App />
+    </BrowserRouter>
   </StrictMode>,
-)
+);
Index: frontend/src/pages/Dashboard.jsx
===================================================================
--- frontend/src/pages/Dashboard.jsx	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ frontend/src/pages/Dashboard.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,2 +1,61 @@
-// hello world
-import React from "react";
+import React, { useMemo } from "react";
+import { Link, useNavigate } from "react-router-dom";
+
+const Dashboard = () => {
+  const navigate = useNavigate();
+
+  const user = useMemo(() => {
+    try {
+      const raw = localStorage.getItem("authUser");
+      return raw ? JSON.parse(raw) : null;
+    } catch {
+      return null;
+    }
+  }, []);
+
+  const onLogout = () => {
+    localStorage.removeItem("authToken");
+    localStorage.removeItem("authUser");
+    navigate("/", { replace: true });
+  };
+
+  return (
+    <div className="min-h-screen p-6">
+      <div className="max-w-3xl mx-auto">
+        <div className="flex items-center justify-between gap-3">
+          <h1 className="text-2xl font-bold">Dashboard</h1>
+          <div className="flex items-center gap-2">
+            <Link className="btn btn-ghost" to="/">
+              Landing
+            </Link>
+            <button className="btn btn-outline" onClick={onLogout}>
+              Logout
+            </button>
+          </div>
+        </div>
+
+        <div className="mt-6 card bg-base-200 border border-base-300">
+          <div className="card-body">
+            <h2 className="card-title">Account</h2>
+            <div className="text-sm leading-7">
+              <div>
+                <span className="opacity-70">Username:</span>{" "}
+                <span className="font-medium">{user?.username ?? "—"}</span>
+              </div>
+              <div>
+                <span className="opacity-70">Email:</span>{" "}
+                <span className="font-medium">{user?.email ?? "—"}</span>
+              </div>
+              <div>
+                <span className="opacity-70">User ID:</span>{" "}
+                <span className="font-medium">{user?.userId ?? "—"}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default Dashboard;
Index: frontend/src/pages/LandingPage/components/Hero.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/Hero.jsx	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ frontend/src/pages/LandingPage/components/Hero.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,5 +1,20 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
+import { Link } from "react-router-dom";
 
 const Hero = () => {
+  const [isAuthed, setIsAuthed] = useState(() =>
+    Boolean(localStorage.getItem("authToken")),
+  );
+
+  useEffect(() => {
+    const onStorage = (e) => {
+      if (e.key === "authToken") {
+        setIsAuthed(Boolean(e.newValue));
+      }
+    };
+    window.addEventListener("storage", onStorage);
+    return () => window.removeEventListener("storage", onStorage);
+  }, []);
+
   return (
     <div
@@ -21,5 +36,10 @@
             consistently, and see your progress in one place.
           </p>
-          <button className="btn btn-primary">Get started</button>
+          <Link
+            className="btn btn-primary"
+            to={isAuthed ? "/dashboard" : "/register"}
+          >
+            {isAuthed ? "Go to dashboard" : "Get started"}
+          </Link>
         </div>
       </div>
Index: frontend/src/pages/LandingPage/components/NavbarLanding.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/NavbarLanding.jsx	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ frontend/src/pages/LandingPage/components/NavbarLanding.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -1,17 +1,54 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
 import logo from "../../../assets/logo.png";
 
 const NavbarLanding = () => {
+  const navigate = useNavigate();
+  const [isAuthed, setIsAuthed] = useState(() =>
+    Boolean(localStorage.getItem("authToken")),
+  );
+
+  useEffect(() => {
+    const onStorage = (e) => {
+      if (e.key === "authToken") {
+        setIsAuthed(Boolean(e.newValue));
+      }
+    };
+    window.addEventListener("storage", onStorage);
+    return () => window.removeEventListener("storage", onStorage);
+  }, []);
+
+  const onLogout = () => {
+    localStorage.removeItem("authToken");
+    localStorage.removeItem("authUser");
+    setIsAuthed(false);
+    navigate("/", { replace: true });
+  };
+
   return (
     <div className="navbar bg-neutral shadow-sm">
       <div className="navbar-start">
-        <a className="btn btn-ghost" aria-label="Trekr home">
+        <Link className="btn btn-ghost" aria-label="Trekr home" to="/">
           <img src={logo} alt="Trekr" className="h-12 w-auto rounded-2xl" />
-        </a>
+        </Link>
       </div>
       <div className="navbar-end">
-        <button className="btn bg-black text-green-200 border-black hover:bg-black/90 hover:border-black px-8 mr-6">
-          Start Tracking
-        </button>
+        {isAuthed ? (
+          <div className="flex items-center gap-2 mr-6">
+            <Link className="btn btn-primary" to="/dashboard">
+              Dashboard
+            </Link>
+            <button className="btn btn-outline" onClick={onLogout}>
+              Logout
+            </button>
+          </div>
+        ) : (
+          <Link
+            className="btn bg-black text-green-200 border-black hover:bg-black/90 hover:border-black px-8 mr-6"
+            to="/login"
+          >
+            Start Tracking
+          </Link>
+        )}
       </div>
     </div>
Index: frontend/src/pages/Login/Login.jsx
===================================================================
--- frontend/src/pages/Login/Login.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
+++ frontend/src/pages/Login/Login.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -0,0 +1,99 @@
+import React, { useEffect, useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+
+import api from "../../api/axios";
+
+const Login = () => {
+  const navigate = useNavigate();
+  const [usernameOrEmail, setUsernameOrEmail] = useState("");
+  const [password, setPassword] = useState("");
+  const [error, setError] = useState("");
+  const [isSubmitting, setIsSubmitting] = useState(false);
+
+  useEffect(() => {
+    const token = localStorage.getItem("authToken");
+    if (token) navigate("/dashboard", { replace: true });
+  }, [navigate]);
+
+  const onSubmit = async (e) => {
+    e.preventDefault();
+    setError("");
+    setIsSubmitting(true);
+    try {
+      const res = await api.post("/auth/login", {
+        usernameOrEmail,
+        password,
+      });
+      localStorage.setItem("authToken", res.data.token);
+      localStorage.setItem(
+        "authUser",
+        JSON.stringify({
+          userId: res.data.userId,
+          username: res.data.username,
+          email: res.data.email,
+        }),
+      );
+      navigate("/dashboard");
+    } catch (err) {
+      const message =
+        err?.response?.data?.message ||
+        err?.response?.data?.errors?.usernameOrEmail ||
+        "Login failed";
+      setError(message);
+    } finally {
+      setIsSubmitting(false);
+    }
+  };
+
+  return (
+    <div className="min-h-screen flex items-center justify-center p-4">
+      <form
+        onSubmit={onSubmit}
+        className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4"
+      >
+        <legend className="fieldset-legend">Login</legend>
+
+        <label className="label">Username or Email</label>
+        <input
+          type="text"
+          className="input"
+          placeholder="Username or Email"
+          value={usernameOrEmail}
+          onChange={(e) => setUsernameOrEmail(e.target.value)}
+          autoComplete="username"
+          required
+        />
+
+        <label className="label">Password</label>
+        <input
+          type="password"
+          className="input"
+          placeholder="Password"
+          value={password}
+          onChange={(e) => setPassword(e.target.value)}
+          autoComplete="current-password"
+          required
+        />
+
+        {error ? <p className="text-error mt-2 text-sm">{error}</p> : null}
+
+        <button
+          className="btn btn-neutral mt-4"
+          type="submit"
+          disabled={isSubmitting}
+        >
+          {isSubmitting ? "Logging in..." : "Login"}
+        </button>
+
+        <p className="mt-3 text-sm">
+          Don&apos;t have an account?{" "}
+          <Link className="link link-primary" to="/register">
+            Register
+          </Link>
+        </p>
+      </form>
+    </div>
+  );
+};
+
+export default Login;
Index: frontend/src/pages/Register/Register.jsx
===================================================================
--- frontend/src/pages/Register/Register.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
+++ frontend/src/pages/Register/Register.jsx	(revision 3ebe47cce61b3bfc1dee2566f7475e378374591e)
@@ -0,0 +1,112 @@
+import React, { useEffect, useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+
+import api from "../../api/axios";
+
+const Register = () => {
+  const navigate = useNavigate();
+  const [email, setEmail] = useState("");
+  const [username, setUsername] = useState("");
+  const [password, setPassword] = useState("");
+  const [error, setError] = useState("");
+  const [isSubmitting, setIsSubmitting] = useState(false);
+
+  useEffect(() => {
+    const token = localStorage.getItem("authToken");
+    if (token) navigate("/dashboard", { replace: true });
+  }, [navigate]);
+
+  const onSubmit = async (e) => {
+    e.preventDefault();
+    setError("");
+    setIsSubmitting(true);
+    try {
+      const res = await api.post("/auth/register", {
+        email,
+        username,
+        password,
+      });
+      localStorage.setItem("authToken", res.data.token);
+      localStorage.setItem(
+        "authUser",
+        JSON.stringify({
+          userId: res.data.userId,
+          username: res.data.username,
+          email: res.data.email,
+        }),
+      );
+      navigate("/dashboard");
+    } catch (err) {
+      const message =
+        err?.response?.data?.message ||
+        Object.values(err?.response?.data?.errors ?? {})[0] ||
+        "Registration failed";
+      setError(message);
+    } finally {
+      setIsSubmitting(false);
+    }
+  };
+
+  return (
+    <div className="min-h-screen flex items-center justify-center p-4">
+      <form
+        onSubmit={onSubmit}
+        className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4"
+      >
+        <legend className="fieldset-legend">Register</legend>
+
+        <label className="label">Email</label>
+        <input
+          type="email"
+          className="input"
+          placeholder="Email"
+          value={email}
+          onChange={(e) => setEmail(e.target.value)}
+          autoComplete="email"
+          required
+        />
+
+        <label className="label">Username</label>
+        <input
+          type="text"
+          className="input"
+          placeholder="Username"
+          value={username}
+          onChange={(e) => setUsername(e.target.value)}
+          autoComplete="username"
+          required
+        />
+
+        <label className="label">Password</label>
+        <input
+          type="password"
+          className="input"
+          placeholder="Password"
+          value={password}
+          onChange={(e) => setPassword(e.target.value)}
+          autoComplete="new-password"
+          required
+        />
+
+        {error ? <p className="text-error mt-2 text-sm">{error}</p> : null}
+
+        <button
+          className="btn btn-neutral mt-4"
+          type="submit"
+          disabled={isSubmitting}
+        >
+          {isSubmitting ? "Creating account..." : "Register"}
+        </button>
+
+        <p className="mt-3 text-sm">
+          Already have an account?{" "}
+          <Link className="link link-primary" to="/login">
+            Login
+          </Link>
+        </p>
+      </form>
+    </div>
+  );
+};
+
+export default Register;
