Authentication and Account Management
This page explains how the authentication system works, why certain design decisions exist, and how the confirmation-token flows protect sensitive account changes.
For endpoint payloads, see Endpoints. For env vars, see Configuration.
Design Rationale
Better Auth manages sessions and the core auth lifecycle. The custom ProfileController extends this with a confirmation-token layer that enforces email verification before applying sensitive changes. This prevents account takeovers through unverified email addresses.
Session Lookup
Every protected profile endpoint calls getSession({ disableCookieCache: true }). This forces a live database read and prevents stale cookie state from granting access after a session is revoked.
Blocked Mutation Endpoints
The custom plugin confirmedAccountChangePlugin blocks three native Better Auth paths:
| Blocked path | Reason |
|---|---|
/update-user | Bypasses email confirmation |
/change-password | Bypasses current password check |
/change-email | Bypasses two-step email flow |
When you call one of these paths, the API returns 400 BAD_REQUEST with code CONFIRMATION_REQUIRED.
INCORRECT: Calling /api/auth/change-email directly from the frontend.
CORRECT: Use POST /api/profile/request-email-change to start the confirmed flow.
Callback URL Policy
Every profile change request requires a callbackURL. The API appends the confirmation token to this URL and includes it in the email.
INCORRECT: callbackURL: "http://evil.example.com/confirm" — the API rejects untrusted origins.
CORRECT: callbackURL: "http://localhost:4200/profile/confirm" — must match a trusted origin.
Trusted origins in local development:
http://localhost:4200http://127.0.0.1:4200
Profile Change Flow
One email confirmation updates name and nickname.
Email Change Flow
Two emails are sent — one to confirm the old address, one to verify the new address.
Password Change Flow
The API verifies the current password before storing the hashed new password. On confirmation, all active sessions are revoked.
The frontend must redirect the user to login after a password confirmation.
Validation and Error Reference
| Condition | HTTP Status |
|---|---|
| Missing required field | 400 Bad Request |
| Untrusted or relative callback URL | 400 Bad Request |
| Email not yet verified | 403 Forbidden |
| Duplicate nickname or email | 409 Conflict |
| Invalid or expired token | 404 Not Found |
| No valid session | 401 Unauthorized |