Auth Audit Events
Every significant auth action emits an audit event. Events flow from the auth service through a durable PGMQ queue to the control-plane consumer, which writes them to the project’s audit log and fans them out to any configured webhooks. The transport is at-least-once and survives an auth-service restart.
The 15 event types
| Event | When it fires |
|---|---|
login.success | A password sign-in succeeded (a Better Auth session was created). |
login.failure | A password sign-in failed (bad credentials). Carries the attempted email + IP/UA. |
logout | A user signed out (POST /logout). |
signup | A new user was created. |
password.reset.requested | A password-reset email was requested (POST /recover). |
password.reset.completed | A recovery-flow code was exchanged for a session (reset finished). |
password.changed | An authenticated user set a new password (PUT /user). |
email.verified | An email was confirmed (OTP or link). |
session.created | A session was created via an OAuth/PKCE code exchange. |
session.revoked | A user’s sessions were revoked (on sign-out). |
oauth.callback | An OAuth sign-in completed at the callback. |
token.refreshed | A refresh-token grant minted a new access token. |
mfa.enabled | A TOTP factor was verified for the first time. |
mfa.verified | A TOTP code was verified. |
mfa.disabled | A TOTP factor was unenrolled. |
Event shape
Each event carries the project id, the event type, and (where applicable) the
user id, email, IP address, user agent, an error code (failures), and a small
metadata object (e.g. session_id).
Where they go
- Audit log — query via the management API:
GET /api/v1/admin/audit(and…/audit/export). - Webhooks — configure a project webhook subscribed to auth events; the consumer dispatches each event as an HTTP delivery. See the webhooks docs.
Notes
login.failureis emitted once, by the auth service. The control-plane per-IP brute-force limiter only rate-limits credential endpoints; it does not emit its own auth event, so there is no double-counting.password.reset.requestedis recorded whether or not the email exists, but the HTTP response is always200(the audit log is operator-private; the wire response never leaks user existence).
Next: Auth Overview.