Status: Accepted · Date: 2025-Q1 · Deciders: Aarokya Engineering
This design evolved. An earlier draft proposed a phone-OTP flow with refresh-token rotation and per-device session rows (
otp_sessions, user_sessions). That was not what shipped — there is no OTP, no refresh token, and no server-side session table in the codebase. The Decision below documents what actually runs today.Context
Aarokya is embedded inside a host platform (for example a driver app). The user never types a password or an OTP into Aarokya — the host platform already knows who they are. Auth requirements:- Platform-mediated identity: the host platform vouches for the user and exchanges their identity for an app token.
- Offline-tolerant: app access tokens must be verifiable without a DB round-trip.
- Separate admin plane: admin, dashboard, and service actors authenticate through a central IdP with role information on the token.
Decisions Made
1. App Token Issuance via Platform Service Account
There is exactly one token-issuance endpoint for app users:POST /auth/token. A platform service account (not the end user) calls it with the user’s {phone_number, phone_country_code, id_proof} and receives {user_id, access_token}. The access_token is an app JWT signed RS256 by the backend.
The service account authenticates to Aarokya with a bearer JWT minted by Keycloak via the client_credentials grant; the route requires actor_type: "platform_service".
2. Stateless RS256 App JWT
The app token is a signed JWT verified on every protected request by the auth middleware — no DB lookup, no server-side session row. The app is purely a bearer of this token. There is no refresh token; when the token expires the platform re-issues one throughPOST /auth/token.
The signing algorithm is pinned per issuer (APP_JWT_ALGORITHM = RS256) as a defense against algorithm-confusion attacks.
3. Keycloak OIDC for Admin / Dashboard / Service Actors
Admin, dashboard, benefit-provider, platform, and callback actors authenticate through Keycloak OIDC. Their tokens are JWKS-verified (signature +iss + aud + exp), and an actor_type claim drives role-based access. The actor_type discriminator covers app users plus admin, readonly-admin, admin-service, benefit-provider (human + service), platform (human + service), and callback-service roles.
4. No OTP, No Refresh Token, No Session Table
The app auth path has no SMS step, no OTP, and no refresh token. There are nootp_sessions or user_sessions tables — app sessions are entirely stateless JWTs. (A separate dashboard OIDC flow does use Keycloak-managed refresh cookies for browser sessions, but that is the IdP’s mechanism, not an Aarokya-owned token store.)
Security Properties Summary
Consequences
Gained
- Stateless app token verification — no DB on every request
- No OTP/SMS dependency or delivery-latency tuning
- One central IdP (Keycloak) for all admin/service roles
- No session/OTP tables to secure, rotate, or reconcile
Trade-offs accepted
- App tokens cannot be revoked mid-life — re-issuance is platform-driven
- Auth depends on the host platform’s service-account integrity
- Keycloak is a hard runtime dependency for the admin/service plane