VEYNDO
Handbook
docs/account-security.mdView source ↗

English source · 4 min

Personal identity, recovery and two-factor sign-in

The native Rust account remains the owner of merchant passwords, active workspace membership and personal sessions. The private Experience service verifies email through its existing transactional email transport and sends short-lived signed identity assertions. Neither an email link nor an app integration key is a Studio session. Personal security is operational state, never sandbox configuration.

sequenceDiagram
  actor Merchant
  participant Entry as Start / Experience
  participant Mail as Existing email transport
  participant Core as Rust identity owner
  participant Studio
  Merchant->>Entry: Request recovery
  Entry->>Mail: Existing bounded email code
  Merchant->>Entry: Verify code
  Entry->>Core: Signed recovery lookup
  Core-->>Entry: Own workspaces and credential revision
  Merchant->>Entry: Choose new password
  Entry->>Core: Reset with expected revision
  Core->>Core: Revoke sessions and handoffs
  alt MFA enabled
    Core-->>Entry: Expiring challenge, no session
    Merchant->>Entry: Authenticator or unused recovery code
    Entry->>Core: Verify second factor
  end
  Core-->>Entry: Verified personal session
  Entry->>Core: One-use Studio handoff
  Entry-->>Merchant: Studio redirect
  Merchant->>Studio: Redeem handoff

Merchant and operator interfaces

Team & access → Account security and the operator console's Account security view share one component and one native contract. Enter the current password, add the displayed setup key to an authenticator (SHA-1, six digits, 30 seconds), and confirm a current code. Eight random recovery codes are shown once. Store them privately; each can be used once. Enrollment revokes older sessions, and returns one newly verified session so the codes can be displayed before signing out. Disabling protection requires the current password, a current authenticator or unused recovery code and the current security revision.

Password login, verified email/social entry, welcome password setup, recovery, additional-shop creation and Studio handoff all preserve the same factor gate. A pending challenge never goes into merchant/operator bearer storage. Native session responses carry user.emailVerified; local direct registration does not pretend that an email was verified. Hosted Start/Experience use verified identity.

Operator configuration

Keep IDENTITY_BROKER_KEY and IDENTITY_BROKER_ISSUER consistent between the native owner and its approved private identity service. Set IDENTITY_RECOVERY_URL to an HTTPS recovery entry without embedded credentials, query or fragment; the hosted default is https://start.veyndo.ai/recover. /api/auth/recovery returns available: false without a configured broker and valid destination. Recovery needs the existing private email provider; no second mailer or public reset-by-email endpoint is introduced. Back up the existing native encryption key with PostgreSQL; factor secrets use the existing authenticated encryption owner.

Migration 091 adds operational factor/challenge state, email verification, credential revisions and handoff-to-parent-session references. These identity records use forced system-only RLS and are excluded from shop release/history restore. A schema administrator still has trusted system authority. Password, authenticator secret and recovery codes must never enter audit history or logs.

Failure and admission behavior

  • Factor challenges expire after five minutes, allow at most five attempts and are consumed with their verified code in the native transaction. An authenticator time step or recovery code cannot be reused. Drift is limited to one adjacent step.
  • Enrollment expires after ten minutes; stale revisions roll back without consuming a recovery code. MFA can only be configured in a live personal workspace.
  • A password reset compares the credential revision captured after verified email; a stale recovery continuation cannot overwrite a newer password. MFA stays enabled.
  • App/integration credentials cannot configure personal MFA or mint a personal Studio handoff. A handoff is bound to a live parent personal session and expires after sixty seconds; logout/recovery/factor changes revoke it with that session.
  • Experience continuation data is encrypted and expires. GET only renders a form; same-origin POST performs the action. A failed Studio handoff can resume after successful reset/factor verification, without repeating those mutations.
  • A private Experience cookie is rechecked against its current native proof. Revoked proof cannot retain access to original Storyfront editors. Multiple auth reads of one HTTP request share admission; there is no cross-request rights cache.
  • Creation retries refresh the current authenticated proof. Jobs preflight identity before paid brand/model work and pause for reauthentication if a factor is required.

Source owners and verification

src/auth/{totp,mfa,sessions,handoff,broker_recovery,broker_session,broker_credentials}.rs share the existing native account and middleware owners. frontend/src/shared/auth/ is the common merchant/operator client. Private forms, continuations, email-code entry and current-cookie admission live in the private Experience repository. Provider credentials and private source are not copied into this repository.

The registered account_security and identity_broker HTTP/PostgreSQL suites cover native effects, foreign/forged/revoked proof, attempt/expiry/replay races, stale recovery revisions, cross-shop admission and process restart. Frontend regressions cover challenge/enrollment controls and storage boundaries. The private suite covers email-code → recovery → MFA → interrupted handoff → one-use completion with actual isolated storage and synthetic providers. These tests send no live email.

Lean covers the extracted Boolean factor-admission policy only. Cryptography, PostgreSQL, email delivery, browser behavior and broker integration remain tested trust boundaries; this is not a claim of universal identity-provider certification.