A single sign-on and rate-limiting platform — the "front door" of a company. Users prove who they are once (with a password, or via Google, Microsoft Entra ID, Okta, or SAML), and every downstream tool trusts that proof.
Built as a long-form learning project, one phase at a time.
Every request that reaches this platform has to answer four questions:
| Question | Answered by |
|---|---|
| Who are you? | Authentication — Amazon Cognito |
| What may you do? | Authorization — roles and permissions in PostgreSQL |
| Are you behaving? | Rate limiting — token buckets in Redis |
| Is the system healthy? | Circuit breakers and monitoring |
Backend — Java 21, Spring Boot, Spring Security, Spring Data JPA, Thymeleaf, Maven Identity — Amazon Cognito (federated to Google / Microsoft / Okta / SAML) Data — PostgreSQL with Flyway migrations, Redis for counters and circuit state Infrastructure — Docker, ECS Fargate, Terraform, GitHub Actions
.
├── UI/ Hand-built HTML pages (landing, signup, SSO login)
└── Auth/ Spring Boot backend
The pages in UI/ move into Auth/src/main/resources/templates in Phase 3, where they
get served at clean routes (/, /signup, /login, /dashboard) instead of .html files.
Milestone 1 — Foundation, in progress.
- Three UI pages built, with backend hook points marked
- Git repository initialized
- Maven project builds and runs
-
/actuator/healthresponding - Pages served at clean Spring routes
- Spring Security with in-memory users
Later milestones: real identity with Cognito, Redis rate limiting, the four SSO providers, multi-tenant data and permissions, vendor resilience, and production deployment on AWS.
Requires JDK 21. Maven is not needed — the project ships with the Maven wrapper.
cd Auth
./mvnw spring-boot:run # Windows: .\mvnw.cmd spring-boot:run
The app serves on http://localhost:8080. Verify it is alive:
curl http://localhost:8080/actuator/health # {"status":"UP"}
These are non-negotiable in this codebase:
- Passwords live only in Cognito — never in PostgreSQL, Redis, logs, or code.
- Never log tokens, secrets, authorization codes, or session cookies.
- Validate every JWT fully: signature, expiry, issuer, audience.
- Login errors never reveal whether an account exists.
- Every tenant-owned query filters by
organization_id— no exceptions. - Validate all external input server-side; client-side checks are cosmetic.
- Secrets come from environment variables locally and Secrets Manager in AWS — never Git.
- Deny by default: routes are protected unless explicitly made public.
- Use
state,nonce, and PKCE on every OAuth flow. - Redis is never the system of record; the app must survive Redis being empty.
- Rate limits and lockouts expire on their own — no permanent locks from anonymous traffic.
- The app container runs as a non-root user, with no secrets baked into the image.
mainis always working — never commit broken code to it.- One branch per phase:
phase-02-foundation,phase-08-signup, and so on. - Small commits with meaningful messages.
- Each phase ends with the app running and tests passing.