Many Auth Methods, One Session Path
Product teams accumulate login methods: phone OTP, email password, Google, maybe Apple later. Each method wants its own controller, its own “create user” copy-paste, its own session quirks.
That is how you get three slightly different definitions of “logged in.”
The architectural rule in this identity BC is simple:
What stays method-specific
| Method | Verification concern |
|---|---|
| Phone OTP | Short-lived code in cache, rate limits, contact search token |
| Email + password | Strength rules, scrypt verify, lockout counters |
| OAuth provider | ID token / JWKS, audience, issuer; never store provider access tokens you do not need |
| Password reset | Opaque reset session in cache; always-safe responses (no email enumeration) |
Those concerns belong in command handlers and adapters — not in the session aggregate.
What must be shared
After verification succeeds:
- Provision — create user account + contact when this is a first-time identity (or link OAuth to an existing verified email contact)
- Establish session — create user account session + refresh family on the current device session
- Publish events — registered vs authentication successful / failed
Returning users skip create and still hit the same establish path. New OAuth subjects may link instead of create when a verified email contact already exists.
Layering that keeps secrets out of the UI
| Concern | Belongs in |
|---|---|
| Login screens, OAuth redirect UX | Frontend |
| Provider client secrets | Never in the browser |
| Credential verify, hashing, JWKS | Identity service |
| Session + refresh issuance | Identity service |
| Account linking rules | Identity service |
The frontend forwards proof (OTP code, password, ID token). Identity decides who you are and returns session tokens. Cookies and edge refresh are a delivery concern — not a second source of truth.
Extending without schema thrash
OAuth providers are an adapter registry: new provider = new verifier + enum value. Contact types already live as an aggregate, so email vs phone is not a column war.
Password credentials hang off the email contact (1:1), with failed-attempt and lockout fields. OAuth identities hang off the account with HMAC’d provider subject search tokens — raw subjects are not query keys in the clear.
Why convergence matters
Without a shared establish path you eventually get:
- OTP users with different session TTL than password users
- OAuth “login” that forgets device binding
- Reset flows that never invalidate sessions you thought you revoked
With convergence, every successful authentication means the same thing: a user account session on a known device, with a refresh family, and domain events the rest of the platform can trust.
Next
People and accounts are not the whole world. Organizations, employees, and location are first-class in the same bounded context.
Continue with Organizations and Location.