auth-client.tsfeat(security): make the security settings page mean what it says Three controls on Settings -> Security were wired to nothing. The two-factor switch ran a setTimeout that console.logged its values; the login-notifications switch had no storage, no trigger and no sender behind its fourteen translations; Delete Account had no onClick at all. A user could turn 2FA on and believe they had it. Two-factor, for an explicit allowlist (currently one user, at his organization's request). better-auth's twoFactor plugin alone would have recorded 2FA without ever enforcing it: emailAndPassword is disabled and every sign-in call site is signIn.social, but the plugin's hook matcher covers only /sign-in/{email,username, phone-number}, so the Google OAuth callback is never challenged. The plugin is registered for its primitives only -- TOTP secrets, encryption at rest, constant-time verify, single-use recovery codes, none of which should be hand-rolled -- and the enforcement is Cedar's own gate in http/app.ts, keyed on a per-session marker. Per-session so that a new sign-in is challenged again and revoking a session revokes its clearance with it. Two traps are documented where they bite: the marker is read from the database because the 30-day session cookie cache would otherwise report a stale value forever, and the first successful verification rotates the session while echoing back the PRE-rotation token. Enrolling also enforces, or the settings toggle would be decoration for anyone off the allowlist. The asymmetry is deliberate -- un-enrolling opts you out only if the allowlist was not what put you there -- and /two-factor/disable is rejected by the gate for required users, not merely hidden in the UI. Login notifications fire on a device not seen before, not on every sign-in. Comparing raw user_agent strings looks correct and is not: one live account shows 46 sessions across 6 user agents but only 3 real devices, because Chrome 148, 149, 150 and 151 each got their own row. That version would have sent three false "new device" warnings for routine auto-updates, and an alert that cries wolf monthly is one nobody reads. It compares a digit-stripped signature, tested against those exact strings. Delivered over email rather than the primary channel, which can be Slack reached with the very Google account the alert is about. Default off: defaulting it true would start mailing every existing user the moment this deploys. Deploy order matters. two_factor_auth.sql is additive and must land BEFORE this code -- registering the plugin makes better-auth select two_factor_enabled on every user query, so a missing column is a full outage, not a degraded feature. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 27, 2026, 11:36 PMauth-proxy.tsUse same-origin APIs on AWS app surfacesMar 31, 2026, 2:34 PM