Current App Behavior
The current Laravel portal signs in aConsumer record with the consumer guard. The login screen checks last name, DOB, and last four SSN against the stored consumer identity. If the browser is trusted, the user can enter without another OTP. Otherwise the app stores a pending OTP session and moves the user through contact capture or OTP verification screens.
If no matching consumer is found, the app asks the user to confirm account creation. On confirmation, it creates the consumer profile/account path and starts the same OTP verification flow.
API 2.0 Behavior
The backend API keeps the same user-facing flow. Normal authenticated API calls use bearer tokens. The trusted-device cookie is only used during a future identity login so the same browser can skip OTP after a previous successful OTP verification.
The access token belongs to the
ConsumerProfile, not a single account row. That lets the authenticated consumer profile list and open the accounts it owns.
Login Responses
POST /auth/login has four successful branches:
POST /auth/login and POST /auth/otp/verify do not return the consumer profile payload. After any branch returns meta.access_token, the frontend can call the authenticated profile endpoint if it needs profile details.
For browser frontends, POST /auth/otp/verify and POST /auth/login must include credentials. OTP verification needs credentials so the browser accepts the Set-Cookie response, and login needs credentials so the browser sends the trusted-device cookie back. With fetch, use credentials: "include". With Axios, use withCredentials: true.
Guardrails
attempt_token and flow_token are short-lived and single-use after completion. OTP codes expire after the flow window, resend is rate-limited, and verification locks out after repeated failed attempts. Email OTPs are hashed before storage; mobile OTPs are verified through Twilio Verify.
Trusted-device cookies are HTTP-only and opaque to frontend JavaScript. The frontend should not read or build the cookie value. The backend sets it after a successful OTP verification with remember_device: true, stores only a server-side hash, and validates it on a future POST /auth/login.
For developer and controlled environment workflows, the backend may accept an OTP bypass according to server configuration: local accepts any six-digit OTP, and other environments can opt into a configured bypass code. Frontend code should still follow the normal OTP flow and treat bypass as an environment-only backend behavior.
