Appearance
Account Verification & Recovery β
The identity surface adds three public pages and one in-app banner that back Gnosari's transactional-email flows β email verification, password reset, and email change. All are pure front-end shells: they collect input, $fetch the backend /auth/* endpoints, and render success/error state. No business logic lives in the UI.
The three pages (/forgot-password, /reset-password, /verify-email) are registered as public routes in app/middleware/auth.global.ts β they must be reachable by a signed-out or unverified user. (confirm-email-change is also public.)
Concepts β
Soft-gate verification β
A newly registered user is unverified (is_verified = false). Verification is a soft gate: it drives a dismissible nudge banner but never blocks navigation, creating agents, publishing, or any other action. Google-provisioned accounts arrive pre-verified.
Anti-enumeration β
The forgot-password and resend-verification flows must not reveal whether an account exists. The backend always returns 200 with a fixed body, and the UI mirrors that: it shows the same neutral confirmation regardless of the response β even on a network error, a 500, or a rate-limit 429. The pages never surface "no such account".
Opaque tokens β
Reset and verify links carry a raw token in the query string (?token=β¦). The token is the only credential β the pages POST it to the backend, which redeems it atomically (single-use, TTL-bound). Because the token is opaque, the resend action on the verify page collects the email instead.
Flows β
Password reset β
Email verification (three states) β
/verify-email reads ?token= and POSTs it on mount (client-only side effect β never during SSR). It renders one of three states:
| State | When | UI |
|---|---|---|
verifying | initial, request in flight | spinner + "verifyingβ¦" |
success | 200 from /auth/verify-email | check icon + button to /dashboard |
expired | missing token, or any error from the API | warning icon + a resend form (collects email β POST /auth/resend-verification) |
The resend form is always-200/anti-enumeration too: it shows a neutral "check your inbox" toast regardless of outcome.
Guides β
The three pages β
| Route | Component(s) | Purpose |
|---|---|---|
/forgot-password | AuthForgotPasswordForm | Request a reset link; neutral confirmation on submit. |
/reset-password?token= | AuthResetPasswordForm (uses AuthPasswordFields) | Set a new password; success / invalid-token states. |
/verify-email?token= | inline three-state card | Verify on mount; resend on failure. |
All three use a bare UCard shell matching /login and /signup, Nuxt UI v4 semantic tokens, layout: 'marketing', and robots: 'noindex, nofollow' (these are not indexable pages).
The verify nudge banner β
AuthVerifyEmailBanner is a dismissible UAlert (color="warning") rendered by the authed layout only when !user.is_verified. It nudges the user to verify and exposes a Resend action (emits resend; the parent POSTs /auth/resend-verification) and a dismiss action (emits dismiss; the parent owns dismissal state). It never blocks the page β closing it simply hides the nudge.
Locale capture at signup / OAuth β
The user's email language (User.locale) is captured once, at account creation, and there is no locale field in the signup form:
- Password signup β the app's API proxy forwards the active UI locale as the
Accept-Languageheader on every/api/*call, includingPOST /users/. The backend negotiatesUser.localefrom that header. No signup-form change is needed; the build-wizard signup inherits it through the same proxy. - Google OAuth β the OAuth callback is a bare-domain request that does not flow the i18n proxy, so the Google callback page supplies the locale as an explicit
localefield onPOST /auth/google.
In both cases an absent/unparseable locale falls back to en. Transactional emails are then rendered in that locale by the Resend automation trees.
Related β
- API Β· Authentication β the
/auth/*endpoints these pages call. - API Β· Users β
is_verified,locale, and email-change gating. - Internationalisation (i18n) β
Accept-Languagewiring in the proxy.