Appearance
Authentication states (web)
One statechart for every way into a storefront session: register, a code, a password, a magic link, Google and a password reset. Each edge is a call, and each call is traced to the line that makes the move. Green is the goal state; red marks a state where the code breaks a promise a storefront would rely on. The sign-up, login and password reset players walk the same edges one screen at a time.
Statechart
What each state does to the stored account (non_verified, verified, banned) is the account's own lifecycle, on Sign-up and login.
Every edge, with its source
Lines are in app/v1/service/customer_service.js of the customer backend unless another file is named.
| Edge | Call | What the code does |
|---|---|---|
| Loading → Guest | POST /api/v1/tenant | Looks the site up by origin and returns its brand key (site_service.js:272) |
| Guest → Code sent | POST /api/v1/register | Refuses an existing account (:913), creates one (:949) as non_verified (schemaSequalize/customers.js:62) and emails a code. Returns no token |
| Guest → Code sent | POST /api/v1/send-otp | Creates the account when none matches (:776), then stores the hashed code (:844). So asking for a code is also a way to sign up |
| Code sent → Signed in | POST /api/v1/login with otp | Sets verified (:188) and issues the customer token (:224) |
| Code sent → Code spent | POST /api/v1/verify-otp | Clears the code (:347) and answers OTP verification successful (:400) with no token and no state change |
| Guest → Signed in | POST /api/v1/login with password | Refused for non_verified (:129); otherwise as the code login |
| Guest → Magic link sent | POST /api/v1/send/magic/link | Emails a link to /magic/login/<token> (:1436) |
| Magic link sent → Signed in | POST /api/v1/magic/login/:magicToken | Sets verified (:1503) and returns a customer token |
| Guest → Signed in | POST /api/v1/user/register/google-oauth | Sets verified (:1711) and returns a customer token |
| Guest → Reset link sent | POST /api/v1/resetPassword | Emails a link to /reset-password?passwordToken=… (:1075) |
| Reset link sent → Password changed | POST /api/v1/changePassword/:passwordToken | Writes the new password and nothing else (:1290), then says Please login again (:1298) |
| Guest → Refused | login, verify-otp, send-otp, send magic link, magic login, resetPassword, changePassword | Each refuses banned with a message to contact support (:152, :327, :801, :1405, :1479, :1045, :1279) |
| Signed in → Guest | — | There is no logout route. Signing out is the storefront forgetting the token |
Properties
| Status | Property | Name |
|---|---|---|
| holds | A password login never signs in an unverified account. | passwordNeedsVerified |
| holds | Register, send-otp and verify-otp never return a customer token; only the sign-in calls do, and each of those also marks the account verified. | tokenMeansVerified |
| holds | A banned account is refused by login, verify-otp, send-otp, both magic-link calls, resetPassword and changePassword. | bannedRefused |
| breaks | A customer who verifies the code they were sent can then sign in with their password. | codeVerifies |
| breaks | After a password reset, the new password signs the customer in. | resetLetsYouIn |
Fix that makes both hold: have verify-otp set isVerified to verified, as login with the code already does at :188. Until then, the storefront should send the code to login, never to verify-otp.
Counterexample: the code is spent and the account is still unverified
- Guest → Code sent. The customer registers. The account is
non_verifiedand a code is emailed.customer_service.js:949 - Code sent → Code spent. The page posts the code to
verify-otp. The server checks it, clears it and answers OTP verification successful.isVerifiedis untouched and no token comes back.:347,:400 - Stuck. The customer logs in with their password and is told to verify their account first (
:129). The code they could have done that with is gone, so they have to ask for a new one.
Counterexample: the reset works and the login still fails
- Guest → Code sent. An account is created by
registerorsend-otp, and the customer never uses the code. It staysnon_verified.:776,:949 - Guest → Reset link sent → Password changed. The customer resets their password from the emailed link.
changePasswordwrites the password and says Please login again to continue.:1290,:1298 - Changed → Reset, still unverified. They log in with the new password and are told to verify first.
:129. The way out is a code login or a magic link, either of which setsverified.
Play the steps
| Edges | Player |
|---|---|
| Guest → Code sent → Signed in | Sign up, path Register and verify |
| Code sent → Code spent | Sign up, path The verify-otp trap |
| Guest → Signed in, by each door | Log in, paths Password, Email me a code, Magic link, Google |
| Guest → Reset link sent → Password changed | Reset a password, path Reset by link |