Skip to content

Auth-flow fixes: device-flow UX, callback error handling, dev visibility (found during #503 live testing) #541

Description

@nhyiramante1

While building and live-testing the account & privacy page (#503 PR 1), we found and diagnosed several pre-existing auth-flow and dev-tooling issues. None are caused by or fixed in that PR. Planned as two follow-up PRs off main: (a) auth-flow fixes, (b) dev tooling.

The state_mismatch mystery — resolved, no code bug

Sign-ins were intermittently dying on a state_mismatch error page. A controlled clean run (one backend, one incognito window, one attempt) completed with zero errors — the OAuth state machinery is correct. The failures were multi-attempt artifacts: clicking sign-in twice overwrites the single better-auth.state cookie, and replaying/refreshing an old callback tab consumes-then-misses the verification row. Fix: prevention, not state-machine surgery (see Cancel/reset below). We will not ship skipStateCookieCheck or storeStateStrategy: 'cookie' — those were diagnostic-only.

Device-flow UX bugs

  • No way to cancel a sign-in in progress. useDeviceAuth.reset() exists but isn't exposed through AppAuthSession, so the pane offers no Cancel while polling — and abandoned attempts are exactly what caused the state_mismatch mess. One-line enabler + a button.
  • 4-minute device code is too tight (backend/src/auth.ts expiresIn: '4m'). A Google 2FA detour (observed live: an org-policy rejection then a second account) easily outlives the code, and the user only finds out at Approve time. Fix: longer expiry and/or a countdown in the pane.
  • The approval page shows raw JSON on failure{"error":"invalid_request","error_description":"Invalid user code"} instead of "This code expired — request a new one in Word."

Callback / error-page bugs

  • No errorCallbackURL on the approval-page sign-in, so OAuth failures land on Better Auth's default error page whose "Go Home" points at the backend root → 404 dead end.
  • requestDeviceCode / pollForToken parse the body before checking res.ok — an empty 502/proxy body throws "Unexpected end of JSON input" and masks the real failure ("backend not running").

Dev-experience gaps (found during PR 1 live testing)

  • Backend errors are invisible in dev. app.onError ships exceptions to PostHog only; nothing reaches the console. A dev-mode console.error would have surfaced PR 1's FIELD_NOT_ALLOWED bug in seconds instead of requiring a temporary patch.
  • Deletion freshness window: delete-account's re-auth gate only triggers past Better Auth's default freshAge (~1 day), so most real deletions never re-prompt. Decide whether deletion should always demand a fresh sign-in (tighten freshAge or gate deletion explicitly).
  • (minor) Repeated editor loads in demo mode mint a new anonymous user each time — cosmetic in dev DBs, but worth a look.

Dev script issues (→ tooling PR)

  • Frontend npm scripts still point at manifest.xml, which moved to public/manifest.xml in fbcfae3 — sideload scripts are broken; workaround is npx office-addin-debugging start public/manifest.xml.
  • The DB migration command isn't committed anywhere — should be a proper npm script (npx tsx src/migrate.ts).

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions