# giovanni104/secure-otp-service architecture

> Architecture diagram of the GitHub repository giovanni104/secure-otp-service, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-18). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/giovanni104/secure-otp-service
- Repository: https://github.com/giovanni104/secure-otp-service

## Overview

1. **What this repository is**
   - A single-service REST API for generating and validating one-time passwords (OTPs).
   - The runtime is a Spring Boot application written in Java 25, with Redis as the only persistent external dependency.
   - The app is designed as a small, layered backend: HTTP boundary, application/service logic, Redis-backed state, and centralized error handling.

2. **Top-level runtime boundary**
   - `OtpApplication` is the Spring Boot entry point.
   - The application is packaged and run either directly with Maven or as a Docker container.
   - `docker-compose.yml` defines the deployment boundary: one container for the API and one for Redis.

3. **HTTP API layer**
   - `OtpController` is the REST façade.
   - It exposes two main operations:
     - generate an OTP for an identifier
     - validate an OTP for an identifier
   - Request/response shapes are isolated in DTOs under `src/main/java/com/example/otp/dto`, which keeps the API contract separate from internal service logic.

4. **Application/service layer**
   - `OtpService` is the core business layer.
   - This is where OTP generation, validation, expiration semantics, and state transitions are handled.
   - The service sits between the controller and Redis, so the controller does not contain OTP logic.

5. **Security-sensitive OTP processing**
   - OTPs are not stored in plaintext.
   - `MessageDigestSupport` and the security approach described in the README indicate HMAC-SHA256 hashing plus constant-time comparison via `MessageDigest.isEqual`.
   - The service uses `SecureRandom` for code generation, which is important because this is security-sensitive data rather than a normal random token.

6. **Redis as the state store**
   - `RedisConfig` wires Spring Data Redis connectivity.
   - Redis is used as a temporary, TTL-based store for OTP lifecycle state rather than as a long-term database.
   - The README describes three logical key types:
     - active OTP hash
     - request marker for distinguishing expired vs nonexistent
     - used marker for distinguishing already-used vs expired/nonexistent
   - This means the architecture relies on Redis expiration as part of the business rules, not just as storage cleanup.

7. **Configuration boundary**
   - `OtpProperties` and `ApplicationConfig` hold externalized settings.
   - Important runtime parameters include OTP length, TTL, used-marker TTL, HMAC secret, Redis host/port/password, and whether the raw OTP is returned in responses.
   - This makes the service environment-driven and suitable for local, containerized, or production deployments with different secrets and TTLs.

8. **Validation and API contracts**
   - The DTOs are split by use case:
     - `GenerateOtpRequest` / `GenerateOtpResponse`
     - `ValidateOtpRequest` / `ValidateOtpResponse`
   - Jakarta Validation is part of the stack, so request validation likely happens at the controller boundary before service logic is invoked.
   - This keeps invalid input from reaching Redis or the crypto logic.

9. **Error handling subsystem**
   - The `exception` package defines domain-specific failure modes:
     - `OtpNotFoundException`
     - `OtpExpiredException`
     - `InvalidOtpException`
     - `OtpAlreadyUsedException`
   - `GlobalExceptionHandler` centralizes translation from domain exceptions to API error responses via `ApiErrorResponse`.
   - The architecture therefore has a clean separation between business failures and HTTP status mapping.

10. **End-to-end generate flow**
   - Client calls `POST /api/otp/generate`.
   - Controller validates input and delegates to `OtpService`.
   - Service generates a numeric OTP, hashes it, stores it in Redis with TTL, and returns metadata.
   - If enabled by config, the actual OTP is included in the response; otherwise it is meant to be delivered through an external channel such as SMS/email.

11. **End-to-end validate flow**
   - Client calls `POST /api/otp/validate`.
   - Controller passes identifier and OTP to `OtpService`.
   - Service looks up Redis state, distinguishes between not found / expired / already used, hashes the supplied OTP, compares securely, and deletes or marks state on success.
   - Validation is therefore stateful and depends on Redis key lifecycle, not just a stateless cryptographic check.

12. **Operational and deployment boundaries**
   - The app exposes Spring Boot Actuator health endpoints for readiness/liveness style checks.
   - Docker Compose is the operational entry point for local execution and integration testing.
   - The README’s production guidance also implies the system expects Redis network isolation, secret management, and rate limiting outside the service itself.

13. **Testing footprint**
   - The repository currently shows a minimal Spring Boot test class under `src/test`.
   - That suggests the repo is oriented around integration/bootstrapping verification rather than a large internal test suite, at least in the current tree.

