diff --git a/.env b/.env
deleted file mode 100644
index 6d5f502b908a143939149a2e58f4a1823e7736c4..0000000000000000000000000000000000000000
--- a/.env
+++ /dev/null
@@ -1,3 +0,0 @@
-DB_URL=jdbc:postgresql://localhost:5432/be-authentication
-DB_USERNAME=postgres
-DB_PASSWORD=postgres
diff --git a/.github/workflows/production-cd.yml b/.github/workflows/production-cd.yml
index 1e4a4112ab91d7e9ec6e17148606e5ec28ec732f..705daa42c453207eeba7d061a3a3b7905d05b3af 100644
--- a/.github/workflows/production-cd.yml
+++ b/.github/workflows/production-cd.yml
@@ -49,5 +49,5 @@ jobs:
           GOOGLE_PROJECT: ${{ secrets.GOOGLE_PROJECT }}
         run: |
           gcloud container clusters get-credentials safetypin-cluster --region asia-southeast2
-          sed -i "s/GOOGLE_PROJECT/$GOOGLE_PROJECT/g" resources.yaml
-          kubectl apply -f resources.yaml
\ No newline at end of file
+          sed -i "s/GOOGLE_PROJECT/$GOOGLE_PROJECT/g" production.yaml
+          kubectl apply -f production.yaml
\ No newline at end of file
diff --git a/.github/workflows/staging-ci-cd.yml b/.github/workflows/staging-ci-cd.yml
index ccc2d24e7f4f9766978055dd6e464be9416947cc..b9f2245a1ecb24e75818e76ec49a7ccfddf507ff 100644
--- a/.github/workflows/staging-ci-cd.yml
+++ b/.github/workflows/staging-ci-cd.yml
@@ -40,16 +40,39 @@ jobs:
         uses: actions/checkout@v3
 
       - name: Install the gcloud CLI
-        uses: google-github-actions/setup-gcloud@v0
+        uses: google-github-actions/setup-gcloud@v2
         with:
           project_id: ${{ secrets.GOOGLE_PROJECT }}
           service_account_key: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
           export_default_credentials: true
 
+      - name: Authenticate with GCP
+        uses: google-github-actions/auth@v1
+        with:
+          credentials_json: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}
+
       - name: Build and Push Docker Image
         env:
+          PRODUCTION: staging
           GOOGLE_PROJECT: ${{ secrets.GOOGLE_PROJECT }}
+          JDBC_STAGING_DATABASE_PASSWORD: ${{ secrets.JDBC_STAGING_DATABASE_PASSWORD }}
+          JDBC_STAGING_DATABASE_URL: ${{ secrets.JDBC_STAGING_DATABASE_URL }}
+          JDBC_STAGING_DATABASE_USERNAME: ${{ secrets.JDBC_STAGING_DATABASE_USERNAME }}
         run: |
           gcloud auth configure-docker us-central1-docker.pkg.dev
-          docker build -t us-central1-docker.pkg.dev/$GOOGLE_PROJECT/my-repository/authentication:latest .
-          docker push us-central1-docker.pkg.dev/$GOOGLE_PROJECT/my-repository/authentication:latest
\ No newline at end of file
+          docker build --build-arg PRODUCTION=$PRODUCTION --build-arg JDBC_STAGING_DATABASE_PASSWORD=$JDBC_STAGING_DATABASE_PASSWORD --build-arg JDBC_STAGING_DATABASE_URL=$JDBC_STAGING_DATABASE_URL --build-arg JDBC_STAGING_DATABASE_USERNAME=$JDBC_STAGING_DATABASE_USERNAME -t us-central1-docker.pkg.dev/$GOOGLE_PROJECT/staging-repository/authentication:latest .
+          docker push us-central1-docker.pkg.dev/$GOOGLE_PROJECT/staging-repository/authentication:latest
+
+      - name: Install required components
+        run: |
+          gcloud components update
+          gcloud components install gke-gcloud-auth-plugin
+
+      - name: Deploy to GKE
+        env:
+          GOOGLE_PROJECT: ${{ secrets.GOOGLE_PROJECT }}
+          GOOGLE_REPOSiTORY: staging-repository
+        run: |
+          gcloud container clusters get-credentials safetypin-staging --region asia-southeast2
+          sed -i "s/GOOGLE_PROJECT/$GOOGLE_PROJECT/g" staging.yaml
+          kubectl apply -f staging.yaml
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index a5403eff656f15272fcb7e6c9c10de0edac79cd4..9372156b8f55652b70d6d0af1f7bde92551459ba 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,6 +6,24 @@ RUN mvn clean package -DskipTests
 
 # Step 2: Use OpenJDK 21 to run the application
 FROM openjdk:21-jdk-slim
