Skip to content

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.

EdgeCallWhat the code does
Loading → GuestPOST /api/v1/tenantLooks the site up by origin and returns its brand key (site_service.js:272)
Guest → Code sentPOST /api/v1/registerRefuses an existing account (:913), creates one (:949) as non_verified (schemaSequalize/customers.js:62) and emails a code. Returns no token
Guest → Code sentPOST /api/v1/send-otpCreates 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 inPOST /api/v1/login with otpSets verified (:188) and issues the customer token (:224)
Code sent → Code spentPOST /api/v1/verify-otpClears the code (:347) and answers OTP verification successful (:400) with no token and no state change
Guest → Signed inPOST /api/v1/login with passwordRefused for non_verified (:129); otherwise as the code login
Guest → Magic link sentPOST /api/v1/send/magic/linkEmails a link to /magic/login/<token> (:1436)
Magic link sent → Signed inPOST /api/v1/magic/login/:magicTokenSets verified (:1503) and returns a customer token
Guest → Signed inPOST /api/v1/user/register/google-oauthSets verified (:1711) and returns a customer token
Guest → Reset link sentPOST /api/v1/resetPasswordEmails a link to /reset-password?passwordToken=… (:1075)
Reset link sent → Password changedPOST /api/v1/changePassword/:passwordTokenWrites the new password and nothing else (:1290), then says Please login again (:1298)
Guest → Refusedlogin, verify-otp, send-otp, send magic link, magic login, resetPassword, changePasswordEach 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 ​

StatusPropertyName
holdsA password login never signs in an unverified account.passwordNeedsVerified
holdsRegister, 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
holdsA banned account is refused by login, verify-otp, send-otp, both magic-link calls, resetPassword and changePassword.bannedRefused
breaksA customer who verifies the code they were sent can then sign in with their password.codeVerifies
breaksAfter 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 ​

  1. Guest → Code sent. The customer registers. The account is non_verified and a code is emailed. customer_service.js:949
  2. Code sent → Code spent. The page posts the code to verify-otp. The server checks it, clears it and answers OTP verification successful. isVerified is untouched and no token comes back. :347, :400
  3. 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 ​

  1. Guest → Code sent. An account is created by register or send-otp, and the customer never uses the code. It stays non_verified. :776, :949
  2. Guest → Reset link sent → Password changed. The customer resets their password from the emailed link. changePassword writes the password and says Please login again to continue. :1290, :1298
  3. 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 sets verified.

Play the steps ​

EdgesPlayer
Guest → Code sent → Signed inSign up, path Register and verify
Code sent → Code spentSign up, path The verify-otp trap
Guest → Signed in, by each doorLog in, paths Password, Email me a code, Magic link, Google
Guest → Reset link sent → Password changedReset a password, path Reset by link

Batch platform, Shopify app and API documentation