diff --git a/README.md b/README.md index a0dcac2..5aa1d9a 100644 --- a/README.md +++ b/README.md @@ -1,259 +1,140 @@ -# 🔐 Secure Wallet API +# Secure Wallet API -A **learning project** built to explore real-world security practices in the context of a digital savings wallet REST API. +Multi-currency digital savings wallet — REST API with authentication, transactions, and audit trail. -Built with **Java 17**, **Spring Boot 3.4.2**, **PostgreSQL 16** and **Docker**. +Built with **Java 17**, **Spring Boot 3.4.2**, and **PostgreSQL 16**. -> **Security is a first-class concern here — not an afterthought.** -> Every architectural decision is documented, every security rule references OWASP, and every sensitive operation is audited. +> This is a learning project built with AI assistance to explore how a savings wallet API works under the hood — authentication flows, ACID financial transactions, audit logging, and security practices. Some sections still need review and polish. --- -## 🎯 What This Project Is +## What is this -This is an academic/portfolio project designed to answer a specific question: +A REST API for a digital savings wallet where users can: -> *How would a financial API look if security, auditability, and correctness were treated as requirements from day one — before a single line of business code is written?* +- Register and manage their profile +- Create savings wallets in multiple currencies (USD, EUR, COP, MXN, ARS) +- Deposit, withdraw, and transfer funds between wallets +- Track every operation through a full audit trail +- Authenticate with dual-token JWT and optional two-factor authentication -The result is a digital savings wallet where users can deposit, withdraw, and transfer money between accounts — built with the same security discipline you'd expect in a real fintech product. - -**Core priorities (in order):** -1. **Security** — OWASP Top 10 (2025) compliance throughout -2. **Correctness** — ACID financial transactions, no money lost -3. **Auditability** — every sensitive operation logged and traceable -4. **Maintainability** — clean, documented, testable code +The idea came from wanting to understand the moving parts of a real savings wallet — not just the happy path, but edge cases, concurrency, token rotation, and what happens when things go wrong. --- -## ✨ Features +## Features -### 👤 User Management -- Register, update profile, and soft-delete accounts -- Role-based access control: `USER`, `ADMIN`, `MANAGER` -- Account lockout after 5 failed login attempts (30-minute cooldown) -- Two-Factor Authentication (2FA / TOTP — RFC 6238, Google Authenticator compatible) -- Email verification flag and login tracking +### User Management +- Registration, profile updates, and soft-delete accounts +- Role-based access: `USER`, `ADMIN`, `MANAGER` +- Account lockout after repeated failed login attempts +- Two-Factor Authentication via TOTP (compatible with Google Authenticator, Authy) -### 💰 Savings Wallets +### Multi-Currency Savings Wallets - One wallet per user per currency -- Multi-currency support: USD, EUR, COP, MXN, ARS -- Real-time balance with ACID-guaranteed updates +- Supported currencies: USD, EUR, COP, MXN, ARS - Wallet states: `ACTIVE`, `SUSPENDED`, `CLOSED` +- Balance updates with pessimistic locking -### 💸 Financial Transactions +### Financial Transactions | Operation | Description | |-----------|-------------| -| **Deposit** | Add funds from an external source into a wallet | -| **Withdrawal** | Move funds from a wallet to an external destination | -| **Transfer** | Move funds between any two wallets in the system | -| **History** | Full state-change log per transaction | - -- 2FA required for operations above **$100** -- Transaction limit: $10,000 | Daily limit: $50,000 -- Pessimistic locking (`FOR UPDATE`) on all balance reads +| Deposit | Add funds from an external source | +| Withdrawal | Move funds out to an external destination | +| Transfer | Move funds between two wallets in the system | +| History | State-change log per transaction | + +- Configurable limits: max per transaction, max per day +- 2FA threshold: operations above a configurable amount require a TOTP code +- Pessimistic locking on balance reads to prevent race conditions - Unique reference code per transaction -- JSONB metadata (IP, device, geolocation) for fraud detection - -### 🔒 Security & DevSecOps -- Dual-token JWT: Access Token (15 min) + Refresh Token (7 days) -- Refresh Token rotation and revocation stored in the database -- SHA-256 for token hashing — BCrypt only for passwords -- UUID primary keys on all entities (prevents enumeration) -- Stack traces never exposed to the client -- OWASP Dependency Check in CI — **fails build on CVSS ≥ 7** -- CodeQL static analysis on every PR - -### 📋 Audit Trail -- Independent `audit/` domain — auditing is a business requirement, not a cross-cutting concern -- Every sensitive operation generates an `audit_logs` entry -- IP address, User-Agent, timestamp, and JSONB details per event -- PostgreSQL triggers for automatic state-change auditing + +### Authentication & Security +- Dual-token JWT: Access Token (short-lived) + Refresh Token (longer-lived with rotation) +- Refresh token stored as SHA-256 hash — real revocation on logout +- UUID primary keys on all entities (no sequential IDs) +- Stack traces never reach the client — all errors go through a centralized handler +- Input validation on every endpoint + +### Audit Trail +- Every sensitive operation generates a log entry +- IP address, User-Agent, timestamp, and contextual details per event +- Database triggers for automatic state-change tracking --- -## 🏗️ Architecture +## Architecture -**Pattern:** Hybrid Domain-Driven Design — layers organized **by domain**, never by class type. +**Pattern:** Domain-Driven Design — code organized by business domain, not by layer type. ``` com.wallet.secure/ ├── config/ # SecurityConfig, OpenApiConfig, AuditConfig, JwtConfig ├── auth/ # Authentication: JWT, 2FA, sessions -│ ├── controller/ # AuthController, SessionController -│ ├── dto/ # LoginRequest, AuthResponse, RefreshTokenRequest -│ ├── entity/ # Session -│ ├── repository/ # SessionRepository -│ ├── security/ # JwtAuthFilter, JwtService, UserDetailsServiceImpl -│ └── service/ # AuthService, SessionService +│ ├── controller/, dto/, entity/, repository/, security/, service/ ├── user/ # Users and profiles ├── wallet/ # Wallets and balances -├── transaction/ # Transactions + history (business core) -├── audit/ # Security audit trail +├── transaction/ # Transactions and history (business core) +├── audit/ # Security audit log └── common/ # ApiResponse, exceptions, enums, validators ``` -**Architecture rule:** NEVER create root-level folders by type (`controllers/`, `services/`). Each domain owns its own layers. - -> 📖 [`CONTEXT.md`](CONTEXT.md) — Single source of truth: full architecture, stack, conventions, and rules. -> 📖 [`DECISIONS.md`](DECISIONS.md) — 8 Architecture Decision Records with full reasoning for every major choice. - ---- - -## 🛡️ Security Model - -### Why Authentication and Authorization Matter Here - -A wallet API without a solid auth model is not a wallet — it's an open bank account. - -This project enforces two distinct layers: - -**Authentication** — *Who are you?* -Dual-token JWT ensures a stolen Access Token is useless after 15 minutes. The Refresh Token is stored as a SHA-256 hash in the database, enabling real revocation on logout. Sessions are tracked per device. - -**Authorization** — *What are you allowed to do?* -`userId` is **always** extracted from the JWT — never from the request body or path parameters. This means a user cannot access another user's wallet by simply changing an ID in the URL. Resources respond with `404` (not `403`) to prevent enumeration. - -``` -POST /auth/login - → Validate credentials (BCrypt password check) - → Check account not locked - → If operation requires 2FA → POST /auth/2fa/verify - → Issue Access Token (15 min) + Refresh Token (7 days) - → Register session in DB - → Audit log event - → Return tokens - -POST /auth/refresh - → Validate Refresh Token (signature + expiry + exists in DB) - → Rotate token (invalidate old, issue new) - → Return new Access Token + new Refresh Token - -POST /auth/logout - → Revoke Refresh Token in DB - → Audit log event - → Access Token expires naturally within 15 min -``` - -### Session Parameters - -| Parameter | Value | Reason | -|-----------|-------|--------| -| Access Token TTL | 15 minutes | Limits exposure if stolen | -| Refresh Token TTL | 7 days | Security/UX balance | -| Max inactivity | 30 minutes | Banking standard | -| Concurrent sessions | Allowed | Tracked per device in DB | -| 2FA threshold | $100 | Protects everyday transactions, not just large ones | - -### OWASP Top 10 (2025) Coverage - -> OWASP Top 10 2025 was published on November 6, 2025 at the Global AppSec Conference in Washington D.C. - -| # | Risk | Status | How it is addressed | -|---|------|--------|---------------------| -| A01 | Broken Access Control *(includes SSRF)* | ✅ | `userId` from JWT only — never from request. `404` on ownership mismatch. RBAC via Spring Security. API makes no outbound calls (SSRF N/A) | -| A02 | Cryptographic Failures | ✅ | BCrypt strength 12 for passwords. SHA-256 for token hashing. JWT signed with HS256. HTTPS required in production. `DECIMAL(19,4)` for money — no float precision errors | -| A03 | Software Supply Chain Failures 🆕 | ✅ | OWASP Dependency Check in CI (`failOnCVSS ≥ 7`). All dependency versions pinned in `pom.xml` | -| A04 | Injection | ✅ | Hibernate prepared statements on all queries. Bean Validation (`@Valid`) on every endpoint input | -| A05 | Security Misconfiguration | ✅ | Error messages never expose internals. Stack traces never sent to client. No default credentials. Secure HTTP headers. Limited Actuator exposure | -| A06 | Insecure Design | ✅ | Documented threat model. Transaction limits. 2FA threshold at $100. Pessimistic locking for concurrent balance reads | -| A07 | Identification & Authentication Failures | ✅ | Dual-token JWT (15 min / 7 days). TOTP 2FA (RFC 6238). Account lockout. 30-min inactivity. Refresh Token rotation and revocation | -| A08 | Software and Data Integrity Failures | ✅ | CodeQL static analysis in GitHub Actions. SQL schema versioned in Git. Hibernate `ddl-auto: validate` | -| A09 | Security Logging & Monitoring Failures | ✅ | Every security event logged to `audit_logs`. Log4j2 async logging. No passwords or tokens ever written to logs | -| A10 | Mishandling of Exceptional Conditions 🆕 | ✅ | Centralized `GlobalExceptionHandler` — never "fail open". No sensitive data in error responses | - ---- - -## 🗄️ Database - -PostgreSQL 16 with schema managed exclusively by versioned SQL scripts. Hibernate only validates — never creates or modifies tables. - -| # | Script | Purpose | -|---|--------|---------| -| 01 | `extensions.sql` | PostgreSQL extensions: `pgcrypto`, `uuid-ossp` | -| 02 | `types.sql` | 7 custom ENUMs (roles, currencies, statuses) | -| 03 | `tables.sql` | 6 core tables with constraints and FK rules | -| 04 | `index.sql` | Partial, composite, and GIN indexes | -| 05 | `triggers.sql` | Auto-update timestamps, balance validation, state-change audit | -| 06 | `functions.sql` | `process_transaction()` ACID, `cleanup_expired_sessions()` | -| 07 | `seed.sql` | Development test data only | -| 08 | `migrations.sql` | Incremental schema changes | - -> 📖 [`database/README.md`](database/README.md) — Full ER diagram, FK rules, and DB security design decisions. +Each domain owns its controllers, DTOs, entities, repositories, and services. Nothing lives at the root level grouped by type. --- -## 🛠️ Tech Stack - -| Component | Technology | Version | -|-----------|-----------|---------| -| Language | Java | 17 | -| Framework | Spring Boot | 3.4.2 | -| Security | Spring Security + JWT | jjwt 0.12.6 | -| Database | PostgreSQL | 16-alpine | -| ORM | Spring Data JPA / Hibernate | — | -| Logging | Log4j2 | 2.25.3 | -| Validation | Spring Bean Validation | — | -| Boilerplate | Lombok | 1.18.36 | -| API Docs | SpringDoc OpenAPI | 2.8.8 | -| Tests | JUnit 5 + Mockito + AssertJ | — | -| Containers | Docker + Docker Compose | — | -| CI/CD | GitHub Actions | — | -| Static Analysis | CodeQL | — | -| Dependency Scanning | OWASP Dependency Check | 12.2.0 | +## Tech Stack + +| Component | Technology | +|-----------|------------| +| Language | Java 17 | +| Framework | Spring Boot 3.4.2 | +| Security | Spring Security + jjwt 0.12.6 | +| Database | PostgreSQL 16 (Docker) | +| ORM | Spring Data JPA / Hibernate | +| Logging | Log4j2 | +| Validation | Spring Bean Validation | +| API Docs | SpringDoc OpenAPI 2.8.8 (Swagger) | +| Boilerplate | Lombok | +| Tests | JUnit 5 + Mockito + AssertJ | +| CI/CD | GitHub Actions + CodeQL + OWASP Dependency Check | --- -## 🚀 Getting Started +## Getting Started ### Prerequisites - Java 17+ -- Docker & Docker Compose -- Maven 3.9+ (Maven wrapper `./mvnw` included) +- Docker and Docker Compose -### Run locally +### Setup ```bash -# 1. Clone the repository git clone https://github.com/DJAngel973/Secure-Wallet-API.git cd Secure-Wallet-API -# 2. Set up environment variables +# Copy and fill in the environment variables cp .env.example .env -# Edit .env with your local values (DB credentials, JWT secret, ports) -# 3. Start everything (automated script) -./script/dev-start.sh +# Start PostgreSQL +docker compose up -d -# Or step by step: -docker compose up -d # PostgreSQL only -docker compose --profile tools up -d # PostgreSQL + pgAdmin at http://localhost:5050 +# Run the application (dev profile) ./mvnw spring-boot:run -Dspring-boot.run.profiles=dev ``` -### Stop - -```bash -./script/dev-stop.sh -``` - ### Run tests ```bash -./mvnw test # All unit tests (H2 in-memory, no DB needed) -./mvnw test -Dtest=TransactionServiceTest # Single test class +./mvnw test # All unit tests (uses H2 in-memory, no DB needed) +./mvnw test -Dtest=TransactionServiceTest # Single test class ``` ---- - -## 📡 Using the API - -Once the application is running, the full interactive API documentation is available at: +### Interactive API docs -``` -http://localhost:8080/swagger-ui.html -``` +Once running: [http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html) -All endpoints return a standard `ApiResponse` envelope: +All responses follow a standard wrapper: ```json { @@ -263,162 +144,78 @@ All endpoints return a standard `ApiResponse` envelope: } ``` -### 1. Register a new user - -```http -POST /auth/register -Content-Type: application/json - -{ - "email": "alice@example.com", - "password": "SecureP@ss1", - "firstName": "Alice", - "lastName": "Smith" -} -``` - -### 2. Login and get tokens - -```http -POST /auth/login -Content-Type: application/json - -{ - "email": "alice@example.com", - "password": "ExampleSecureP@ss1" -} -``` - -```json -{ - "success": true, - "message": "Login successful", - "data": { - "accessToken": "ExampleekeypracticeyJhbGc...", - "refreshToken": "ExampleekeypracticeyJhbGcNiJ9...", - "tokenType": "Bearer", - "expiresIn": 900 - } -} -``` - -> Use the `accessToken` as a Bearer token in the `Authorization` header for all protected requests. - -### 3. Get your own profile - -```http -GET /users/me -Authorization: Bearer -``` - -### 4. Create a wallet - -```http -POST /wallets -Authorization: Bearer -Content-Type: application/json - -{ - "currency": "USD" -} -``` - -### 5. Deposit funds - -```http -POST /transactions/deposit -Authorization: Bearer -Content-Type: application/json - -{ - "walletId": "ExampleekeypracticeyJhbGcafa6", - "amount": 500.00, - "description": "Initial deposit" -} -``` - -### 6. Transfer between wallets - -> Amounts above **$100** require a valid TOTP code. Add the `X-2FA-Code` header with the 6-digit code from your authenticator app. +--- -```http -POST /transactions/transfer -Authorization: Bearer -X-2FA-Code: 123456 -Content-Type: application/json +**Note:** endpoints listed in Features above have not been tested end-to-end yet. The Swagger UI at `http://localhost:8080/swagger-ui.html` lists all available endpoints for exploration when the app is running. -{ - "sourceWalletId": "ExampleekeypracticeyJhbGc-2c963f66afa6", - "targetWalletId": "ExampleekeypracticeyJhbGc-3d074g77bgb7", - "amount": 150.00, - "description": "Splitting dinner" -} -``` - -### 7. Get transaction history for a wallet +--- -```http -GET /transactions/wallet/ExampleekeypracticeyJhbGcafa6 -Authorization: Bearer -``` +## Database -### 8. Refresh your access token +PostgreSQL 16 with schema managed through versioned SQL scripts — Hibernate validates but never creates or modifies tables. -```http -POST /auth/refresh -Content-Type: application/json +| # | Script | Purpose | +|---|--------|---------| +| 01 | `extensions.sql` | PostgreSQL extensions (pgcrypto, uuid-ossp) | +| 02 | `types.sql` | Custom ENUMs (roles, currencies, statuses) | +| 03 | `tables.sql` | Core tables with constraints and foreign keys | +| 04 | `index.sql` | Partial, composite, and GIN indexes | +| 05 | `triggers.sql` | Auto-timestamps, balance validation, state-change audit | +| 06 | `functions.sql` | `process_transaction()` with ACID guarantees | +| 07 | `seed.sql` | Development test data | +| 08 | `migrations.sql` | Incremental schema changes | -{ - "refreshToken": "ExampleekeypracticeyJhbGcNiJ9..." -} -``` +--- -### 9. Logout +## OWASP Top 10 (2025) coverage -```http -POST /auth/logout -Authorization: Bearer -Content-Type: application/json +This project uses the OWASP Top 10 as a guide for security decisions. Here is what is currently addressed: -{ - "refreshToken": "ExampleekeypracticeyJhbGcNiJ9..." -} -``` +| # | Risk | How it is handled | +|---|------|-------------------| +| A01 | Broken Access Control | `userId` always from JWT — never from request body. `404` on ownership mismatch. RBAC via Spring Security. | +| A02 | Cryptographic Failures | BCrypt strength 12 for passwords. SHA-256 for token hashing. `DECIMAL(19,4)` for amounts. | +| A03 | Software Supply Chain Failures | OWASP Dependency Check in CI. Pinned dependency versions. | +| A04 | Injection | Hibernate prepared statements. Bean Validation on all inputs. | +| A05 | Security Misconfiguration | No internals exposed in errors. No default credentials. Secure headers. | +| A06 | Insecure Design | Transaction and daily limits. 2FA threshold. Pessimistic locking on balance reads. | +| A07 | Auth Failures | Dual-token JWT with rotation. Account lockout. TOTP 2FA. | +| A08 | Data Integrity Failures | CodeQL in CI. SQL scripts versioned in Git. `ddl-auto: validate`. | +| A09 | Logging & Monitoring | All security events written to `audit_logs`. No passwords or tokens in logs. | +| A10 | Mishandled Exceptions | Centralized exception handler. Never "fail open". | --- -## 🔄 CI/CD Pipeline +## CI/CD -| Job | Trigger | Description | -|-----|---------|-------------| -| **Build & Test** | Every PR / push | Compile + unit tests (JUnit 5, `test` profile with H2) | -| **OWASP Dependency Check** | Every PR / push | CVE scan — **fails on CVSS ≥ 7** | -| **CodeQL Analysis** | Every PR / push | Static security analysis | -| **Package JAR** | Merge to `main` | Builds production artifact | +| Job | When | What | +|-----|------|------| +| Build & Test | Every push / PR | Compile + unit tests (H2 in-memory) | +| OWASP Dependency Check | Every push / PR | CVE scan | +| CodeQL Analysis | Every push / PR + weekly | Static security analysis | +| Package | Merge to main | Production JAR | --- -## 📚 Project Documentation +## Future plans -| Document | Description | -|----------|-------------| -| [`CONTEXT.md`](CONTEXT.md) | Single source of truth: architecture, stack, conventions, and rules | -| [`DECISIONS.md`](DECISIONS.md) | 8 Architecture Decision Records with full reasoning | -| [`SECURITY.md`](SECURITY.md) | Threat model, OWASP Top 10 (2025) full coverage, unbreakable rules | -| [`AGENTS.md`](AGENTS.md) | Instructions for AI agents working on this codebase | -| [`database/README.md`](database/README.md) | ER diagram, FK rules, DB security design decisions | -| [`CONTRIBUTING.md`](CONTRIBUTING.md) | How to contribute | -| [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) | Community standards | +- Review and test all endpoints end-to-end +- Polish existing code sections marked for improvement +- Postman collection for easier API exploration --- -## 🔐 Security Vulnerability Reporting +## Project documentation -This is an academic/portfolio project. -If you find a vulnerability, please open an issue with the `security` label. +| Document | What it covers | +|----------|---------------| +| [`CONTEXT.md`](CONTEXT.md) | Architecture, conventions, full stack details | +| [`DECISIONS.md`](DECISIONS.md) | 8 Architecture Decision Records with reasoning | +| [`SECURITY.md`](SECURITY.md) | Threat model and security decisions in depth | +| [`database/README.md`](database/README.md) | ER diagram, foreign key rules, DB design | --- -## 📄 License +## License [MIT](LICENSE) © 2025–2026 DJAngel973