Java 21 ยท Spring Boot 3.3
Working
Multi-Tenant Authentication Service
One auth microservice and one Keycloak deployment serving separate tenants
Registration, login, refresh rotation and a custom Keycloak SPI implemented
Overview
A Spring Boot auth microservice covering registration, password-grant login, refresh-token rotation, and profile-driven realm selection, with Bearer JWT verified against the provider JWKS through Spring Security Resource Server. Realm selection from the profile is the part that makes it multi-tenant: one deployment, separate tenants, no per-tenant fork.
The identity provider itself is extended with a custom SPI for one-time-password storage and REST issuance. OTP is where auth services usually leak: the codes get stored in plain text, compared with a normal string equality, or left replayable. Here they are hashed with a peppered SHA-256, compared in constant time, single-use, and rate limited.
Stack
- Language
- Java 21, Spring Boot 3.3
- Identity provider
- Keycloak, extended with a custom SPI
- Protocols
- OAuth2 / OIDC, JWT verified against JWKS
- Token handling
- Spring Security Resource Server
- Data
- MongoDB
- Events
- Kafka
Decisions Worth Explaining
Peppered SHA-256 with constant-time compare for OTP
A one-time password is short and low-entropy, so the stored form has to be useless if the store leaks, and the comparison must not leak timing. Single-use plus rate limiting closes the replay and brute-force paths.
Realm selection from the profile, not the subdomain
Deriving the tenant from the request host couples auth to DNS layout. Driving it from the profile keeps one deployment serving tenants that may share a hostname.
A provider SPI rather than OTP in the application
Putting OTP issuance in the identity provider keeps it available to every client of that provider, not only to this service.
What Is Not Done
- Password-grant is a deprecated OAuth2 flow; it suits a first-party client but is not the right default for third-party ones.
- No load test of the OTP rate limiter under concurrent issuance.