Identity and onboarding
- Design rider signup and the multi-step, vendor-dependent driver onboarding pipeline.
- Explain why onboarding is modeled as a resumable state machine rather than a single transaction.
From 1.1: the Identity service owns users, credentials, sessions, and devices. From 0.2: background checks are bought, not built — which means onboarding contains a step we don't control.
Rider signup is deliberately boring: phone number + OTP, a JWT session token validated at the gateway (so downstream services never re-authenticate), and a device record for push notifications. Boring is the goal — authentication sits on the critical path of every request, so it must be the most cache-friendly, least clever component we own.
Driver onboarding is the interesting half. It spans days, involves document upload, human review, and a third-party background check whose latency we cannot control. Any process with external waits must be a resumable state machine persisted in the database — never an in-memory workflow that dies with a deploy. This is the first appearance of a pattern that recurs in trips (1.7) and payments (1.8): long-running work = explicit persisted states + events that move between them.
stateDiagram-v2
[*] --> Applied : driver submits form
Applied --> DocsPending : account created
DocsPending --> DocsReview : license, insurance, vehicle photos uploaded
DocsReview --> DocsPending : rejected — resubmit
DocsReview --> BackgroundCheck : docs approved
BackgroundCheck --> ManualReview : vendor flags record
BackgroundCheck --> Approved : vendor clears
ManualReview --> Approved : ops approves
ManualReview --> Denied : ops denies
Approved --> Active : training completed, first go-online
Denied --> [*]
Worked example: the background-check step
sequenceDiagram
autonumber
participant D as Driver app
participant ID as Identity service
participant S3 as Object store
participant V as Check vendor
participant OPS as Ops console
D->>ID: upload documents
ID->>S3: store encrypted docs
ID->>ID: state = DocsReview
OPS->>ID: approve docs
ID->>V: request background check (async, webhook callback)
ID->>ID: state = BackgroundCheck
Note over ID,V: hours to days pass — state survives deploys and restarts
V-->>ID: webhook: clear
ID->>ID: verify webhook signature, state = Approved
ID->>D: push notification: you're approved
Webhooks arrive late, duplicated, and out of order. The handler must be idempotent (a duplicate "clear" is a no-op) and must validate state transitions (a "clear" arriving after ops already denied the driver must not resurrect the application). Idempotency gets a full treatment in 2.4 — here you just meet it.