Skip to content

feat(auth): hashed single-use verification codes, no stub users before verification - #395

Merged
roncodes merged 1 commit into
release/v0.6.72from
feature/sms-guard-phase-1
Oct 13, 2026
Merged

roncodes merged 1 commit into
release/v0.6.72from
feature/sms-guard-phase-1

Conversation

@roncodes

Copy link
Copy Markdown
Member

What

Phase 1 of the SMS guard on the FleetOps side, on top of #393 (phase 0). Pairs with fleetbase/core-api#307 and needs the core release that carries it.

  • No stub users before verification. POST v1/customers/request-creation-code no longer persists a User for an identity that merely asked for a code. The code's subject is the existing user when there is one, otherwise an unsaved user carrying the name and contact details the message needs. A users row per unverified identity was an enumeration and pollution vector behind a leaked credential.
  • Hashed single-use codes. Every SMS code the driver and customer APIs send (login-with-sms, request-creation-code, forgot-password) is issued with core's hashed option: HMAC on the row, 10 minute expiry, plain code only in the message.
  • One verify-and-consume path. customers/ (create), customers/verify-code, customers/reset-password, drivers/verify-code and drivers/reset-password check the submitted code against the live codes for the identity or user, newest first, lock a code after five wrong guesses, and mark the matching one used. Codes written before this change (plaintext) verify the same way; a used code never verifies again.
  • Tracking page consumes a verified code instead of deleting it, so the row stays for the ledger.

Compatibility

API payloads are unchanged. Requires a core with VerificationCode::consume() and the STATUS_* constants (fleetbase/core-api#307); the composer constraint is left for the release that carries it.

Merge order

Based on feature/sms-guard so the diff shows only phase 1. Before merging #393, retarget this PR to release/v0.6.72; otherwise GitHub closes it when the base branch is deleted.

Tests

Contract tests updated for the removed stub user and the spent-code semantics; a new seam test covers a wrong guess counting against live codes, the right code being used up, and a used code refusing to verify again; the tracking-access test asserts the used row stays. Not run locally: the package's Pest bootstrap does not start under this machine's PHP 8.4. CI is red for a pre-existing reason (Composer cannot resolve core-api ^1.6.69 until it is tagged); once that is green, the coverage gate will show whether any branch here is uncovered.

Follow-ups

Per-identity attempt lockout on the API check endpoints (the storefront has it); hashed email codes.

…e verification

Phase 1 of the SMS guard on the FleetOps side (needs core-api with the
hashed-code APIs, fleetbase/core-api#307):

- The customer API no longer persists a User for an identity that merely asked
  for a creation code; the code's subject is the existing user or an unsaved one.
  A users row per unverified identity was a way to fill the table from a leaked
  credential.
- Every SMS code the driver and customer APIs send is issued hashed (HMAC on the
  row, 10 minute expiry, plain code only in the message).
- One verify-and-consume path: create, verify-code, and the password resets check
  the submitted code against the live codes for the identity or user, newest
  first, lock a code after five wrong guesses, and mark the matching one used.
  Plaintext rows written before this verify the same way.
- The public tracking page consumes a verified code instead of deleting it, so
  the row stays for the ledger.
Base automatically changed from feature/sms-guard to release/v0.6.72 October 13, 2026 10:34
@roncodes
roncodes merged commit 7cd82d9 into release/v0.6.72 Oct 13, 2026
@roncodes
roncodes deleted the feature/sms-guard-phase-1 branch October 13, 2026 10:36
@roncodes

roncodes commented Oct 13, 2026 •

Copy link
Copy Markdown
Member Author

Follow-up: the API endpoints here still flattened an SmsBlockedException into the generic 400 send error. Fixed in the next PR on this release branch, fix(api): answer an SMS guard refusal with its status, so login-with-sms, request-creation-code and forgot-password answer 422 / 429 + Retry-After / 423 as storefront does, keeping the email fallback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant