Quiz - Authentication flows
You’re configuring emailAndPassword and want users verified before they ever hold a session. A teammate suggests requireEmailVerification: true together with autoSignIn: true so people “land in the app faster.” Why is that a broken state?
autoSignIn issues a session immediately — so the right pairing is autoSignIn: false.autoSignIn only works with OAuth providers, so it’s silently ignored on an email-and-password sign-up.requireEmailVerification overrides autoSignIn, so the user still won’t get a session until they verify.requireEmailVerification is “credential stored, verification email queued, no session” — handing out a session at the same time defeats it. It also happens to be the same flag that keeps the taken-email path enumeration-safe.A sign-in endpoint needs to survive an attacker who rotates IP addresses to guess one known account’s password. Which statement about Better Auth’s out-of-the-box defenses is correct?
A user clicks the verification link and emailVerified flips to true. What does that flag actually grant?
emailVerified: true is necessary but not sufficient — a floor, never a ceiling. Treating it as authorization is the conflation that separates someone who has shipped auth from someone about to. (And the verify endpoint must still collapse all failure causes into one message, or it reopens enumeration.)You wire the password-reset happy path: the link arrives, the new password saves, the user lands signed in, every test passes. Yet a security reviewer calls it a back door. What’s the most likely cause?
revokeSessionsOnPasswordReset was left at its default false, so an attacker holding a pre-reset session keeps their access after the reset.You’re enabling the magicLink() plugin. Which configuration line is the single most important one to get right, and why?
storeToken: 'hashed' — the plugin defaults to 'plain', so without it a leaked verification row is a live, working sign-in link.disableSignUp: true — leaving it false lets anyone create an account, which is the main risk of magic links.expiresIn: 60 * 60 — matching the verification link’s one-hour fuse keeps the UX consistent across the chapter.magicLink() defaults storeToken to 'plain' and stores the raw token. The omission is invisible until a breach surfaces it. (disableSignUp: false is the right default — the first click is the verification — and a magic link gets a shorter fuse than verification, five minutes, because the click itself is the credential.)During enrollment a user calls authClient.twoFactor.enable({ password }), scans the QR, and the screen shows their recovery codes. Is two-factor now active on the account?
enable() only generates and shows the secret. 2FA arms only after verifyTotp confirms the user typed a working code, which is what flips twoFactorEnabled to true.enable() succeeding is the commit; verifyTotp afterward is just an optional sanity check.trustDevice was passed; otherwise the account stays unprotected.twoFactorEnabled flips on the verifyTotp match, not on enable(). (trustDevice belongs to the sign-in challenge, not enrollment.)Your app runs at app.example.com, but you set rpID: 'example.com' in the passkey() config. What symptom should you expect?
app.example.com, but the passkey also wrongly works at marketing.example.com, widening the attack surface.rpID must be the registrable domain the app actually runs on. Scope a key to example.com while serving from app.example.com and the browser happily registers it, then refuses every assertion because the origins don’t match — the same origin-binding that stops phishing, now tripping you. When passkeys register but die at sign-in, check rpID first.You register http://localhost:3000/api/auth/callback/google in the Google console, but staging keeps failing the OAuth round-trip. Which of these would silently break it?
…/callback/google/ or an https vs http mismatch — because redirect URIs are matched exactly, with no wildcards.client_id across dev and staging, which Google rejects at the redirect step.redirectURI from the socialProviders block, which means Better Auth never tells Google where to return.redirectURI unset is correct: it defaults to the catch-all path. Separate per-environment clients are a good practice, not a failure cause.)A teammate wants to add a provider to trustedProviders “because lots of our users have it.” Why is that reasoning unsafe?
email_verified backstop, so the list itself IS the trust — popularity says nothing about whether its email claim is one you’d stake an account on.email_verified: true before any trusted auto-link, so a weak provider can’t actually link.trustedProviders list, which trusts every provider by default until you populate it.trustedProviders is the entire trust decision — there’s no email_verified check behind it for trusted providers. If a listed provider’s identity for an email falls into the wrong hands (a domain takeover, a Workspace edge), an attacker links into the account without the password. The empty list is safe-but-inert (it refuses); the over-trusting list is the account-takeover lever.Quiz complete
Score by topic