14. **How to draw the architecture**
   - The simplest accurate diagram is:
     - external REST clients on one side
     - Spring Boot API container in the middle, split into controller, service, config, and exception handling
     - Redis as the only backing state service on the other side
   - The key architectural idea is that OTP security and lifecycle are enforced by the service layer plus Redis TTL/markers, not by the HTTP layer.

Main entry points: [`src/main/java/com/example/otp/service/OtpService.java`](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/service/OtpService.java) (OTP service), [`src/main/java/com/example/otp/OtpApplication.java`](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/OtpApplication.java) (OTP app), [`src/main/java/com/example/otp/controller/OtpController.java`](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/controller/OtpController.java) (OTP controller), [`docker-compose.yml`](https://github.com/giovanni104/secure-otp-service/blob/main/docker-compose.yml) (Compose stack)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_clients["Clients"]
  node_external_client(("REST client<br/>external caller"))
end

subgraph group_api["Spring Boot API"]
  node_otp_application(("OTP app<br/>boot entrypoint"))
  node_otp_controller["OTP controller<br/>REST controller<br/>[OtpController.java]"]
  node_otp_service["OTP service<br/>business service<br/>[OtpService.java]"]
  node_message_digest_support["Digest support<br/>crypto helper"]
  node_generate_request["Gen request<br/>DTO"]
  node_generate_response["Gen response<br/>DTO"]
  node_validate_request["Val request<br/>DTO"]
  node_validate_response["Val response<br/>DTO"]
  node_global_exception_handler["Exception handler<br/>api advice"]
  node_api_error_response["API error<br/>error DTO"]
end

subgraph group_config["Configuration"]
  node_otp_properties["OTP props<br/>settings<br/>[OtpProperties.java]"]
  node_application_config["App config<br/>spring config"]
  node_redis_config["Redis config<br/>spring config<br/>[RedisConfig.java]"]
end

subgraph group_state["Redis State"]
  node_redis_store[("Redis<br/>state store")]
end

subgraph group_ops["Operations"]
  node_docker_compose["Compose stack<br/>deployment<br/>[docker-compose.yml]"]
  node_dockerfile["API image<br/>container build"]
end

node_external_client -->|"POST /api/otp/*"| node_otp_controller
node_docker_compose -->|"build/run"| node_dockerfile
node_docker_compose -->|"starts"| node_redis_store
node_dockerfile -->|"packages"| node_otp_application
node_otp_application -->|"loads"| node_application_config
node_otp_application -->|"loads"| node_redis_config
node_otp_application -->|"registers"| node_global_exception_handler
node_otp_controller -->|"binds"| node_generate_request
node_otp_controller -->|"binds"| node_validate_request
node_otp_controller -->|"delegates"| node_otp_service
node_otp_service -->|"hash/compare"| node_message_digest_support
node_otp_service -->|"reads"| node_otp_properties
node_otp_service -->|"stores/loads"| node_redis_store
node_otp_service -->|"returns"| node_generate_response
node_otp_service -->|"returns"| node_validate_response
node_global_exception_handler -->|"emits"| node_api_error_response
node_redis_config -->|"connects"| node_redis_store
node_application_config -->|"binds"| node_otp_properties

click node_docker_compose "https://github.com/giovanni104/secure-otp-service/blob/main/docker-compose.yml"
click node_dockerfile "https://github.com/giovanni104/secure-otp-service/tree/main/Dockerfile"
click node_otp_application "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/OtpApplication.java"
click node_otp_controller "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/controller/OtpController.java"
click node_otp_service "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/service/OtpService.java"
click node_message_digest_support "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/service/MessageDigestSupport.java"
click node_generate_request "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/dto/GenerateOtpRequest.java"
click node_generate_response "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/dto/GenerateOtpResponse.java"
click node_validate_request "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/dto/ValidateOtpRequest.java"
click node_validate_response "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/dto/ValidateOtpResponse.java"
click node_global_exception_handler "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/exception/GlobalExceptionHandler.java"
click node_api_error_response "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/exception/ApiErrorResponse.java"
click node_otp_properties "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/config/OtpProperties.java"
click node_application_config "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/config/ApplicationConfig.java"
click node_redis_config "https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/config/RedisConfig.java"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_external_client toneBlue
class node_otp_application,node_otp_controller,node_otp_service,node_message_digest_support,node_generate_request,node_generate_response,node_validate_request,node_validate_response,node_global_exception_handler,node_api_error_response toneAmber
class node_otp_properties,node_application_config,node_redis_config toneMint
class node_redis_store toneRose
class node_docker_compose,node_dockerfile toneIndigo
```

## Components

### Clients

External callers and deployment entry points

- **REST client**: external caller · Calls the OTP API

### Spring Boot API

HTTP boundary, service logic, and error translation

- [OTP app](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/OtpApplication.java): boot entrypoint · Spring Boot application startup · `src/main/java/com/example/otp/OtpApplication.java`
- [OTP controller](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/controller/OtpController.java): REST controller · HTTP façade for generate and validate · `src/main/java/com/example/otp/controller/OtpController.java`
- [OTP service](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/service/OtpService.java): business service · Generates, validates, and advances OTP state · `src/main/java/com/example/otp/service/OtpService.java`
- [Digest support](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/service/MessageDigestSupport.java): crypto helper · HMAC and constant-time comparison support · `src/main/java/com/example/otp/service/MessageDigestSupport.java`
- [Gen request](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/dto/GenerateOtpRequest.java): DTO · Request payload for OTP generation · `src/main/java/com/example/otp/dto/GenerateOtpRequest.java`
- [Gen response](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/dto/GenerateOtpResponse.java): DTO · Response payload for OTP generation · `src/main/java/com/example/otp/dto/GenerateOtpResponse.java`
- [Val request](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/dto/ValidateOtpRequest.java): DTO · Request payload for OTP validation · `src/main/java/com/example/otp/dto/ValidateOtpRequest.java`
- [Val response](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/dto/ValidateOtpResponse.java): DTO · Response payload for OTP validation · `src/main/java/com/example/otp/dto/ValidateOtpResponse.java`
- [Exception handler](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/exception/GlobalExceptionHandler.java): api advice · Maps domain failures to API errors · `src/main/java/com/example/otp/exception/GlobalExceptionHandler.java`
- [API error](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/exception/ApiErrorResponse.java): error DTO · Standard error response body · `src/main/java/com/example/otp/exception/ApiErrorResponse.java`

### Configuration

Externalized settings and infrastructure wiring

- [OTP props](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/config/OtpProperties.java): settings · OTP length, TTL, secret, and response mode · `src/main/java/com/example/otp/config/OtpProperties.java`
- [App config](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/config/ApplicationConfig.java): spring config · Wires application-level beans and settings · `src/main/java/com/example/otp/config/ApplicationConfig.java`
- [Redis config](https://github.com/giovanni104/secure-otp-service/blob/main/src/main/java/com/example/otp/config/RedisConfig.java): spring config · Connects Spring Data Redis · `src/main/java/com/example/otp/config/RedisConfig.java`

### Redis State

Temporary OTP lifecycle storage

- **Redis**: state store · TTL-backed OTP hashes and markers

### Operations

Container and runtime packaging

- [Compose stack](https://github.com/giovanni104/secure-otp-service/blob/main/docker-compose.yml): deployment · Starts API and Redis containers · `docker-compose.yml`
- [API image](https://github.com/giovanni104/secure-otp-service/tree/main/Dockerfile): container build · Builds the Spring Boot runtime image · `Dockerfile`

## Connections

- REST client → OTP controller: POST /api/otp/* · Client invokes generate or validate endpoints
- Compose stack → API image: build/run · Compose runs the API container image
- Compose stack → Redis: starts · Compose provides Redis as the backing service
- API image → OTP app: packages · Container starts the Spring Boot entrypoint
- OTP app → App config: loads · Bootstraps application configuration
- OTP app → Redis config: loads · Initializes Redis connectivity
- OTP app → Exception handler: registers · Enables centralized exception mapping
- OTP controller → Gen request: binds · Binds generate input JSON
- OTP controller → Val request: binds · Binds validate input JSON
- OTP controller → OTP service: delegates · Hands off OTP business operations
- OTP service → Digest support: hash/compare · Uses HMAC and constant-time equality
- OTP service → OTP props: reads · Consumes TTL, length, secret, and flags
- OTP service → Redis: stores/loads · Persists OTP hashes and lifecycle markers
- OTP service → Gen response: returns · Builds generation result payloads
- OTP service → Val response: returns · Builds validation result payloads
- Exception handler → API error: emits · Serializes structured API errors
- Redis config → Redis: connects · Binds Spring Data Redis to the external store
- App config → OTP props: binds · Maps externalized OTP settings
