V1 inserted test@example.com / password123 everywhere, including prod. V5 deletes that row only when email and seed hash both match.
To Be Played (TBP) - Authentication Service
This is the Authentication and User Management Service for the "To Be Played" project. It acts as the central identity provider, handling user authentication, JWT generation, and profile management for the TBP frontend.
🚀 Tech Stack
- Language: Java 25
- Framework: Spring Boot 4.1.x
- Security: Spring Security & JSON Web Tokens (JWT)
- Database: PostgreSQL 17
- Migrations: Flyway
- Observability: Spring Boot Actuator & Micrometer (Prometheus)
- Build Tool: Maven Wrapper (
mvnw) - CI/CD: Gitea Actions (Forgejo)
🏗️ Architecture (Clean Architecture)
The project strictly follows a Domain-Driven / Clean Architecture (Ports & Adapters) approach to ensure high maintainability and testability. The codebase is divided into three main layers to prevent framework lock-in and keep business logic isolated.
Layer Breakdown
-
API / Web Layer (
api)- Contains REST endpoints, DTOs (
Request/Responserecords), and Global Exception Handlers. - Uses
ApiMapperto map between DTOs and Domain Models. - Rule: This layer delegates to the Service layer and never accesses persistence directly.
- Contains REST endpoints, DTOs (
-
Business / Core Layer (
model,service,exception)- The heart of the application. Contains pure Java classes (Models) and Business Logic (Services).
- Throws specific business exceptions (e.g.,
InvalidCredentialsException). - Rule: This layer has no dependencies on the API or Persistence layer. It defines interfaces that the persistence layer must implement.
-
Persistence Layer (
persistence)- Handles database operations. Contains JPA Entities (
UserEntity), Spring Data Repositories, and theEntityMapper. - Implements the interfaces defined in the Domain layer.
- Rule: Entities never leak into the API layer.
- Handles database operations. Contains JPA Entities (
Architectural boundaries are enforced via ArchUnit (src/test/java/auth_service/architecture/ArchitectureTest.java), which runs as part of the standard ./mvnw test quality gate. Abstract test bases live under auth_service.shared.test (AbstractUnitTest, AbstractIntegrationTest, AbstractPersistenceTest, AbstractWebMvcTest, AbstractArchitectureTest).
⚙️ Local Development
Running the application locally is fully automated via Spring Boot's Docker Compose Support.
Prerequisites
- Java 25 installed
- Docker & Docker Compose running
How to start
- Simply run the application from your IDE (e.g., IntelliJ) via the
AuthServiceApplication.javamain class. - Spring Boot will automatically detect the
compose.yamland spin up a local PostgreSQL container (tbp_auth_db_local) on port5432. - Flyway will automatically apply all database migrations located in
src/main/resources/db/migration. - The application will be available at
http://localhost:8080.
To stop the environment and clean the database volume (if needed):
docker compose down -v
🔐 API Versioning
All API endpoints utilize Query Parameter Versioning to keep URLs clean and manageable. Example: GET /api/users/me?v=1
🐳 CI/CD Pipeline
The project uses Gitea Actions for Continuous Integration and Deployment.
Quality Gate: Runs ./mvnw clean verify to ensure tests and builds pass.
Docker Build: Packages the application using a multi-stage Dockerfile.
Registry: Pushes the lightweight Alpine-based image to the private Gitea container registry (git.andreas-dahm.eu/lobo_corp/tbp-auth-service:latest).
📈 Observability
Metrics and health checks are exposed out-of-the-box via Spring Boot Actuator:
Health: http://localhost:8080/actuator/health
Prometheus Metrics: http://localhost:8080/actuator/prometheus
🗺️ Roadmap / Open Topics
Tracking known work that is not yet implemented. Each item is independently actionable.
Security
Switch JWT signing from HS256 → EdDSA (Ed25519).Done. Auth signs with an Ed25519 private key (JWT_PRIVATE_KEY), Core verifies with the matching public key (JWT_PUBLIC_KEY).kidheader (JWT_KID) is emitted to support future rotation. SeeJwtService,JwtConfig,application.yml,JwtServiceTest, andToBePlayed-Deployment/README.mdfor the rollout / rotation procedure.- Refresh-token rotation. Current access tokens last 24h with no revocation path. Split into a short-lived (5–15 min) access token and a long-lived (7–30 day) refresh token, rotated on every use, persisted server-side so logout / password change / "log out everywhere" actually revoke. Add a
token_versioncolumn onusers(or ajtiblacklist table) checked on each refresh. - Move JWT delivery to an httpOnly
Securecookie. Tokens are currently returned in the response body and stored by the frontend inlocalStorage, exposing them to any XSS payload. Set the cookie server-side, scope it to.tbp.andreas-dahm.eu, and add CSRF protection (Spring Security'sCookieCsrfTokenRepository.withHttpOnlyFalse()). - Tighten CORS for cookie auth. Restrict allowed origins to
https://tbp.andreas-dahm.eu(plus the local dev origin) and setallowCredentials=true. Required before switching to cookie-based JWT. - Rate-limit
/api/auth/loginand/api/registration/**. Currently no brake on credential stuffing or registration spam. Bucket4j or Spring Security's built-inRateLimiterare both options; key by IP + email.
Observability and API
- Publish a JWKS endpoint (
GET /.well-known/jwks.json) once on EdDSA. Lets Core (and any future verifier) fetch the public key automatically and supports key rotation without redeploying every consumer. - API versioning review. Query-parameter versioning (
?v=1) is unusual; path- orAccept-header-based versioning is more conventional and plays better with HTTP caches. Worth a deliberate decision before more endpoints accumulate.
Testing
- The abstract test bases under
auth_service.shared.testalready cover unit / integration / persistence / WebMvc slices. Continue adding controller-levelAbstractWebMvcTestcoverage for any new endpoint and persistence-layer tests for any new repository.
🎯 Product Roadmap (Auth-owned slices)
The full cross-service product roadmap lives in ToBePlayed-Deployment/README.md. This section lists only the slices that land in Auth.
1. Username + public-profile opt-in
The public profile feature (tbp.andreas-dahm.eu/u/<username>) requires:
- Unique, human-readable username distinct from email. Schema: add
usernamecolumn onuserswith a unique index and a Flyway migration. Validation: 3–30 chars,[a-z0-9_-], case-insensitive uniqueness. profile_publicflag (boolean, defaultfalse) onusers. Exposed viaPUT /api/users/settings.- New endpoint:
GET /api/public/users/{username}returning{ username, displayName, steamId?, joinedAt }— the public-facing identity contract that Core's profile endpoint can compose with.
2. Friends / social graph
Once usernames exist:
- Schema:
user_friendship(user_id, friend_id, status, requested_at, accepted_at)withstatusinPENDING / ACCEPTED / BLOCKED. Symmetric pair or single-row with a canonical ordering — pick one and stick with it. - Endpoints:
POST /api/users/me/friends?username=...(request),POST /api/users/me/friends/{id}/accept,DELETE /api/users/me/friends/{id},GET /api/users/me/friends. - Optional: discovery via the Steam friends API (Auth holds the
steam_id; the friends list call itself can live in Core'ssteam_adapter, with Auth resolving Steam IDs back to TBP users).
3. Email-driven retention
The weekly digest, sale alerts, and friend-request notifications all need email infrastructure:
- Spring's
JavaMailSender+ templating (Thymeleaf or plain interpolation). Configure SMTP via env vars (see Deployment roadmap). - Per-user notification preferences:
notify_digest_weekly,notify_sale_alerts,notify_friend_requests— all booleans onuserswith sensible defaults. - Endpoint to render
/api/users/me/notification-preferencesand update viaPUT. - Triggered by either Core scheduled jobs (digest, sale alerts) calling an internal Auth endpoint, or by a shared message queue if/when one is introduced.
4. Invitation system enhancements
Today registration validates an INVALID_INVITATION_CODE — so invites already exist. Worth surfacing:
- Endpoint:
POST /api/users/me/invitationsto generate a single-use code (rate-limited, expires after N days). Used for friend invites and onboarding referrals. - Endpoint:
GET /api/users/me/invitationsto list outstanding codes. - Optional: tie invitation acceptance to an auto-friend-request so referred users are already connected on first login.
5. Account deletion / GDPR
DELETE /api/users/methat soft-deletes (setsdeleted_at, anonymises PII, keeps the row for foreign-key integrity), or hard-deletes with cascade depending on policy.- Data export endpoint:
GET /api/users/me/exportreturning a downloadable JSON bundle of everything Auth knows about the user. Core should expose an equivalent and the Frontend can stitch them. - Document the data-retention policy here once decided.