+
+# Setup envs
+ARG PRODUCTION
+ARG JDBC_DATABASE_PASSWORD
+ARG JDBC_DATABASE_URL
+ARG JDBC_DATABASE_USERNAME
+ARG JDBC_STAGING_DATABASE_USERNAME
+ARG JDBC_STAGING_DATABASE_URL
+ARG JDBC_STAGING_DATABASE_URL
+
+ENV PRODUCTION ${PRODUCTION}
+ENV JDBC_DATABASE_PASSWORD ${JDBC_DATABASE_PASSWORD}
+ENV JDBC_DATABASE_URL ${JDBC_DATABASE_URL}
+ENV JDBC_DATABASE_USERNAME ${JDBC_DATABASE_USERNAME}
+ENV JDBC_STAGING_DATABASE_PASSWORD ${JDBC_STAGING_DATABASE_PASSWORD}
+ENV JDBC_STAGING_DATABASE_URL ${JDBC_STAGING_DATABASE_URL}
+ENV JDBC_STAGING_DATABASE_USERNAME ${JDBC_STAGING_DATABASE_USERNAME}
+
 WORKDIR /app
 COPY --from=builder /app/target/authentication-0.0.1-SNAPSHOT.jar app.jar
 EXPOSE 8080
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..10b7c73cebbb49f871efdd530649287715a73539
--- /dev/null
+++ b/README.md
@@ -0,0 +1,180 @@
+# Authentication Microservice
+[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=safetypin-official_be-authentication)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+
+[![SonarQube Cloud](https://sonarcloud.io/images/project_badges/sonarcloud-dark.svg)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+
+[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=bugs)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=coverage)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=safetypin-official_be-authentication&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=safetypin-official_be-authentication)
+
+## Overview
+
+The **Authentication Microservice** is a Spring Boot-based REST API that handles user authentication and authorization. It supports both traditional email-based registration/login as well as social authentication (e.g., Google, Apple). The service includes features such as OTP (One-Time Password) verification, password reset simulation, and a simple content posting endpoint for verified users.
+
+## Table of Contents
+
+- [Features](#features)
+- [Tech Stack](#tech-stack)
+- [Prerequisites](#prerequisites)
+- [Installation](#installation)
+- [Configuration](#configuration)
+- [API Endpoints](#api-endpoints)
+- [Running Tests](#running-tests)
+- [Development](#development)
+- [Contributing](#contributing)
+- [License](#license)
+- [Acknowledgements](#acknowledgements)
+
+## Features
+
+- **Email Registration & Login:** Supports registration and login using email and password.
+- **Social Authentication:** Simulated endpoints for registration and login using social providers.
+- **OTP Verification:** Generates and verifies OTPs for user account validation.
+- **Password Reset:** Simulated password reset functionality for email-based users.
+- **Content Posting:** A secured endpoint that only allows verified users to post content.
+- **Dev Data Seeder:** Automatically seeds development data when running under the `dev` profile.
+- **Robust Testing:** Comprehensive unit and integration tests using JUnit 5, Spring Boot Test, and TestContainers.
+
+## Tech Stack
+
+- **Java:** 21
+- **Spring Boot:** 3.4.2
+- **Maven:** Build automation and dependency management
+- **Spring Security:** For authentication and password encoding
+- **Spring Data JPA:** For ORM and database access
+- **PostgresSQL & H2:** Database support (PostgresSQL for production; H2 for development/testing)
+- **JUnit 5 & TestContainers:** For testing and integration testing
+
+## Prerequisites
+
+- **Java 21**
+- **Maven 3.6+**
+- A running PostgresSQL instance (for production) or H2 (for development/testing)
+
+## Installation
+
+1. **Clone the repository:**
+```
+   git clone https://github.com/yourusername/authentication-microservice.git  
+   cd authentication-microservice
+```
+2. **Build the project using Maven:**
+```
+   mvn clean install
+```
+3. **Run the application:**
+```
+   mvn spring-boot:run
+```
+   The service will start on the default port (typically 8080).
+
+## Configuration
+
+- **application.properties:** Configure your database, server port, and other environment-specific settings.
+- **Profiles:** Use the `dev` profile for development. The `DevDataSeeder` will automatically seed sample user data when running under this profile:
+
+```
+mvn spring-boot:run -Dspring-boot.run.profiles=dev
+```
+
+## API Endpoints
+
+### Public Endpoints
+
+- **GET `/`**  
+  Returns a simple "Hello, World!" greeting.
+
+### Authentication Endpoints
+
+- **POST `/api/auth/register-email`**  
+  Registers a new user using email.
+    - **Request Body:** JSON containing `email`, `password`, `name`, and `birthdate`.
+    - **Response:** Success message with user data.
+
+- **POST `/api/auth/register-social`**  
+  Registers or logs in a user using social authentication.
+    - **Request Body:** JSON containing `provider`, `socialToken`, `email`, `name`, `birthdate`, and `socialId`.
+    - **Response:** Success message with user data.
+
+- **POST `/api/auth/login-email`**  
+  Authenticates a user using email and password.
+    - **Parameters:** `email`, `password`.
+    - **Response:** User data on successful login.
+
+- **POST `/api/auth/login-social`**  
+  Logs in a user via social authentication.
+    - **Parameter:** `email`.
+    - **Response:** User data on successful social login.
+
+- **POST `/api/auth/verify-otp`**  
+  Verifies the OTP for account validation.
+    - **Parameters:** `email`, `otp`.
+    - **Response:** Message indicating success or failure of OTP verification.
+
+- **POST `/api/auth/forgot-password`**  
+  Simulates password reset for email-registered users.
+    - **Request Body:** JSON containing `email`.
+    - **Response:** Message indicating that reset instructions have been sent.
+
+- **POST `/api/auth/post`**  
+  Allows posting of content for verified users.
+    - **Parameters:** `email`, `content`.
+    - **Response:** Success or failure message based on user verification.
+
+- **GET `/api/auth/dashboard`**  
+  Returns dashboard data (currently a placeholder).
+    - **Response:** An empty JSON object.
+
+## Running Tests
+
+To run all unit and integration tests, execute:
+```
+mvn test
+```
+
+Tests are written using JUnit 5 and cover controllers, services, repository interactions, and utility components such as OTP generation and validation.
+
+## Development
+
+- **Code Style:** The project adheres to standard Java coding conventions and uses Lombok to reduce boilerplate.
+- **Continuous Integration:** Integration with CI tools is recommended. Test coverage is ensured using Maven Surefire and Failsafe plugins.
+- **Debugging:** Utilize Spring Boot DevTools for hot-reloading during development.
+
+## Contributing
+
+Contributions are welcome! Please follow these steps:
+
+1. Fork the repository.
+2. Create a new feature branch (git checkout -b feature/YourFeature).
+3. Commit your changes (git commit -m 'Add some feature').
+4. Push to the branch (git push origin feature/YourFeature).
+5. Open a Pull Request.
+
+Please ensure that your code adheres to the existing coding style and that all tests pass before submitting your PR.
+
+## License
+
+This project is licensed under the
+Creative Commons Attribution-NoDerivatives 4.0 International (CC BY-ND 4.0)
+License. See the LICENSE file for details.
+
+## Acknowledgements
+
+- Thanks to the Spring Boot team and the open-source community for their continuous contributions.
+- Special thanks to contributors who have helped improve the project.
+
+## Author
+SafetyPin Team
+- Darrel Danadyaksa Poli - 2206081995
+- Fredo Melvern Tanzil - 2206024713
+- Sefriano Edsel Jieftara Djie - 2206818966=
+- Alma Putri Nashrida - 2206814671
+- Andi Salsabila Ardian - 2206083571
+- Muhammad Raihan Akbar - 2206827674
+
diff --git a/pom.xml b/pom.xml
index 936334aefa29a5989e8e79243c956eaf4df98600..e9bfa77466c699d2a0b1304e3325e9c9a0ca593e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,8 +66,8 @@
 		<!-- JUnit 5 for unit and regression testing -->
 		<dependency>
 			<groupId>org.junit.jupiter</groupId>
-			<artifactId>junit-jupiter-api</artifactId>
-			<version>5.7.1</version>
+			<artifactId>junit-jupiter</artifactId>
+			<version>5.11.2</version>
 			<scope>test</scope>
 		</dependency>
 
@@ -79,11 +79,33 @@
 			<scope>test</scope>
 		</dependency>
 
+		<dependency>
+			<groupId>org.postgresql</groupId>
+			<artifactId>postgresql</artifactId>
+			<scope>runtime</scope>
+		</dependency>
+
 		<!-- REST-assured for integration testing -->
 		<dependency>
 			<groupId>io.rest-assured</groupId>
 			<artifactId>rest-assured</artifactId>
-			<version>4.4.0</version>
+			<version>4.5.1</version>
+			<scope>test</scope>
+			<exclusions>
+				<exclusion>
+					<groupId>org.codehaus.groovy</groupId>
+					<artifactId>groovy</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.codehaus.groovy</groupId>
+					<artifactId>groovy-xml</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>io.rest-assured</groupId>
+			<artifactId>json-schema-validator</artifactId>
+			<version>4.5.1</version>
 			<scope>test</scope>
 		</dependency>
 
@@ -94,6 +116,44 @@
 			<version>2.5.4</version>
 		</dependency>
 
+		<!-- Spring Data JPA -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-jpa</artifactId>
+		</dependency>
+
+		<!-- Spring Security for Password Encoding -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-security</artifactId>
+		</dependency>
+
+		<!-- H2 Database for development/testing -->
+		<dependency>
+			<groupId>com.h2database</groupId>
+			<artifactId>h2</artifactId>
+			<scope>runtime</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-test</artifactId>
+			<version>6.1.1</version>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-validation</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>net.java.dev.jna</groupId>
+			<artifactId>jna-platform</artifactId>
+			<version>5.13.0</version>
+			<scope>test</scope>
+		</dependency>
+
 	</dependencies>
 
 	<build>
@@ -110,6 +170,7 @@
 						<path>
 							<groupId>org.projectlombok</groupId>
 							<artifactId>lombok</artifactId>
+							<version>1.18.36</version>
 						</path>
 					</annotationProcessorPaths>
 				</configuration>
@@ -141,6 +202,25 @@
 				<artifactId>maven-jar-plugin</artifactId>
 				<version>3.2.0</version>
 			</plugin>
+			<plugin>
+				<groupId>org.jacoco</groupId>
+				<artifactId>jacoco-maven-plugin</artifactId>
+				<version>0.8.12</version>
+				<executions>
+					<execution>
+						<goals>
+							<goal>prepare-agent</goal>
+						</goals>
+					</execution>
+					<execution>
+						<id>report</id>
+						<phase>test</phase>
+						<goals>
+							<goal>report</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
 		</plugins>
 	</build>
 
diff --git a/resources.yaml b/production.yaml
similarity index 100%
rename from resources.yaml
rename to production.yaml
diff --git a/src/main/java/com/safetypin/authentication/AuthenticationApplication.java b/src/main/java/com/safetypin/authentication/AuthenticationApplication.java
index 9b1e9513e0085a1dad784230ee2924c42a16d1fd..b54a9d5381a33cdf0bc8f447f1dcd01b76f08e7f 100644
--- a/src/main/java/com/safetypin/authentication/AuthenticationApplication.java
+++ b/src/main/java/com/safetypin/authentication/AuthenticationApplication.java
@@ -8,7 +8,6 @@ import org.springframework.web.bind.annotation.RestController;
 
 @SpringBootApplication
 public class AuthenticationApplication {
-
 	public static void main(String[] args) {
 		SpringApplication.run(AuthenticationApplication.class, args);
 	}
diff --git a/src/main/java/com/safetypin/authentication/controller/AuthenticationController.java b/src/main/java/com/safetypin/authentication/controller/AuthenticationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee3bcfc0e1aabb7a1fd410f60bebb12c5569ab3f
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/controller/AuthenticationController.java
@@ -0,0 +1,110 @@
+package com.safetypin.authentication.controller;
+
+import com.safetypin.authentication.dto.AuthResponse;
+import com.safetypin.authentication.dto.PasswordResetRequest;
+import com.safetypin.authentication.dto.RegistrationRequest;
+import com.safetypin.authentication.dto.SocialLoginRequest;
+import com.safetypin.authentication.exception.InvalidCredentialsException;
+import com.safetypin.authentication.exception.UserAlreadyExistsException;
+import com.safetypin.authentication.model.User;
+import com.safetypin.authentication.service.AuthenticationService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import jakarta.validation.Valid;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/auth")
+public class AuthenticationController {
+
+    private final AuthenticationService authenticationService;
+
+    public AuthenticationController(AuthenticationService authenticationService) {
+        this.authenticationService = authenticationService;
+    }
+
+
+    // Endpoint for email registration
+    @PostMapping("/register-email")
+    public ResponseEntity<AuthResponse> registerEmail(@Valid @RequestBody RegistrationRequest request) {
+        User user;
+        try {
+            user = authenticationService.registerUser(request);
+        } catch (IllegalArgumentException | UserAlreadyExistsException e) {
+            AuthResponse response = new AuthResponse(false, e.getMessage(), null);
+            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
+        }
+        return ResponseEntity.ok().body(new AuthResponse(true, "OK", user));
+    }
+
+    // Endpoint for social registration/login
+    @PostMapping("/register-social")
+    public ResponseEntity<AuthResponse> registerSocial(@Valid @RequestBody SocialLoginRequest request) {
+        User user;
+        try {
+            user = authenticationService.socialLogin(request);
+        } catch (IllegalArgumentException | UserAlreadyExistsException e) {
+            AuthResponse response = new AuthResponse(false, e.getMessage(), null);
+            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
+        }
+        return ResponseEntity.ok().body(new AuthResponse(true, "OK", user));
+    }
+
+    // OTP verification endpoint
+    @PostMapping("/verify-otp")
+    public String verifyOTP(@RequestParam String email, @RequestParam String otp) {
+        boolean verified = authenticationService.verifyOTP(email, otp);
+        return verified ? "User verified successfully" : "OTP verification failed";
+    }
+
+
+
+    // Endpoint for email login
+    @PostMapping("/login-email")
+    public ResponseEntity<Object> loginEmail(@RequestParam String email, @RequestParam String password) {
+        try {
+            return ResponseEntity.ok(authenticationService.loginUser(email, password));
+        } catch (InvalidCredentialsException e){
+            AuthResponse response = new AuthResponse(false, e.getMessage(), null);
+            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
+        }
+
+    }
+
+    // Endpoint for social login
+    @PostMapping("/login-social")
+    public ResponseEntity<Object> loginSocial(@RequestParam String email) {
+        try {
+            return ResponseEntity.ok(authenticationService.loginSocial(email));
+        } catch (InvalidCredentialsException e){
+            AuthResponse response = new AuthResponse(false, e.getMessage(), null);
+            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
+        }
+
+    }
+
+
+
+
+    // Endpoint for forgot password (only for email users)
+    @PostMapping("/forgot-password")
+    public String forgotPassword(@Valid @RequestBody PasswordResetRequest request) {
+        authenticationService.forgotPassword(request.getEmail());
+        return "Password reset instructions have been sent to your email (simulated)";
+    }
+
+
+
+
+    // Endpoint simulating a content post that requires a verified account
+    @PostMapping("/post")
+    public String postContent(@RequestParam String email, @RequestParam String content) {
+        return authenticationService.postContent(email, content);
+    }
+
+    // On successful login, return an empty map as a placeholder for future reports
+    @GetMapping("/dashboard")
+    public String dashboard() {
+        return "{}";
+    }
+}
diff --git a/src/main/java/com/safetypin/authentication/dto/AuthResponse.java b/src/main/java/com/safetypin/authentication/dto/AuthResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..a29962ff41457e80c4adbbb6f198b35b121751b4
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/dto/AuthResponse.java
@@ -0,0 +1,13 @@
+package com.safetypin.authentication.dto;
+
+import lombok.*;
+
+@Data
+@Getter
+@Setter
+@AllArgsConstructor
+public class AuthResponse {
+    private boolean success;
+    private String message;
+    private Object data;
+}
\ No newline at end of file
diff --git a/src/main/java/com/safetypin/authentication/dto/ErrorResponse.java b/src/main/java/com/safetypin/authentication/dto/ErrorResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7bb4102d7b8ee0ef4a89fd83cbef9406ccb6922
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/dto/ErrorResponse.java
@@ -0,0 +1,25 @@
+package com.safetypin.authentication.dto;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+public class ErrorResponse{
+    private int status;
+    private String message;
+    private LocalDateTime timestamp;
+
+    public ErrorResponse(int status, String message) {
+        this.status = status;
+        this.message = message;
+        this.timestamp = LocalDateTime.now();
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/safetypin/authentication/dto/PasswordResetRequest.java b/src/main/java/com/safetypin/authentication/dto/PasswordResetRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9b12ed833092cf9fb644dbaae68983653b21b21c
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/dto/PasswordResetRequest.java
@@ -0,0 +1,18 @@
+package com.safetypin.authentication.dto;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+public class PasswordResetRequest {
+
+    @NotBlank
+    @Email
+    private String email;
+
+    // Getters and setters
+
+}
diff --git a/src/main/java/com/safetypin/authentication/dto/RegistrationRequest.java b/src/main/java/com/safetypin/authentication/dto/RegistrationRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ddb267f1f3b933e64dd9fff109b3123a0b5d3d12
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/dto/RegistrationRequest.java
@@ -0,0 +1,30 @@
+package com.safetypin.authentication.dto;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDate;
+
+@Setter
+@Getter
+public class RegistrationRequest {
+
+    @NotBlank
+    @Email
+    private String email;
+
+    @NotBlank
+    private String password;
+
+    @NotBlank
+    private String name;
+
+    @NotNull
+    private LocalDate birthdate;
+
+    // Getters and setters
+
+}
diff --git a/src/main/java/com/safetypin/authentication/dto/SocialLoginRequest.java b/src/main/java/com/safetypin/authentication/dto/SocialLoginRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1b34b2e38e3975795f5bacbbd33cd55aafab53a7
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/dto/SocialLoginRequest.java
@@ -0,0 +1,35 @@
+package com.safetypin.authentication.dto;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDate;
+
+@Setter
+@Getter
+public class SocialLoginRequest {
+
+    @NotBlank
+    private String provider; // "GOOGLE" or "APPLE"
+
+    @NotBlank
+    private String socialToken; // Token from the social provider
+
+    // Simulated fields as if retrieved from the provider
+    @NotBlank
+    private String email;
+
+    @NotBlank
+    private String name;
+
+    @NotNull
+    private LocalDate birthdate;
+
+    @NotBlank
+    private String socialId; // ID provided by the social provider
+
+    // Getters and setters
+
+}
diff --git a/src/main/java/com/safetypin/authentication/exception/InvalidCredentialsException.java b/src/main/java/com/safetypin/authentication/exception/InvalidCredentialsException.java
new file mode 100644
index 0000000000000000000000000000000000000000..e43d14a97b4af00b304ef223461aab63f87d80eb
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/exception/InvalidCredentialsException.java
@@ -0,0 +1,7 @@
+package com.safetypin.authentication.exception;
+
+public class InvalidCredentialsException extends RuntimeException {
+    public InvalidCredentialsException(String message) {
+        super(message);
+    }
+}
diff --git a/src/main/java/com/safetypin/authentication/exception/UserAlreadyExistsException.java b/src/main/java/com/safetypin/authentication/exception/UserAlreadyExistsException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c31a5a7d67770f3d328acf0db7436058bc44dc54
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/exception/UserAlreadyExistsException.java
@@ -0,0 +1,7 @@
+package com.safetypin.authentication.exception;
+
+public class UserAlreadyExistsException extends RuntimeException {
+    public UserAlreadyExistsException(String message) {
+        super(message);
+    }
+}
diff --git a/src/main/java/com/safetypin/authentication/model/User.java b/src/main/java/com/safetypin/authentication/model/User.java
new file mode 100644
index 0000000000000000000000000000000000000000..41e776c80b3d4fa70956243f206185478de6a340
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/model/User.java
@@ -0,0 +1,59 @@
+package com.safetypin.authentication.model;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.LocalDate;
+
+@Entity
+@Table(name = "users")
+@NoArgsConstructor
+public class User {
+
+    @Id
+    @Setter @Getter
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Setter @Getter
+    @Column(nullable = false, unique = true)
+    private String email;
+
+    // May be null for social login users
+    @Setter @Getter
+    @Column(nullable = false)
+    private String password;
+
+    @Setter @Getter
+    @Column(nullable = false)
+    private String name;
+
+    @Column(nullable = false)
+    private boolean isVerified = false;
+
+    @Setter @Getter
+    private String role;
+
+    // New fields
+    @Setter @Getter
+    private LocalDate birthdate;
+
+    @Setter  @Getter
+    private String provider;  // "EMAIL", "GOOGLE", "APPLE"
+
+    @Setter @Getter
+    private String socialId;  // For social login users
+
+
+    // Getters and setters
+
+    public boolean isVerified() {
+        return isVerified;
+    }
+    public void setVerified(boolean verified) {
+        isVerified = verified;
+    }
+
+}
diff --git a/src/main/java/com/safetypin/authentication/repository/UserRepository.java b/src/main/java/com/safetypin/authentication/repository/UserRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..bef7fa961afab7cbb1e697d0578cb9037f619af1
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/repository/UserRepository.java
@@ -0,0 +1,10 @@
+package com.safetypin.authentication.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import com.safetypin.authentication.model.User;
+
+@Repository
+public interface UserRepository extends JpaRepository<User, Long> {
+    User findByEmail(String email);
+}
diff --git a/src/main/java/com/safetypin/authentication/security/PasswordEncoderConfig.java b/src/main/java/com/safetypin/authentication/security/PasswordEncoderConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4e107fa1ac73de8fa0c5389dcca59178bcf043e
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/security/PasswordEncoderConfig.java
@@ -0,0 +1,14 @@
+package com.safetypin.authentication.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Configuration
+public class PasswordEncoderConfig {
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+}
diff --git a/src/main/java/com/safetypin/authentication/security/SecurityConfig.java b/src/main/java/com/safetypin/authentication/security/SecurityConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..dae1a161a0afaada40ea379b67e9614dc16cfd35
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/security/SecurityConfig.java
@@ -0,0 +1,34 @@
+package com.safetypin.authentication.security;
+
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+public class SecurityConfig {
+
+    @Bean
+    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+        http
+                // CSRF protection is enabled by default, so we don't disable it here
+                .authorizeHttpRequests(auth -> auth
+                        .requestMatchers("/**").permitAll() // Allow all requests
+                )
+                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // No session
+                .formLogin(AbstractHttpConfigurer::disable) // Disable login page
+                .httpBasic(AbstractHttpConfigurer::disable); // Disable basic authentication
+
+        return http.build();
+    }
+
+    @Bean
+    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
+        return authenticationConfiguration.getAuthenticationManager();
+    }
+}
diff --git a/src/main/java/com/safetypin/authentication/seeder/DevDataSeeder.java b/src/main/java/com/safetypin/authentication/seeder/DevDataSeeder.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8644025b263b5c66b039a5a80c4703d83e45a3f
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/seeder/DevDataSeeder.java
@@ -0,0 +1,93 @@
+package com.safetypin.authentication.seeder;
+
+import com.safetypin.authentication.model.User;
+import com.safetypin.authentication.repository.UserRepository;
+import static com.safetypin.authentication.service.AuthenticationService.EMAIL_PROVIDER;
+
+import jakarta.annotation.PostConstruct;
+import org.springframework.context.annotation.Profile;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+
+@Component
+@Profile({"dev"})
+public class DevDataSeeder implements Runnable {
+
+    private final UserRepository userRepository;
+    private final PasswordEncoder passwordEncoder;
+
+    public DevDataSeeder(UserRepository userRepository, PasswordEncoder passwordEncoder) {
+        this.userRepository = userRepository;
+        this.passwordEncoder = passwordEncoder;
+    }
+
+    @PostConstruct
+    public void init() {
+        run();
+    }
+
+    @Override
+    public void run() {
+        // Only seed if there are no users in the repository
+        if (userRepository.count() == 0) {
+
+            User user1 = new User();
+            user1.setEmail("user1@example.com");
+            user1.setPassword(passwordEncoder.encode("password1")); //NOSONAR
+            user1.setName("User One");
+            user1.setVerified(true);
+            user1.setRole("user");
+            user1.setBirthdate(LocalDate.of(1990, 1, 1));
+            user1.setProvider(EMAIL_PROVIDER);
+            user1.setSocialId("social1");
+            userRepository.save(user1);
+
+            User user2 = new User();
+            user2.setEmail("user2@example.com");
+            user2.setPassword(passwordEncoder.encode("password2")); //NOSONAR
+            user2.setName("User Two");
+            user2.setVerified(true);
+            user2.setRole("user");
+            user2.setBirthdate(LocalDate.of(1991, 2, 2));
+            user2.setProvider(EMAIL_PROVIDER);
+            user2.setSocialId("social2");
+            userRepository.save(user2);
+
+
+            User user3 = new User();
+            user3.setEmail("user3@example.com");
+            user3.setPassword(passwordEncoder.encode("password3")); //NOSONAR
+            user3.setName("User Three");
+            user3.setVerified(true);
+            user3.setRole("user");
+            user3.setBirthdate(LocalDate.of(1992, 3, 3));
+            user3.setProvider(EMAIL_PROVIDER);
+            user3.setSocialId("social3");
+            userRepository.save(user3);
+
+            User user4 = new User();
+            user4.setEmail("user4@example.com");
+            user4.setPassword(passwordEncoder.encode("password4")); //NOSONAR
+            user4.setName("User Four");
+            user4.setVerified(true);
+            user4.setRole("user");
+            user4.setBirthdate(LocalDate.of(1993, 4, 4));
+            user4.setProvider(EMAIL_PROVIDER);
+            user4.setSocialId("social4");
+            userRepository.save(user4);
+
+            User user5 = new User();
+            user5.setEmail("user5@example.com");
+            user5.setPassword(passwordEncoder.encode("password5")); //NOSONAR
+            user5.setName("User Five");
+            user5.setVerified(true);
+            user5.setRole("user");
+            user5.setBirthdate(LocalDate.of(1994, 5, 5));
+            user5.setProvider(EMAIL_PROVIDER);
+            user5.setSocialId("social5");
+            userRepository.save(user5);
+        }
+    }
+}
diff --git a/src/main/java/com/safetypin/authentication/service/AuthenticationService.java b/src/main/java/com/safetypin/authentication/service/AuthenticationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..71e36836ec78ac0c369da2522698072b937f0b51
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/service/AuthenticationService.java
@@ -0,0 +1,154 @@
+package com.safetypin.authentication.service;
+
+import com.safetypin.authentication.dto.RegistrationRequest;
+import com.safetypin.authentication.dto.SocialLoginRequest;
+import com.safetypin.authentication.exception.InvalidCredentialsException;
+import com.safetypin.authentication.exception.UserAlreadyExistsException;
+import com.safetypin.authentication.model.User;
+import com.safetypin.authentication.repository.UserRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import java.time.LocalDate;
+import java.time.Period;
+
+@Service
+public class AuthenticationService {
+    public static final String EMAIL_PROVIDER = "EMAIL";
+
+    private final UserRepository userRepository;
+    private final PasswordEncoder passwordEncoder;
+    private final OTPService otpService;
+    private static final Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
+
+    public AuthenticationService(UserRepository userRepository, PasswordEncoder passwordEncoder, OTPService otpService) {
+        this.userRepository = userRepository;
+        this.passwordEncoder = passwordEncoder;
+        this.otpService = otpService;
+    }
+
+    // Registration using email – includes birthdate and OTP generation
+    public User registerUser(RegistrationRequest request) {
+        if (calculateAge(request.getBirthdate()) < 16) {
+            throw new IllegalArgumentException("User must be at least 16 years old");
+        }
+        User existingUser = userRepository.findByEmail(request.getEmail());
+        if (existingUser != null) {
+            throw new UserAlreadyExistsException("Email address is already registered. If you previously used social login (Google/Apple), please use that method to sign in.");
+        }
+        String encodedPassword = passwordEncoder.encode(request.getPassword());
+
+        User user = new User();
+        user.setEmail(request.getEmail());
+        user.setPassword(encodedPassword);
+        user.setName(request.getName());
+        user.setVerified(false);
+        user.setRole("USER");
+        user.setBirthdate(request.getBirthdate());
+        user.setProvider(EMAIL_PROVIDER);
+        user.setSocialId(null);
+        user = userRepository.save(user);
+        otpService.generateOTP(request.getEmail());
+        logger.info("OTP generated for user at {}", java.time.LocalDateTime.now());
+        return user;
+    }
+
+    // Social registration/login – simulating data fetched from Google/Apple
+    public User socialLogin(SocialLoginRequest request) {
+        if (calculateAge(request.getBirthdate()) < 16) {
+            throw new IllegalArgumentException("User must be at least 16 years old");
+        }
+        User existing = userRepository.findByEmail(request.getEmail());
+        if (existing != null) {
+            if (EMAIL_PROVIDER.equals(existing.getProvider())) {
+                throw new UserAlreadyExistsException("An account with this email exists. Please sign in using your email and password.");
+            }
+            return existing;
+        }
+        User user = new User();
+        user.setEmail(request.getEmail());
+        user.setPassword(null);
+        user.setName(request.getName());
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(request.getBirthdate());
+        user.setProvider(request.getProvider().toUpperCase());
+        user.setSocialId(request.getSocialId());
+
+        user = userRepository.save(user);
+        logger.info("User registered via social login at {}", java.time.LocalDateTime.now());
+        return user;
+    }
+
+    // Email login with detailed error messages
+    public User loginUser(String email, String rawPassword) {
+        User user = userRepository.findByEmail(email);
+        if (user == null) {
+            // email not exists
+            logger.warn("Login failed: Email not found");
+            throw new InvalidCredentialsException("Invalid email");
+        }
+        if (!passwordEncoder.matches(rawPassword, user.getPassword())) {
+            // incorrect password
+            logger.warn("Login failed: Incorrect password attempt");
+            throw new InvalidCredentialsException("Invalid password");
+        }
+        logger.info("User logged in at {}", java.time.LocalDateTime.now());
+        return user;
+    }
+
+    // Social login verification (assumed to be pre-verified externally)
+    public User loginSocial(String email) {
+        User user = userRepository.findByEmail(email);
+        if (user == null) {
+            throw new InvalidCredentialsException("Social login failed: Email not found");
+        }
+        logger.info("User logged in via social authentication at {}", java.time.LocalDateTime.now());
+        return user;
+    }
+
+    // OTP verification – marks user as verified upon success
+    public boolean verifyOTP(String email, String otp) {
+        boolean result = otpService.verifyOTP(email, otp);
+        if (result) {
+            User user = userRepository.findByEmail(email);
+            if (user != null) {
+                user.setVerified(true);
+                userRepository.save(user);
+                logger.info("OTP successfully verified at {}", java.time.LocalDateTime.now());
+            }
+        } else {
+            logger.warn("OTP verification failed at {}", java.time.LocalDateTime.now());
+        }
+        return result;
+    }
+
+    // Forgot password – only applicable for email-registered users
+    public void forgotPassword(String email) {
+        User user = userRepository.findByEmail(email);
+        if (user == null || !EMAIL_PROVIDER.equals(user.getProvider())) {
+            throw new IllegalArgumentException("Password reset is only available for email-registered users.");
+        }
+        // In production, send a reset token via email.
+        logger.info("Password reset requested at {}", java.time.LocalDateTime.now());
+    }
+
+    // Example method representing posting content that requires a verified account
+    public String postContent(String email, String content) { // NOSONAR
+        User user = userRepository.findByEmail(email);
+        if (user == null) {
+            return "User not found. Please register.";
+        }
+        if (!user.isVerified()) {
+            return "Your account is not verified. Please complete OTP verification. You may request a new OTP after 2 minutes.";
+        }
+        logger.info("Content posted successfully by user");
+        // For demo purposes, we assume the post is successful.
+        return "Content posted successfully";
+    }
+
+    private int calculateAge(LocalDate birthdate) {
+        return Period.between(birthdate, LocalDate.now()).getYears();
+    }
+}
diff --git a/src/main/java/com/safetypin/authentication/service/OTPService.java b/src/main/java/com/safetypin/authentication/service/OTPService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b338aca55d7c58021ce5e648fa48ce1cf362d8dd
--- /dev/null
+++ b/src/main/java/com/safetypin/authentication/service/OTPService.java
@@ -0,0 +1,47 @@
+package com.safetypin.authentication.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.security.SecureRandom;
+import java.time.LocalDateTime;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Service
+public class OTPService {
+
+    private static final long OTP_EXPIRATION_SECONDS = 120; // 2 minutes expiration
+    private static final Logger log = LoggerFactory.getLogger(OTPService.class);
+    private final ConcurrentHashMap<String, OTPDetails> otpStorage = new ConcurrentHashMap<>();
+    private final SecureRandom random = new SecureRandom();
+
+    public String generateOTP(String email) {
+        String otp = String.format("%06d", random.nextInt(1000000));
+        OTPDetails details = new OTPDetails(otp, LocalDateTime.now());
+        otpStorage.put(email, details);
+        // Simulate sending OTP via email (in production, integrate with an email service)
+        log.info("Sending OTP {} to {}", otp, email);
+        return otp;
+    }
+
+    public boolean verifyOTP(String email, String otp) {
+        OTPDetails details = otpStorage.get(email);
+        if (details == null) {
+            return false;
+        }
+        // Check if OTP has expired
+        if (details.generatedAt().plusSeconds(OTP_EXPIRATION_SECONDS).isBefore(LocalDateTime.now())) {
+            otpStorage.remove(email);
+            return false;
+        }
+        if (details.otp().equals(otp)) {
+            otpStorage.remove(email);
+            return true;
+        }
+        return false;
+    }
+
+    private record OTPDetails(String otp, LocalDateTime generatedAt) {
+    }
+}
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
index a0f2b58a7fb614b52250942544d45fe65ce53a0c..b7c904d50fe81e4261f1b3a6d8da9bc3c7e217cf 100644
--- a/src/main/resources/application-dev.properties
+++ b/src/main/resources/application-dev.properties
@@ -1,8 +1,8 @@
 spring.application.name=authentication
 
-spring.datasource.url=${DB_URL}
-spring.datasource.username=${DB_PASSWORD}
-spring.datasource.password=${DB_PASSWORD}
+spring.datasource.url=jdbc:postgresql://localhost:5432/be-authentication
+spring.datasource.username=postgres
+spring.datasource.password=postgres
 
 spring.datasource.driver-class-name=org.postgresql.Driver
 spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
diff --git a/src/main/resources/application-staging.properties b/src/main/resources/application-staging.properties
index c12917add41ed81cc9e56817d979dd07c7db729e..8491638fcdc38630d4c9b9b2ffafac01f9774c36 100644
--- a/src/main/resources/application-staging.properties
+++ b/src/main/resources/application-staging.properties
@@ -1 +1,12 @@
-spring.application.name=authentication
\ No newline at end of file
+spring.application.name=authentication
+
+spring.datasource.url=${JDBC_STAGING_DATABASE_URL}
+spring.datasource.username=${JDBC_STAGING_DATABASE_USERNAME}
+spring.datasource.password=${JDBC_STAGING_DATABASE_PASSWORD}
+
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
+
+# Hibernate Properties
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.show-sql=true
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index c12917add41ed81cc9e56817d979dd07c7db729e..7087d052396c6c6e70c607393a2065ea43c6fec0 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1 +1,2 @@
-spring.application.name=authentication
\ No newline at end of file
+spring.application.name=authentication
+spring.profiles.active=${PRODUCTION:dev}
\ No newline at end of file
diff --git a/src/test/java/com/safetypin/authentication/AuthenticationApplicationTests.java b/src/test/java/com/safetypin/authentication/AuthenticationApplicationTests.java
index 599099f38578d629b37983ce7119b66c97b2915b..a528070135724a4e7a25ed890042dfd3bcf6b5cc 100644
--- a/src/test/java/com/safetypin/authentication/AuthenticationApplicationTests.java
+++ b/src/test/java/com/safetypin/authentication/AuthenticationApplicationTests.java
@@ -1,13 +1,13 @@
 package com.safetypin.authentication;
 
 import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 
-@SpringBootTest
-class AuthenticationApplicationTests {
+class AuthenticationApplicationTest {
 
 	@Test
-	void contextLoads() {
+	void testMainDoesNotThrowException() {
+		// Calling the main method should load the context without throwing an exception.
+		assertDoesNotThrow(() -> AuthenticationApplication.main(new String[] {}));
 	}
-
 }
diff --git a/src/test/java/com/safetypin/authentication/HelloControllerTest.java b/src/test/java/com/safetypin/authentication/HelloControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb308d4ee48d94b9afc7856f1d598ef9a30217a3
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/HelloControllerTest.java
@@ -0,0 +1,15 @@
+package com.safetypin.authentication;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class HelloControllerTest {
+
+    private final HelloController helloController = new HelloController();
+
+    @Test
+    void testSayHelloReturnsHelloWorld() {
+        String greeting = helloController.sayHello();
+        assertEquals("Hello, World!", greeting);
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/controller/AuthenticationControllerTest.java b/src/test/java/com/safetypin/authentication/controller/AuthenticationControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ecff60c1642d9912d22e5e55e589914d83df9cd
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/controller/AuthenticationControllerTest.java
@@ -0,0 +1,216 @@
+package com.safetypin.authentication.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.safetypin.authentication.dto.PasswordResetRequest;
+import com.safetypin.authentication.dto.RegistrationRequest;
+import com.safetypin.authentication.dto.SocialLoginRequest;
+import com.safetypin.authentication.model.User;
+import com.safetypin.authentication.service.AuthenticationService;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.http.MediaType;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.time.LocalDate;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+@WebMvcTest(AuthenticationController.class)
+@Import({AuthenticationControllerTest.TestConfig.class, AuthenticationControllerTest.TestSecurityConfig.class})
+class AuthenticationControllerTest {
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Autowired
+    private AuthenticationService authenticationService;
+
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @TestConfiguration
+    static class TestConfig {
+        @Bean
+        public AuthenticationService authenticationService() {
+            return Mockito.mock(AuthenticationService.class);
+        }
+    }
+
+    @TestConfiguration
+    static class TestSecurityConfig {
+        @Bean
+        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+            http.csrf(AbstractHttpConfigurer::disable)
+                    .authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll());
+            return http.build();
+        }
+    }
+
+
+    @Test
+    void testRegisterEmail() throws Exception {
+        RegistrationRequest request = new RegistrationRequest();
+        request.setEmail("email@example.com");
+        request.setPassword("password");
+        request.setName("Test User");
+        request.setBirthdate(LocalDate.now().minusYears(20));
+
+        User user = new User();
+        user.setEmail("email@example.com");
+        user.setPassword("encodedPassword");
+        user.setName("Test User");
+        user.setRole("USER");
+        user.setBirthdate(request.getBirthdate());
+        user.setProvider("EMAIL");
+
+        user.setId(1L);
+        Mockito.when(authenticationService.registerUser(any(RegistrationRequest.class))).thenReturn(user);
+
+        mockMvc.perform(post("/api/auth/register-email")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(objectMapper.writeValueAsString(request)))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.data.id").value(1L))
+                .andExpect(jsonPath("$.data.email").value("email@example.com"));
+    }
+
+    @Test
+    void testRegisterSocial() throws Exception {
+        SocialLoginRequest request = new SocialLoginRequest();
+        request.setProvider("GOOGLE");
+        request.setSocialToken("token");
+        request.setEmail("social@example.com");
+        request.setName("Social User");
+        request.setBirthdate(LocalDate.now().minusYears(25));
+        request.setSocialId("social123");
+
+        User user = new User();
+        user.setEmail("social@example.com");
+        user.setPassword(null);
+        user.setName("Social User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(request.getBirthdate());
+        user.setProvider("GOOGLE");
+        user.setSocialId("social123");
+        user.setId(2L);
+        Mockito.when(authenticationService.socialLogin(any(SocialLoginRequest.class))).thenReturn(user);
+
+        mockMvc.perform(post("/api/auth/register-social")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(objectMapper.writeValueAsString(request)))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.data.id").value(2L))
+                .andExpect(jsonPath("$.data.email").value("social@example.com"));
+    }
+
+    @Test
+    void testLoginEmail() throws Exception {
+        User user = new User();
+        user.setEmail("email@example.com");
+        user.setPassword("encodedPassword");
+        user.setName("Test User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(20));
+        user.setProvider("EMAIL");
+        user.setSocialId(null);
+
+        user.setId(1L);
+        Mockito.when(authenticationService.loginUser("email@example.com", "password")).thenReturn(user);
+
+        mockMvc.perform(post("/api/auth/login-email")
+                        .param("email", "email@example.com")
+                        .param("password", "password"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.id").value(1L))
+                .andExpect(jsonPath("$.email").value("email@example.com"));
+    }
+
+    @Test
+    void testLoginSocial() throws Exception {
+        User user = new User();
+        user.setEmail("social@example.com");
+        user.setPassword(null);
+        user.setName("Social User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(25));
+        user.setProvider("GOOGLE");
+        user.setSocialId("social123");
+        user.setId(2L);
+        Mockito.when(authenticationService.loginSocial("social@example.com")).thenReturn(user);
+
+        mockMvc.perform(post("/api/auth/login-social")
+                        .param("email", "social@example.com"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.id").value(2L))
+                .andExpect(jsonPath("$.email").value("social@example.com"));
+    }
+
+    @Test
+    void testVerifyOTP_Success() throws Exception {
+        Mockito.when(authenticationService.verifyOTP("email@example.com", "123456")).thenReturn(true);
+
+        mockMvc.perform(post("/api/auth/verify-otp")
+                        .param("email", "email@example.com")
+                        .param("otp", "123456"))
+                .andExpect(status().isOk())
+                .andExpect(content().string("User verified successfully"));
+    }
+
+    @Test
+    void testVerifyOTP_Failure() throws Exception {
+        Mockito.when(authenticationService.verifyOTP("email@example.com", "000000")).thenReturn(false);
+
+        mockMvc.perform(post("/api/auth/verify-otp")
+                        .param("email", "email@example.com")
+                        .param("otp", "000000"))
+                .andExpect(status().isOk())
+                .andExpect(content().string("OTP verification failed"));
+    }
+
+    @Test
+    void testForgotPassword() throws Exception {
+        PasswordResetRequest request = new PasswordResetRequest();
+        request.setEmail("email@example.com");
+
+        Mockito.doNothing().when(authenticationService).forgotPassword("email@example.com");
+
+        mockMvc.perform(post("/api/auth/forgot-password")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(objectMapper.writeValueAsString(request)))
+                .andExpect(status().isOk())
+                .andExpect(content().string("Password reset instructions have been sent to your email (simulated)"));
+    }
+
+    @Test
+    void testPostContent() throws Exception {
+        Mockito.when(authenticationService.postContent("email@example.com", "Test Content"))
+                .thenReturn("Content posted successfully");
+
+        mockMvc.perform(post("/api/auth/post")
+                        .param("email", "email@example.com")
+                        .param("content", "Test Content"))
+                .andExpect(status().isOk())
+                .andExpect(content().string("Content posted successfully"));
+    }
+
+    @Test
+    void testDashboard() throws Exception {
+        mockMvc.perform(get("/api/auth/dashboard"))
+                .andExpect(status().isOk())
+                .andExpect(content().string("{}"));
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/dto/ErrorResponseTest.java b/src/test/java/com/safetypin/authentication/dto/ErrorResponseTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1398d5a2527655c91f052f4ca71b62f1a3003f55
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/dto/ErrorResponseTest.java
@@ -0,0 +1,18 @@
+package com.safetypin.authentication.dto;
+
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import java.time.LocalDateTime;
+
+class ErrorResponseTest {
+
+    @Test
+    void testErrorResponseConstructor() {
+        ErrorResponse errorResponse = new ErrorResponse(404, "Resource not found");
+
+        assertThat(errorResponse.getStatus()).isEqualTo(404);
+        assertThat(errorResponse.getMessage()).isEqualTo("Resource not found");
+        assertThat(errorResponse.getTimestamp()).isNotNull();
+        assertThat(errorResponse.getTimestamp()).isBeforeOrEqualTo(LocalDateTime.now());
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/dto/PasswordResetRequestTest.java b/src/test/java/com/safetypin/authentication/dto/PasswordResetRequestTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..013ec5dcdbecc804e717e1185c093433603614a8
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/dto/PasswordResetRequestTest.java
@@ -0,0 +1,37 @@
+package com.safetypin.authentication.dto;
+
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
+import jakarta.validation.ConstraintViolation;
+import java.util.Set;
+
+class PasswordResetRequestTest {
+
+    private final Validator validator;
+
+    public PasswordResetRequestTest() {
+        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+        this.validator = factory.getValidator();
+    }
+
+    @Test
+    void testPasswordResetRequestValid() {
+        PasswordResetRequest request = new PasswordResetRequest();
+        request.setEmail("user@example.com");
+
+        Set<ConstraintViolation<PasswordResetRequest>> violations = validator.validate(request);
+        assertThat(violations).isEmpty();
+    }
+
+    @Test
+    void testPasswordResetRequestInvalidEmail() {
+        PasswordResetRequest request = new PasswordResetRequest();
+        request.setEmail("invalid-email");
+
+        Set<ConstraintViolation<PasswordResetRequest>> violations = validator.validate(request);
+        assertThat(violations).isNotEmpty();
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/dto/RegistrationRequestTest.java b/src/test/java/com/safetypin/authentication/dto/RegistrationRequestTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b231e6732dc2eba209553a509fac64f1b35e3120
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/dto/RegistrationRequestTest.java
@@ -0,0 +1,42 @@
+package com.safetypin.authentication.dto;
+
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
+import jakarta.validation.ConstraintViolation;
+import java.time.LocalDate;
+import java.util.Set;
+
+class RegistrationRequestTest {
+
+    private final Validator validator;
+
+    public RegistrationRequestTest() {
+        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+        this.validator = factory.getValidator();
+    }
+
+    @Test
+    void testRegistrationRequestValid() {
+        RegistrationRequest request = new RegistrationRequest();
+        request.setEmail("user@example.com");
+        request.setPassword("securePassword");
+        request.setName("John Doe");
+        request.setBirthdate(LocalDate.of(1995, 5, 10));
+
+        Set<ConstraintViolation<RegistrationRequest>> violations = validator.validate(request);
+        assertThat(violations).isEmpty();
+    }
+
+    @Test
+    void testRegistrationRequestMissingFields() {
+        RegistrationRequest request = new RegistrationRequest(); // Missing required fields
+
+        Set<ConstraintViolation<RegistrationRequest>> violations = validator.validate(request);
+        assertThat(violations)
+                .isNotEmpty()
+                .hasSize(4); // Email, password, name, and birthdate should all be invalid
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/dto/SocialLoginRequestTest.java b/src/test/java/com/safetypin/authentication/dto/SocialLoginRequestTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe4a2a68ff908394936cc3f3b187054577c34d24
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/dto/SocialLoginRequestTest.java
@@ -0,0 +1,44 @@
+package com.safetypin.authentication.dto;
+
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
+import jakarta.validation.ConstraintViolation;
+import java.time.LocalDate;
+import java.util.Set;
+
+class SocialLoginRequestTest {
+
+    private final Validator validator;
+
+    public SocialLoginRequestTest() {
+        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+        this.validator = factory.getValidator();
+    }
+
+    @Test
+    void testSocialLoginRequestValid() {
+        SocialLoginRequest request = new SocialLoginRequest();
+        request.setProvider("GOOGLE");
+        request.setSocialToken("validToken");
+        request.setEmail("socialuser@example.com");
+        request.setName("Social User");
+        request.setBirthdate(LocalDate.of(2000, 1, 1));
+        request.setSocialId("123456789");
+
+        Set<ConstraintViolation<SocialLoginRequest>> violations = validator.validate(request);
+        assertThat(violations).isEmpty();
+    }
+
+    @Test
+    void testSocialLoginRequestMissingFields() {
+        SocialLoginRequest request = new SocialLoginRequest(); // Missing required fields
+
+        Set<ConstraintViolation<SocialLoginRequest>> violations = validator.validate(request);
+        assertThat(violations)
+                .isNotEmpty()
+                .hasSize(6); // All fields should be invalid
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/model/UserTest.java b/src/test/java/com/safetypin/authentication/model/UserTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b3c180b9f24c4b15bbe20ff64e5679ac77fd146
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/model/UserTest.java
@@ -0,0 +1,93 @@
+package com.safetypin.authentication.model;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class UserTest {
+
+    @Test
+    void testDefaultConstructorDefaults() {
+        User user = new User();
+        // Verify that default constructor sets all fields to their default values
+        assertNull(user.getId(), "Default id should be null");
+        assertNull(user.getEmail(), "Default email should be null");
+        assertNull(user.getPassword(), "Default password should be null");
+        assertNull(user.getName(), "Default name should be null");
+        assertFalse(user.isVerified(), "Default isVerified should be false");
+        assertNull(user.getRole(), "Default role should be null");
+        assertNull(user.getBirthdate(), "Default birthdate should be null");
+        assertNull(user.getProvider(), "Default provider should be null");
+        assertNull(user.getSocialId(), "Default socialId should be null");
+    }
+
+    @Test
+    void testSettersAndGetters() {
+        User user = new User();
+        Long id = 123L;
+        String email = "test@example.com";
+        String password = "secret";
+        String name = "Test User";
+        boolean verified = true;
+        String role = "ADMIN";
+        LocalDate birthdate = LocalDate.of(2000, 1, 1);
+        String provider = "GOOGLE";
+        String socialId = "social123";
+
+        user.setId(id);
+        user.setEmail(email);
+        user.setPassword(password);
+        user.setName(name);
+        user.setVerified(verified);
+        user.setRole(role);
+        user.setBirthdate(birthdate);
+        user.setProvider(provider);
+        user.setSocialId(socialId);
+
+        assertEquals(id, user.getId());
+        assertEquals(email, user.getEmail());
+        assertEquals(password, user.getPassword());
+        assertEquals(name, user.getName());
+        assertTrue(user.isVerified());
+        assertEquals(role, user.getRole());
+        assertEquals(birthdate, user.getBirthdate());
+        assertEquals(provider, user.getProvider());
+        assertEquals(socialId, user.getSocialId());
+    }
+
+    @Test
+    void testParameterizedConstructor() {
+        String email = "test2@example.com";
+        String password = "password123";
+        String name = "Another User";
+        boolean verified = false;
+        String role = "USER";
+        LocalDate birthdate = LocalDate.of(1995, 5, 15);
+        String provider = "EMAIL";
+        String socialId = null;
+
+        User user = new User();
+        user.setEmail(email);
+        user.setPassword(password);
+        user.setName(name);
+        user.setVerified(verified);
+        user.setRole(role);
+        user.setBirthdate(birthdate);
+        user.setProvider(provider);
+        user.setSocialId(socialId);
+
+
+        // id remains null until set (by the persistence layer)
+        assertNull(user.getId(), "Id should be null when not set");
+        assertEquals(email, user.getEmail());
+        assertEquals(password, user.getPassword());
+        assertEquals(name, user.getName());
+        assertEquals(verified, user.isVerified());
+        assertEquals(role, user.getRole());
+        assertEquals(birthdate, user.getBirthdate());
+        assertEquals(provider, user.getProvider());
+        assertNull(user.getSocialId(), "SocialId should be null");
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/repository/UserRepositoryTest.java b/src/test/java/com/safetypin/authentication/repository/UserRepositoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd7e4ff0de472cd55392cdb8ba9b5430095be556
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/repository/UserRepositoryTest.java
@@ -0,0 +1,46 @@
+package com.safetypin.authentication.repository;
+
+import com.safetypin.authentication.model.User;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@DataJpaTest
+class UserRepositoryTest {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @BeforeEach
+    void setUp() {
+        userRepository.deleteAll();
+
+        // Create and save a User entity
+        User user = new User();
+        user.setEmail("test@example.com");
+        user.setPassword("password");
+        user.setName("Test User");
+        user.setRole("USER");
+        userRepository.save(user);
+    }
+
+    @Test
+    void testFindByEmailWhenUserExists() {
+        // Retrieve the user by email
+        User foundUser = userRepository.findByEmail("test@example.com");
+        assertNotNull(foundUser, "Expected to find a user with the given email");
+        assertEquals("test@example.com", foundUser.getEmail());
+        assertEquals("Test User", foundUser.getName());
+    }
+
+    @Test
+    void testFindByEmailWhenUserDoesNotExist() {
+        // Attempt to find a user that doesn't exist
+        User foundUser = userRepository.findByEmail("nonexistent@example.com");
+        assertNull(foundUser, "Expected no user to be found for a non-existent email");
+    }
+
+}
diff --git a/src/test/java/com/safetypin/authentication/seeder/DevDataSeederTest.java b/src/test/java/com/safetypin/authentication/seeder/DevDataSeederTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..04e4931d56f4a30d4e83ca5236abc3ae4c4d550d
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/seeder/DevDataSeederTest.java
@@ -0,0 +1,62 @@
+package com.safetypin.authentication.seeder;
+
+import com.safetypin.authentication.model.User;
+import com.safetypin.authentication.repository.UserRepository;
+import jakarta.transaction.Transactional;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@SpringBootTest
+@ActiveProfiles("dev")  // Use the 'dev' profile during tests
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@Transactional
+class DevDataSeederTest {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+
+    // Test that the seeder inserts 5 users when no users exist
+    @Test
+    void testSeederInsertsUsersWhenEmpty() {
+        userRepository.deleteAll(); // Ensure the database is empty before seeding
+
+        new DevDataSeeder(userRepository, passwordEncoder).run(); // Run the seeder
+
+        List<User> users = userRepository.findAll();
+        assertEquals(5, users.size(), "Seeder should insert 5 users when repository is empty");
+    }
+
+    // Test that the seeder does not add any users if at least one user already exists
+    @Test
+    void testSeederDoesNotInsertIfUsersExist() {
+        // Save an existing user into the repository
+        User user = new User();
+        user.setEmail("existing@example.com");
+        user.setPassword(passwordEncoder.encode("test"));
+        user.setName("Existing User");
+        user.setVerified(true);
+        user.setRole("admin");
+        user.setBirthdate(LocalDate.of(1990, 1, 1));
+        user.setProvider("EMAIL");
+        user.setSocialId("social_9999");
+        userRepository.save(user);
+
+        long countBefore = userRepository.count();
+        new DevDataSeeder(userRepository, passwordEncoder).run();
+        long countAfter = userRepository.count();
+
+        assertEquals(countBefore, countAfter, "Seeder should not insert new users if users already exist");
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/service/AuthenticationServiceTest.java b/src/test/java/com/safetypin/authentication/service/AuthenticationServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..97b3d107e86541481058024dc228798d75c85308
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/service/AuthenticationServiceTest.java
@@ -0,0 +1,436 @@
+package com.safetypin.authentication.service;
+
+import com.safetypin.authentication.dto.RegistrationRequest;
+import com.safetypin.authentication.dto.SocialLoginRequest;
+import com.safetypin.authentication.exception.InvalidCredentialsException;
+import com.safetypin.authentication.exception.UserAlreadyExistsException;
+import com.safetypin.authentication.model.User;
+import com.safetypin.authentication.repository.UserRepository;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class AuthenticationServiceTest {
+
+    @Mock
+    private UserRepository userRepository;
+
+    @Mock
+    private PasswordEncoder passwordEncoder;
+
+    @Mock
+    private OTPService otpService;
+
+    @InjectMocks
+    private AuthenticationService authenticationService;
+
+    // registerUser tests
+
+    @Test
+    void testRegisterUser_UnderAge() {
+        RegistrationRequest request = new RegistrationRequest();
+        request.setEmail("test@example.com");
+        request.setPassword("password");
+        request.setName("Test User");
+        // set birthdate to 17 years old
+        request.setBirthdate(LocalDate.now().minusYears(15));
+
+        Exception exception = assertThrows(IllegalArgumentException.class, () ->
+                authenticationService.registerUser(request)
+        );
+        assertEquals("User must be at least 16 years old", exception.getMessage());
+    }
+
+    @Test
+    void testRegisterUser_DuplicateEmail() {
+        RegistrationRequest request = new RegistrationRequest();
+        request.setEmail("test@example.com");
+        request.setPassword("password");
+        request.setName("Test User");
+        request.setBirthdate(LocalDate.now().minusYears(20));
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(new User());
+
+        Exception exception = assertThrows(UserAlreadyExistsException.class, () ->
+                authenticationService.registerUser(request)
+        );
+        assertTrue(exception.getMessage().contains("Email address is already registered"));
+    }
+
+    @Test
+    void testRegisterUser_Success() {
+        RegistrationRequest request = new RegistrationRequest();
+        request.setEmail("test@example.com");
+        request.setPassword("password");
+        request.setName("Test User");
+        request.setBirthdate(LocalDate.now().minusYears(20));
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(null);
+        when(passwordEncoder.encode("password")).thenReturn("encodedPassword");
+        User savedUser = new User();
+        savedUser.setEmail("test@example.com");
+        savedUser.setPassword("encodedPassword");
+        savedUser.setName("Test User");
+        savedUser.setVerified(false);
+        savedUser.setRole("USER");
+        savedUser.setBirthdate(request.getBirthdate());
+        savedUser.setProvider("EMAIL");
+        savedUser.setSocialId(null);
+
+        savedUser.setId(1L);
+        when(userRepository.save(any(User.class))).thenReturn(savedUser);
+
+        User result = authenticationService.registerUser(request);
+        assertNotNull(result);
+        assertEquals("test@example.com", result.getEmail());
+        // OTPService should be invoked to generate OTP.
+        verify(otpService, times(1)).generateOTP("test@example.com");
+    }
+
+    // socialLogin tests
+
+    @Test
+    void testSocialLogin_UnderAge() {
+        SocialLoginRequest request = new SocialLoginRequest();
+        request.setEmail("social@example.com");
+        request.setName("Social User");
+        request.setBirthdate(LocalDate.now().minusYears(15));
+        request.setProvider("GOOGLE");
+        request.setSocialId("social123");
+        request.setSocialToken("token");
+
+        Exception exception = assertThrows(IllegalArgumentException.class, () ->
+                authenticationService.socialLogin(request)
+        );
+        assertEquals("User must be at least 16 years old", exception.getMessage());
+    }
+
+    @Test
+    void testSocialLogin_DuplicateEmailWithEmailProvider() {
+        SocialLoginRequest request = new SocialLoginRequest();
+        request.setEmail("social@example.com");
+        request.setName("Social User");
+        request.setBirthdate(LocalDate.now().minusYears(25));
+        request.setProvider("APPLE");
+        request.setSocialId("social123");
+        request.setSocialToken("token");
+
+        User existingUser = new User();
+        existingUser.setEmail("social@example.com");
+        existingUser.setPassword("encodedPassword");
+        existingUser.setName("Existing User");
+        existingUser.setVerified(false);
+        existingUser.setRole("USER");
+        existingUser.setBirthdate(LocalDate.now().minusYears(30));
+        existingUser.setProvider("EMAIL");
+        existingUser.setSocialId(null);
+
+        when(userRepository.findByEmail("social@example.com")).thenReturn(existingUser);
+
+        Exception exception = assertThrows(UserAlreadyExistsException.class, () ->
+                authenticationService.socialLogin(request)
+        );
+        assertTrue(exception.getMessage().contains("An account with this email exists"));
+    }
+
+    @Test
+    void testSocialLogin_ExistingSocialUser() {
+        SocialLoginRequest request = new SocialLoginRequest();
+        request.setEmail("social@example.com");
+        request.setName("Social User");
+        request.setBirthdate(LocalDate.now().minusYears(25));
+        request.setProvider("GOOGLE");
+        request.setSocialId("social123");
+        request.setSocialToken("token");
+
+        User existingUser = new User();
+        existingUser.setEmail("social@example.com");
+        existingUser.setPassword(null);
+        existingUser.setName("Social User");
+        existingUser.setVerified(true);
+        existingUser.setRole("USER");
+        existingUser.setBirthdate(LocalDate.now().minusYears(25));
+        existingUser.setProvider("GOOGLE");
+        existingUser.setSocialId("social123");
+
+        when(userRepository.findByEmail("social@example.com")).thenReturn(existingUser);
+
+        User result = authenticationService.socialLogin(request);
+        assertNotNull(result);
+        assertEquals("social@example.com", result.getEmail());
+    }
+
+    @Test
+    void testSocialLogin_NewUser() {
+        SocialLoginRequest request = new SocialLoginRequest();
+        request.setEmail("social@example.com");
+        request.setName("Social User");
+        request.setBirthdate(LocalDate.now().minusYears(25));
+        request.setProvider("GOOGLE");
+        request.setSocialId("social123");
+        request.setSocialToken("token");
+
+        when(userRepository.findByEmail("social@example.com")).thenReturn(null);
+        User savedUser = new User();
+        savedUser.setEmail("social@example.com");
+        savedUser.setPassword(null);
+        savedUser.setName("Social User");
+        savedUser.setVerified(true);
+        savedUser.setRole("USER");
+        savedUser.setBirthdate(request.getBirthdate());
+        savedUser.setProvider("GOOGLE");
+        savedUser.setSocialId("social123");
+
+        savedUser.setId(2L);
+        when(userRepository.save(any(User.class))).thenReturn(savedUser);
+
+        User result = authenticationService.socialLogin(request);
+        assertNotNull(result);
+        assertEquals("social@example.com", result.getEmail());
+    }
+
+    // loginUser tests
+
+    @Test
+    void testLoginUser_EmailNotFound() {
+        when(userRepository.findByEmail("notfound@example.com")).thenReturn(null);
+        Exception exception = assertThrows(InvalidCredentialsException.class, () ->
+                authenticationService.loginUser("notfound@example.com", "password")
+        );
+        assertTrue(exception.getMessage().contains("Invalid email"));
+    }
+
+    @Test
+    void testLoginUser_InvalidPassword_NullPassword() {
+        User user = new User();
+        user.setEmail("test@example.com");
+        user.setPassword(null);
+        user.setName("Test User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(20));
+        user.setProvider("EMAIL");
+        user.setSocialId(null);
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(user);
+
+        Exception exception = assertThrows(InvalidCredentialsException.class, () ->
+                authenticationService.loginUser("test@example.com", "password")
+        );
+        assertTrue(exception.getMessage().contains("Invalid password"));
+    }
+
+    @Test
+    void testLoginUser_InvalidPassword_WrongMatch() {
+        User user = new User();
+        user.setEmail("test@example.com");
+        user.setPassword("encodedPassword");
+        user.setName("Test User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(20));
+        user.setProvider("EMAIL");
+        user.setSocialId(null);
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(user);
+        when(passwordEncoder.matches("wrongPassword", "encodedPassword")).thenReturn(false);
+
+        Exception exception = assertThrows(InvalidCredentialsException.class, () ->
+                authenticationService.loginUser("test@example.com", "wrongPassword")
+        );
+        assertTrue(exception.getMessage().contains("Invalid password"));
+    }
+
+    @Test
+    void testLoginUser_Success() {
+        User user = new User();
+        user.setEmail("test@example.com");
+        user.setPassword("encodedPassword");
+        user.setName("Test User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(20));
+        user.setProvider("EMAIL");
+        user.setSocialId(null);
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(user);
+        when(passwordEncoder.matches("password", "encodedPassword")).thenReturn(true);
+
+        User result = authenticationService.loginUser("test@example.com", "password");
+        assertNotNull(result);
+        assertEquals("test@example.com", result.getEmail());
+    }
+
+    // loginSocial tests
+
+    @Test
+    void testLoginSocial_UserNotFound() {
+        when(userRepository.findByEmail("notfound@example.com")).thenReturn(null);
+        Exception exception = assertThrows(InvalidCredentialsException.class, () ->
+                authenticationService.loginSocial("notfound@example.com")
+        );
+        assertTrue(exception.getMessage().contains("Social login failed"));
+    }
+
+    @Test
+    void testLoginSocial_Success() {
+        User user = new User();
+        user.setEmail("social@example.com");
+        user.setPassword(null);
+        user.setName("Social User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(25));
+        user.setProvider("GOOGLE");
+        user.setSocialId("social123");
+
+        when(userRepository.findByEmail("social@example.com")).thenReturn(user);
+
+        User result = authenticationService.loginSocial("social@example.com");
+        assertNotNull(result);
+        assertEquals("social@example.com", result.getEmail());
+    }
+
+    // verifyOTP tests
+
+    @Test
+    void testVerifyOTP_Success() {
+        // OTPService returns true and user is found
+        when(otpService.verifyOTP("test@example.com", "123456")).thenReturn(true);
+        User user = new User();
+        user.setEmail("test@example.com");
+        user.setPassword("encodedPassword");
+        user.setName("Test User");
+        user.setVerified(false);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(20));
+        user.setProvider("EMAIL");
+        user.setSocialId(null);
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(user);
+        when(userRepository.save(any(User.class))).thenReturn(user);
+
+        boolean result = authenticationService.verifyOTP("test@example.com", "123456");
+        assertTrue(result);
+        assertTrue(user.isVerified());
+        verify(userRepository, times(1)).save(user);
+    }
+
+    @Test
+    void testVerifyOTP_Success_UserNotFound() {
+        // OTPService returns true but user is not found
+        when(otpService.verifyOTP("nonexistent@example.com", "123456")).thenReturn(true);
+        when(userRepository.findByEmail("nonexistent@example.com")).thenReturn(null);
+
+        boolean result = authenticationService.verifyOTP("nonexistent@example.com", "123456");
+        assertTrue(result);
+        verify(userRepository, never()).save(any(User.class));
+    }
+
+    @Test
+    void testVerifyOTP_Failure() {
+        when(otpService.verifyOTP("test@example.com", "000000")).thenReturn(false);
+        boolean result = authenticationService.verifyOTP("test@example.com", "000000");
+        assertFalse(result);
+        verify(userRepository, never()).save(any(User.class));
+    }
+
+    // forgotPassword tests
+
+    @Test
+    void testForgotPassword_Success() {
+        User user = new User();
+        user.setEmail("test@example.com");
+        user.setPassword("encodedPassword");
+        user.setName("Test User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(20));
+        user.setProvider("EMAIL");
+        user.setSocialId(null);
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(user);
+
+        assertDoesNotThrow(() -> authenticationService.forgotPassword("test@example.com"));
+    }
+
+    @Test
+    void testForgotPassword_Invalid() {
+        // Case 1: user not found
+        when(userRepository.findByEmail("notfound@example.com")).thenReturn(null);
+        Exception exception1 = assertThrows(IllegalArgumentException.class, () ->
+                authenticationService.forgotPassword("notfound@example.com")
+        );
+        assertTrue(exception1.getMessage().contains("Password reset is only available for email-registered users."));
+
+        // Case 2: user exists but provider is not EMAIL
+        User user = new User();
+        user.setEmail("social@example.com");
+        user.setPassword(null);
+        user.setName("Social User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(25));
+        user.setProvider("GOOGLE");
+        user.setSocialId("social123");
+
+        when(userRepository.findByEmail("social@example.com")).thenReturn(user);
+        Exception exception2 = assertThrows(IllegalArgumentException.class, () ->
+                authenticationService.forgotPassword("social@example.com")
+        );
+        assertTrue(exception2.getMessage().contains("Password reset is only available for email-registered users."));
+    }
+
+    // postContent tests
+
+    @Test
+    void testPostContent_UserNotFound() {
+        when(userRepository.findByEmail("notfound@example.com")).thenReturn(null);
+        String response = authenticationService.postContent("notfound@example.com", "Content");
+        assertEquals("User not found. Please register.", response);
+    }
+
+    @Test
+    void testPostContent_UserNotVerified() {
+        User user = new User();
+        user.setEmail("test@example.com");
+        user.setPassword("encodedPassword");
+        user.setName("Test User");
+        user.setVerified(false);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(20));
+        user.setProvider("EMAIL");
+        user.setSocialId(null);
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(user);
+        String response = authenticationService.postContent("test@example.com", "Content");
+        assertTrue(response.contains("not verified"));
+    }
+
+    @Test
+    void testPostContent_UserVerified() {
+        User user = new User();
+        user.setEmail("test@example.com");
+        user.setPassword("encodedPassword");
+        user.setName("Test User");
+        user.setVerified(true);
+        user.setRole("USER");
+        user.setBirthdate(LocalDate.now().minusYears(20));
+        user.setProvider("EMAIL");
+        user.setSocialId(null);
+
+        when(userRepository.findByEmail("test@example.com")).thenReturn(user);
+        String response = authenticationService.postContent("test@example.com", "Content");
+        assertEquals("Content posted successfully", response);
+    }
+}
diff --git a/src/test/java/com/safetypin/authentication/service/OTPServiceTest.java b/src/test/java/com/safetypin/authentication/service/OTPServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfca094d53014f45858935dfa0f1c97eb354ac48
--- /dev/null
+++ b/src/test/java/com/safetypin/authentication/service/OTPServiceTest.java
@@ -0,0 +1,80 @@
+package com.safetypin.authentication.service;
+
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Constructor;
+import java.time.LocalDateTime;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OTPServiceTest {
+
+    @Test
+    void testGenerateOTP() {
+        OTPService otpService = new OTPService();
+        String email = "user@example.com";
+        String otp = otpService.generateOTP(email);
+        assertNotNull(otp, "OTP should not be null");
+        assertEquals(6, otp.length(), "OTP should be 6 characters long");
+        assertTrue(otp.matches("\\d{6}"), "OTP should consist of 6 digits");
+    }
+
+    @Test
+    void testVerifyOTPSuccess() {
+        OTPService otpService = new OTPService();
+        String email = "user@example.com";
+        String otp = otpService.generateOTP(email);
+        // Immediately verify the generated OTP; it should succeed.
+        boolean result = otpService.verifyOTP(email, otp);
+        assertTrue(result, "The OTP should verify successfully");
+    }
+
+    @Test
+    void testVerifyOTPWrongOtp() {
+        OTPService otpService = new OTPService();
+        String email = "user@example.com";
+        otpService.generateOTP(email);
+        // Try verifying with an incorrect OTP.
+        boolean result = otpService.verifyOTP(email, "000000");
+        assertFalse(result, "Verification should fail for an incorrect OTP");
+    }
+
+    @Test
+    void testVerifyOTPExpired() throws Exception {
+        OTPService otpService = new OTPService();
+        String email = "user@example.com";
+        String otp = otpService.generateOTP(email);
+
+        // Access the private otpStorage field via reflection.
+        java.lang.reflect.Field otpStorageField = OTPService.class.getDeclaredField("otpStorage");
+        otpStorageField.setAccessible(true);
+        @SuppressWarnings("unchecked")
+        ConcurrentHashMap<String, Object> otpStorage = (ConcurrentHashMap<String, Object>) otpStorageField.get(otpService);
+
+        // Retrieve the current OTPDetails instance.
+        Object oldOtpDetails = otpStorage.get(email);
+        assertNotNull(oldOtpDetails, "OTPDetails instance should exist");
+
+        // Use reflection to get the private constructor of OTPDetails.
+        Class<?> otpDetailsClass = oldOtpDetails.getClass();
+        Constructor<?> constructor = otpDetailsClass.getDeclaredConstructor(String.class, LocalDateTime.class);
+        constructor.setAccessible(true);
+        // Create a new OTPDetails instance with an expired time (3 minutes ago).
+        Object expiredOtpDetails = constructor.newInstance(otp, LocalDateTime.now().minusMinutes(3));
+        // Replace the old OTPDetails with the expired one.
+        otpStorage.put(email, expiredOtpDetails);
+
+        // Now verification should fail because the OTP is expired.
+        boolean result = otpService.verifyOTP(email, otp);
+        assertFalse(result, "The OTP should be expired and verification should fail");
+    }
+
+    @Test
+    void testVerifyOTPWhenNotGenerated() {
+        OTPService otpService = new OTPService();
+        // No OTP was generated for this email, so verification should return false.
+        boolean result = otpService.verifyOTP("nonexistent@example.com", "123456");
+        assertFalse(result, "Verification should fail when no OTP is generated for the given email");
+    }
+}
diff --git a/src/test/resources/application-dev.properties b/src/test/resources/application-dev.properties
new file mode 100644
index 0000000000000000000000000000000000000000..e42e68fd7cebc1b0e033fbdaadfad2e93ffa1545
--- /dev/null
+++ b/src/test/resources/application-dev.properties
@@ -0,0 +1,14 @@
+# src/test/resources/application-dev.properties
+
+# Configure H2 in-memory database
+spring.datasource.url=jdbc:h2:mem:devdb;DB_CLOSE_DELAY=-1
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+
+# JPA/Hibernate configurations
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.jpa.hibernate.ddl-auto=create-drop
+
+# (Optional) Enable SQL logging
+spring.jpa.show-sql=true
diff --git a/staging.yaml b/staging.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..91457e76499c95f3a4b2718dfaf62f90a0dda88b
--- /dev/null
+++ b/staging.yaml
@@ -0,0 +1,34 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: authentication
+spec:
+  type: LoadBalancer
+  selector:
+    app: authentication
+  ports:
+    - port: 80
+      targetPort: 8080
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: authentication
+  labels:
+    app: authentication
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: authentication
+  template:
+    metadata:
+      labels:
+        app: authentication
+    spec:
+      containers:
+        - name: authentication
+          image: us-central1-docker.pkg.dev/GOOGLE_PROJECT/staging-repository/authentication:latest
+          ports:
+            - containerPort: 8080
\ No newline at end of file