Index: .vscode/settings.json
===================================================================
--- .vscode/settings.json	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ .vscode/settings.json	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,4 @@
+{
+    "java.compile.nullAnalysis.mode": "automatic",
+    "java.configuration.updateBuildConfiguration": "interactive"
+}
Index: ckend/.gitignore
===================================================================
--- backend/.gitignore	(revision 793ce2c999fddd728a9a4f772ab13cc20b8c9914)
+++ 	(revision )
@@ -1,33 +1,0 @@
-HELP.md
-target/
-.mvn/wrapper/maven-wrapper.jar
-!**/src/main/**/target/
-!**/src/test/**/target/
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-build/
-!**/src/main/**/build/
-!**/src/test/**/build/
-
-### VS Code ###
-.vscode/
Index: backend/HELP.md
===================================================================
--- backend/HELP.md	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/HELP.md	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,35 @@
+# Getting Started
+
+### Reference Documentation
+
+For further reference, please consider the following sections:
+
+* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
+* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/4.0.2/maven-plugin)
+* [Create an OCI image](https://docs.spring.io/spring-boot/4.0.2/maven-plugin/build-image.html)
+* [Spring Data JPA](https://docs.spring.io/spring-boot/4.0.2/reference/data/sql.html#data.sql.jpa-and-spring-data)
+* [Spring Security](https://docs.spring.io/spring-boot/4.0.2/reference/web/spring-security.html)
+* [Validation](https://docs.spring.io/spring-boot/4.0.2/reference/io/validation.html)
+* [Spring Web](https://docs.spring.io/spring-boot/4.0.2/reference/web/servlet.html)
+
+### Guides
+
+The following guides illustrate how to use some features concretely:
+
+* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
+* [Securing a Web Application](https://spring.io/guides/gs/securing-web/)
+* [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/)
+* [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/)
+* [Validation](https://spring.io/guides/gs/validating-form-input/)
+* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
+* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
+* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
+
+### Maven Parent overrides
+
+Due to Maven's design, elements are inherited from the parent POM to the project POM.
+While most of the inheritance is fine, it also inherits unwanted elements like `<license>` and `<developers>` from the
+parent.
+To prevent this, the project POM contains empty overrides for these elements.
+If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides.
+
Index: backend/env
===================================================================
--- backend/env	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/env	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,54 @@
+# ============================================
+# 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/gitignore
===================================================================
--- backend/gitignore	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/gitignore	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,38 @@
+HELP.md
+target/
+.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Environment Variables ###
+.env
+.env.local
+.env.*.local
Index: backend/pom.xml
===================================================================
--- backend/pom.xml	(revision 793ce2c999fddd728a9a4f772ab13cc20b8c9914)
+++ backend/pom.xml	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -32,4 +32,9 @@
     <dependencies>
         <dependency>
+            <groupId>io.github.cdimascio</groupId>
+            <artifactId>dotenv-java</artifactId>
+            <version>3.0.0</version>
+        </dependency>
+        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
@@ -47,5 +52,21 @@
             <artifactId>spring-boot-starter-webmvc</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.12.6</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.12.6</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.12.6</version>
+            <scope>runtime</scope>
+        </dependency>
         <dependency>
             <groupId>org.postgresql</groupId>
Index: backend/src/main/java/com/trekr/backend/BackendApplication.java
===================================================================
--- backend/src/main/java/com/trekr/backend/BackendApplication.java	(revision 793ce2c999fddd728a9a4f772ab13cc20b8c9914)
+++ backend/src/main/java/com/trekr/backend/BackendApplication.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -1,4 +1,5 @@
 package com.trekr.backend;
 
+import io.github.cdimascio.dotenv.Dotenv;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -8,5 +9,81 @@
 
     public static void main(String[] args) {
-        SpringApplication.run(BackendApplication.class, args);
+        // Load .env file BEFORE Spring Boot starts
+        // 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();
+                String value = entry.getValue();
+                if (value != null && !value.trim().isEmpty()) {
+                    // Set as system property (for ${} placeholders in application.properties)
+                    System.setProperty(key, value);
+                    // Collect for default properties
+                    defaultProps.put(key, value);
+                }
+            });
+            
+            // Set all default properties at once
+            if (!defaultProps.isEmpty()) {
+                app.setDefaultProperties(defaultProps);
+            }
+        }
+        
+        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
+            try {
+                dotenv = Dotenv.configure()
+                        .directory("../")
+                        .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;
+            }
+        }
+        
+        // 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/AuthProperties.java
===================================================================
--- backend/src/main/java/com/trekr/backend/config/AuthProperties.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/config/AuthProperties.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,15 @@
+package com.trekr.backend.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@Getter @Setter
+@ConfigurationProperties(prefix = "auth")
+public class AuthProperties {
+    private String secret;
+    private int accessTokenMaxAge;
+    private int refreshTokenMaxAge;
+}
Index: backend/src/main/java/com/trekr/backend/config/LegacyPasswordEncoder.java
===================================================================
--- backend/src/main/java/com/trekr/backend/config/LegacyPasswordEncoder.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/config/LegacyPasswordEncoder.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,29 @@
+package com.trekr.backend.config;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * Supports both BCrypt (for new users) and plain text (for legacy seed data).
+ * Use BCrypt for encoding; for matching, tries BCrypt first, then plain.
+ */
+public class LegacyPasswordEncoder implements PasswordEncoder {
+
+    private final BCryptPasswordEncoder bcrypt = new BCryptPasswordEncoder();
+
+    @Override
+    public String encode(CharSequence rawPassword) {
+        return bcrypt.encode(rawPassword);
+    }
+
+    @Override
+    public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        if (encodedPassword == null || encodedPassword.isEmpty()) {
+            return false;
+        }
+        if (encodedPassword.startsWith("$2a$") || encodedPassword.startsWith("$2b$")) {
+            return bcrypt.matches(rawPassword, encodedPassword);
+        }
+        return rawPassword.toString().equals(encodedPassword);
+    }
+}
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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,30 @@
+package com.trekr.backend.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+    @Bean
+    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+        http
+                .csrf(AbstractHttpConfigurer::disable)
+                .authorizeHttpRequests(auth -> auth
+                        .requestMatchers("/api/auth/**").permitAll()
+                        .anyRequest().authenticated()
+                );
+        return http.build();
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new LegacyPasswordEncoder();
+    }
+}
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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,31 @@
+package com.trekr.backend.controller;
+
+import com.trekr.backend.dto.auth.AuthResponse;
+import com.trekr.backend.dto.auth.LoginRequest;
+import com.trekr.backend.dto.auth.RegisterRequest;
+import com.trekr.backend.service.AuthService;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/auth")
+@RequiredArgsConstructor
+public class AuthController {
+    
+    private final AuthService authService;
+    
+    @PostMapping("/register")
+    public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest request) {
+        AuthResponse response = authService.register(request);
+        return ResponseEntity.status(HttpStatus.CREATED).body(response);
+    }
+    
+    @PostMapping("/login")
+    public ResponseEntity<AuthResponse> login(@Valid @RequestBody LoginRequest request) {
+        AuthResponse response = authService.login(request);
+        return ResponseEntity.ok(response);
+    }
+}
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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,16 @@
+package com.trekr.backend.dto.auth;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class AuthResponse {
+    private String token;
+    private String type = "Bearer";
+    private Long userId;
+    private String username;
+    private String 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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,14 @@
+package com.trekr.backend.dto.auth;
+
+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;
+}
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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,22 @@
+package com.trekr.backend.dto.auth;
+
+import jakarta.validation.constraints.Email;
+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;
+}
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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,31 @@
+package com.trekr.backend.entity;
+
+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)
+    @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;
+}
Index: backend/src/main/java/com/trekr/backend/exception/GlobalExceptionHandler.java
===================================================================
--- backend/src/main/java/com/trekr/backend/exception/GlobalExceptionHandler.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/exception/GlobalExceptionHandler.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,56 @@
+package com.trekr.backend.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+    
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public ResponseEntity<Map<String, Object>> handleValidationExceptions(
+            MethodArgumentNotValidException ex) {
+        Map<String, Object> errors = new HashMap<>();
+        Map<String, String> fieldErrors = new HashMap<>();
+        
+        ex.getBindingResult().getAllErrors().forEach((error) -> {
+            String fieldName = ((FieldError) error).getField();
+            String errorMessage = error.getDefaultMessage();
+            fieldErrors.put(fieldName, errorMessage);
+        });
+        
+        errors.put("timestamp", LocalDateTime.now());
+        errors.put("status", HttpStatus.BAD_REQUEST.value());
+        errors.put("message", "Validation failed");
+        errors.put("errors", fieldErrors);
+        
+        return ResponseEntity.badRequest().body(errors);
+    }
+    
+    @ExceptionHandler(RuntimeException.class)
+    public ResponseEntity<Map<String, Object>> handleRuntimeException(RuntimeException ex) {
+        Map<String, Object> error = new HashMap<>();
+        error.put("timestamp", LocalDateTime.now());
+        error.put("status", HttpStatus.BAD_REQUEST.value());
+        error.put("message", ex.getMessage());
+        
+        return ResponseEntity.badRequest().body(error);
+    }
+    
+    @ExceptionHandler(Exception.class)
+    public ResponseEntity<Map<String, Object>> handleGenericException(Exception ex) {
+        Map<String, Object> error = new HashMap<>();
+        error.put("timestamp", LocalDateTime.now());
+        error.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
+        error.put("message", "An unexpected error occurred");
+        
+        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
+    }
+}
Index: backend/src/main/java/com/trekr/backend/repository/UserRepository.java
===================================================================
--- backend/src/main/java/com/trekr/backend/repository/UserRepository.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/repository/UserRepository.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,15 @@
+package com.trekr.backend.repository;
+
+import com.trekr.backend.entity.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> findByEmail(String email);
+    Optional<User> findByUsername(String username);
+    boolean existsByEmail(String email);
+    boolean existsByUsername(String username);
+}
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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,66 @@
+# 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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,18 @@
+package com.trekr.backend.security;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * Represents the currently logged-in user after we've validated their JWT.
+ * Controllers can inject this or get it from SecurityContextHolder.
+ */
+@Getter
+@RequiredArgsConstructor
+public class UserPrincipal implements Serializable {
+    private final Long userId;
+    private final String username;
+    private final String 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 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,55 @@
+package com.trekr.backend.service;
+
+import com.trekr.backend.dto.auth.AuthResponse;
+import com.trekr.backend.dto.auth.LoginRequest;
+import com.trekr.backend.dto.auth.RegisterRequest;
+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;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+public class AuthService {
+
+    private final UserRepository userRepository;
+    private final PasswordEncoder passwordEncoder;
+    private final JwtService jwtService;
+
+    @Transactional
+    public AuthResponse register(RegisterRequest request) {
+        if (userRepository.existsByEmail(request.getEmail())) {
+            throw new RuntimeException("Email already exists");
+        }
+        if (userRepository.existsByUsername(request.getUsername())) {
+            throw new RuntimeException("Username already exists");
+        }
+
+        User user = new User();
+        user.setEmail(request.getEmail().trim().toLowerCase());
+        user.setUsername(request.getUsername().trim());
+        user.setPassword(passwordEncoder.encode(request.getPassword()));
+
+        user = userRepository.save(user);
+
+        String token = jwtService.generateToken(user.getUserId(), user.getUsername(), user.getEmail());
+        return new AuthResponse(token, "Bearer", user.getUserId(), user.getUsername(), user.getEmail());
+    }
+
+    public AuthResponse login(LoginRequest request) {
+        String usernameOrEmail = request.getUsernameOrEmail().trim();
+        String emailLookup = usernameOrEmail.contains("@") ? usernameOrEmail.toLowerCase() : usernameOrEmail;
+        User user = userRepository.findByUsername(usernameOrEmail)
+                .or(() -> userRepository.findByEmail(emailLookup))
+                .orElseThrow(() -> new RuntimeException("Invalid credentials"));
+
+        if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
+            throw new RuntimeException("Invalid credentials");
+        }
+
+        String token = jwtService.generateToken(user.getUserId(), user.getUsername(), user.getEmail());
+        return new AuthResponse(token, "Bearer", user.getUserId(), user.getUsername(), user.getEmail());
+    }
+}
Index: backend/src/main/java/com/trekr/backend/service/JwtService.java
===================================================================
--- backend/src/main/java/com/trekr/backend/service/JwtService.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
+++ backend/src/main/java/com/trekr/backend/service/JwtService.java	(revision 140d09810bfccea4e866a65432770142f194ebbf)
@@ -0,0 +1,36 @@
+package com.trekr.backend.service;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.crypto.SecretKey;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+@Service
+public class JwtService {
+
+    private final SecretKey signingKey;
+    private static final long TOKEN_VALIDITY_MS = 24 * 60 * 60 * 1000; // 24 hours
+
+    public JwtService(@Value("${jwt.secret}") String secret) {
+        String key = secret != null && !secret.isBlank() ? secret : "dev-fallback-secret-min-32-chars-required";
+        if (key.length() < 32) {
+            key = key + "0".repeat(Math.max(0, 32 - key.length()));
+        }
+        this.signingKey = Keys.hmacShaKeyFor(key.getBytes(StandardCharsets.UTF_8));
+    }
+
+    public String generateToken(Long userId, String username, String email) {
+        return Jwts.builder()
+                .subject(String.valueOf(userId))
+                .claim("username", username)
+                .claim("email", email)
+                .issuedAt(new Date())
+                .expiration(new Date(System.currentTimeMillis() + TOKEN_VALIDITY_MS))
+                .signWith(signingKey)
+                .compact();
+    }
+}
