Previously only 2 share-code routes required a verified email; every other
authenticated route (residences, tasks, contractors, documents, notifications,
subscription, users, uploads, media — ~70 routes) accepted an authenticated but
UNVERIFIED user. This inverts the default to verified-by-default.
- router.go: add a `verified` sub-group that applies RequireVerified() ONCE at
the group level, and move all app-data route setups under it. Verification is
now the default; new routes are gated automatically. The authenticated-only
allow-list is just the sign-up surface (/auth/me, /auth/profile, /auth/account).
Public stays: register, health, webhooks, lookups.
- kratos_auth.go: fix a latent bug the gating exposed — the Redis session cache
stored the verified flag for 24h, so a user who verified their email mid-session
was still seen as unverified until the TTL expired (sign up -> verify -> create
residence would 403). Now only a cached verified=true is trusted (verification
is sticky); a cached verified=false re-resolves the live status from Kratos.
- auth_safety_test.go: add RequireVerified unit tests (verified passes,
unverified -> 403, no-user -> 401).
Validated: API gating test (unverified->403, verified->200) + full iOS XCUITest
suite green (211 passed) including the onboarding verify->use-immediately flow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>