From 4b41c63ca4e517cca2fead182b60697f81442d6f Mon Sep 17 00:00:00 2001 From: DJAngel973 Date: Thu, 23 Apr 2026 22:43:16 -0500 Subject: [PATCH 1/5] feat(docs): add springdoc-openapi-starter-webmvc-ui 2.8.8 dependency --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index f1b90e3..65abfaf 100644 --- a/pom.xml +++ b/pom.xml @@ -134,6 +134,12 @@ spring-security-test test + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.8.8 + From 64e19638d33e4d940a987780995333764b10964a Mon Sep 17 00:00:00 2001 From: DJAngel973 Date: Thu, 23 Apr 2026 23:04:55 -0500 Subject: [PATCH 2/5] feat(docs): add OpenApiConfig - JWT Bearer auth, servers, API description --- .../wallet/secure/config/OpenApiConfig.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/main/java/com/wallet/secure/config/OpenApiConfig.java diff --git a/src/main/java/com/wallet/secure/config/OpenApiConfig.java b/src/main/java/com/wallet/secure/config/OpenApiConfig.java new file mode 100644 index 0000000..73613f0 --- /dev/null +++ b/src/main/java/com/wallet/secure/config/OpenApiConfig.java @@ -0,0 +1,105 @@ +package com.wallet.secure.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +/** + * OpenAPI 3.0 configuration — generates Swagger UI and API spec. + * + * Accessible at: + * → Swagger UI: http://localhost:8080/swagger-ui/index.html + * → OpenAPI JSON: http://localhost:8080/v3/api-docs + * → OpenAPI YAML: http://localhost:8080/v3/api-docs.yaml + * + * WHY document the API: + * → Reclutadores pueden probar la API sin Postman + * → Frontend developers know exactly what each endpoint expects + * → Auto-generated from annotations — always in sync with code + * + * Security scheme: Bearer JWT + * → "Authorize" button in Swagger UI accepts the access token + * → All authenticated endpoints show the lock icon + * → Testers can login, copy the token, paste in Authorize → test everything + */ +@Configuration +public class OpenApiConfig { + + private static final String SECURITY_SCHEME_NAME = "bearerAuth"; + + @Bean + public OpenAPI secureWalletOpenAPI() { + return new OpenAPI() + .info(buildInfo()) + .servers(buildServers()) + .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME)) + .components(new Components() + .addSecuritySchemes(SECURITY_SCHEME_NAME, buildSecurityScheme())); + } + + private Info buildInfo() { + return new Info() + .title("Secure Wallet API") + .version("0.0.1-SNAPSHOT") + .description(""" + ## Digital Wallet with OWASP Security Best Practices + + A production-grade REST API implementing: + - **JWT Authentication** with access + refresh tokens + - **Multi-device session management** (user_sessions table) + - **ACID-compliant financial transactions** with pessimistic locking + - **Complete audit trail** (audit_logs + transaction_history) + - **OWASP Top 10** mitigations throughout + + ### How to authenticate: + 1. `POST /auth/register` or `POST /auth/login` + 2. Copy the `accessToken` from the response + 3. Click **Authorize** → paste `Bearer {accessToken}` + 4. All authenticated endpoints are now accessible + + ### Roles: + - `USER` — standard wallet operations + - `ADMIN` — user management, wallet suspension, session audit + """) + .contact(new Contact() + .name("DJAngel973") + .url("https://github.com/DJAngel973/Secure-Wallet-API")) + .license(new License() + .name("MIT") + .url("https://opensource.org/licenses/MIT")); + } + + private List buildServers() { + return List.of( + new Server() + .url("http://localhost:8080") + .description("Local development"), + new Server() + .url("https://api.securewallet.dev") + .description("Production (future)") + ); + } + + /** + * Defines the JWT Bearer security scheme. + * This adds the "Authorize" button to Swagger UI. + * Users paste their accessToken once — all endpoints use it automatically. + */ + private SecurityScheme buildSecurityScheme() { + return new SecurityScheme() + .name(SECURITY_SCHEME_NAME) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + .description("Paste your access token here (without 'Bearer ' prefix)"); + } +} \ No newline at end of file From e56d3c36b948786e1d2a6120a12eaf1ce3c25a8c Mon Sep 17 00:00:00 2001 From: DJAngel973 Date: Thu, 23 Apr 2026 23:07:39 -0500 Subject: [PATCH 3/5] feat(docs): add Swagger UI paths to PUBLIC_ENDPOINTS in SecurityConfig --- src/main/java/com/wallet/secure/config/SecurityConfig.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/wallet/secure/config/SecurityConfig.java b/src/main/java/com/wallet/secure/config/SecurityConfig.java index 9803854..28558e0 100644 --- a/src/main/java/com/wallet/secure/config/SecurityConfig.java +++ b/src/main/java/com/wallet/secure/config/SecurityConfig.java @@ -49,7 +49,11 @@ public class SecurityConfig { "/auth/login", "/auth/register", "/auth/refresh", - "/actuator/health" + "/actuator/health", + "/swagger-ui/**", + "/swagger-ui.html", + "/v3/api-docs/**", + "/v3/api-docs" }; /** From 2fc5fcd6ced016a7074503c558ebdf3be5dfc433 Mon Sep 17 00:00:00 2001 From: DJAngel973 Date: Thu, 23 Apr 2026 23:12:58 -0500 Subject: [PATCH 4/5] feat(docs): configure SpringDoc in application.yml - UI sorting, try-it-out enabled --- src/main/resources/application.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a76291d..c9374e4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -133,4 +133,17 @@ app: password-require-uppercase: true password-require-lowercase: true password-require-digit: true - password-require-special: true \ No newline at end of file + password-require-special: true + + # SPRINGDOC - OpenAPI / Swagger + springdoc: + api-docs: + path: /v3/api-docs + swagger-ui: + path: /swagger-ui/index.html + operations-sorter: method # Sort by HTTP method (GET, POST, PUT, DELETE) + tags-sorter: alpha # Sort tags alphabetically + display-request-duration: true # Show response time in UI + try-it-out-enabled: true # Enable "Try it out" by default + show-actuator: false # Don't expose actuator in Swagger + packages-to-scan: com.wallet.secure # Only scan our packages \ No newline at end of file From 7f737883156b3d8cc3321d4fb56dcbda43a8ef80 Mon Sep 17 00:00:00 2001 From: DJAngel973 Date: Thu, 23 Apr 2026 23:36:38 -0500 Subject: [PATCH 5/5] feat(docs): add @Tag to all controllers - numbered groups in Swagger UI --- .../com/wallet/secure/audit/controller/AuditController.java | 6 ++---- .../com/wallet/secure/auth/controller/AuthController.java | 2 ++ .../wallet/secure/auth/controller/SessionController.java | 2 ++ .../transaction/controller/TransactionController.java | 2 ++ .../controller/TransactionHistoryController.java | 2 ++ .../com/wallet/secure/user/controller/UserController.java | 2 ++ .../wallet/secure/wallet/controller/WalletController.java | 2 ++ 7 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/wallet/secure/audit/controller/AuditController.java b/src/main/java/com/wallet/secure/audit/controller/AuditController.java index 8161456..423e53d 100644 --- a/src/main/java/com/wallet/secure/audit/controller/AuditController.java +++ b/src/main/java/com/wallet/secure/audit/controller/AuditController.java @@ -1,23 +1,20 @@ package com.wallet.secure.audit.controller; import com.wallet.secure.audit.dto.AuditLogResponse; -import com.wallet.secure.audit.entity.AuditLog; import com.wallet.secure.audit.repository.AuditLogRepository; import com.wallet.secure.common.enums.AuditAction; import com.wallet.secure.common.enums.LogSeverity; import com.wallet.secure.common.response.ApiResponse; -import jakarta.validation.constraints.Size; -import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.support.PageableExecutionUtils; import org.springframework.data.web.PageableDefault; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.tags.Tag; import java.time.Instant; import java.time.LocalDate; @@ -55,6 +52,7 @@ @RequestMapping("/audit") @RequiredArgsConstructor @Log4j2 +@Tag(name = "7. Audit Logs", description = "Security audit trail — ADMIN only") @PreAuthorize("hasRole('ADMIN')") public class AuditController { diff --git a/src/main/java/com/wallet/secure/auth/controller/AuthController.java b/src/main/java/com/wallet/secure/auth/controller/AuthController.java index 78cca8b..e45020d 100644 --- a/src/main/java/com/wallet/secure/auth/controller/AuthController.java +++ b/src/main/java/com/wallet/secure/auth/controller/AuthController.java @@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import io.swagger.v3.oas.annotations.tags.Tag; /** * REST Controller for authentication endpoints. @@ -39,6 +40,7 @@ @RequestMapping("/auth") @RequiredArgsConstructor @Log4j2 +@Tag(name = "1. Authentication", description = "Register, login, logout and token refresh — public endpoints") public class AuthController { private final AuthService authService; diff --git a/src/main/java/com/wallet/secure/auth/controller/SessionController.java b/src/main/java/com/wallet/secure/auth/controller/SessionController.java index 50682f4..f483a30 100644 --- a/src/main/java/com/wallet/secure/auth/controller/SessionController.java +++ b/src/main/java/com/wallet/secure/auth/controller/SessionController.java @@ -12,6 +12,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import java.util.UUID; @@ -48,6 +49,7 @@ @RequestMapping("/sessions") @RequiredArgsConstructor @Log4j2 +@Tag(name = "2. Sessions", description = "Multi-device session management — see and revoke active sessions") public class SessionController { private final SessionService sessionService; diff --git a/src/main/java/com/wallet/secure/transaction/controller/TransactionController.java b/src/main/java/com/wallet/secure/transaction/controller/TransactionController.java index c0c7fa6..8d54aa7 100644 --- a/src/main/java/com/wallet/secure/transaction/controller/TransactionController.java +++ b/src/main/java/com/wallet/secure/transaction/controller/TransactionController.java @@ -19,6 +19,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.UUID; @@ -47,6 +48,7 @@ @RequestMapping("/transactions") @RequiredArgsConstructor @Log4j2 +@Tag(name = "5. Transactions", description = "Deposit, withdraw and transfer funds between wallets") public class TransactionController { private final TransactionService transactionService; diff --git a/src/main/java/com/wallet/secure/transaction/controller/TransactionHistoryController.java b/src/main/java/com/wallet/secure/transaction/controller/TransactionHistoryController.java index 36b2a40..1c4ab29 100644 --- a/src/main/java/com/wallet/secure/transaction/controller/TransactionHistoryController.java +++ b/src/main/java/com/wallet/secure/transaction/controller/TransactionHistoryController.java @@ -12,6 +12,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import java.util.UUID; @@ -51,6 +52,7 @@ @RestController @RequiredArgsConstructor @Log4j2 +@Tag(name = "6. Transaction History", description = "Complete state timeline for each transaction — PENDING → PROCESSING → COMPLETED/FAILED") public class TransactionHistoryController { private final TransactionHistoryService historyService; diff --git a/src/main/java/com/wallet/secure/user/controller/UserController.java b/src/main/java/com/wallet/secure/user/controller/UserController.java index 030282a..83ce46a 100644 --- a/src/main/java/com/wallet/secure/user/controller/UserController.java +++ b/src/main/java/com/wallet/secure/user/controller/UserController.java @@ -12,6 +12,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.UUID; @@ -34,6 +35,7 @@ @RequestMapping("/users") @RequiredArgsConstructor @Log4j2 +@Tag(name = "3. Users", description = "User profile, password change and account management") public class UserController { private final UserService userService; diff --git a/src/main/java/com/wallet/secure/wallet/controller/WalletController.java b/src/main/java/com/wallet/secure/wallet/controller/WalletController.java index 1f845f2..31aad1e 100644 --- a/src/main/java/com/wallet/secure/wallet/controller/WalletController.java +++ b/src/main/java/com/wallet/secure/wallet/controller/WalletController.java @@ -14,6 +14,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import java.util.UUID; @@ -41,6 +42,7 @@ @RequestMapping("/wallets") @RequiredArgsConstructor @Log4j2 +@Tag(name = "4. Wallets", description = "Create and manage multi-currency digital wallets") public class WalletController { private final WalletService walletService;