Refreshes _docs/02_document/ to reflect the cycle-2 auth-modernization
+ CMMC hardening landings (AZ-531..AZ-538). Authoritative source for
the ripple set is ripple_log_cycle2.md.
Covered:
- architecture.md (section 1 rewritten, ADRs 6-9 added)
- data_model.md (sessions, audit_events, user columns, migrations)
- system-flows.md (F1 rewritten; F11-F17 added; F2/F7/F9 minor)
- module-layout.md (cycle-2 sub-component table)
- diagrams/flows/flow_login.md (dual-token + MFA)
- components/{01_data_layer,03_auth_and_security,05_admin_api}
- modules/ (12 new, 8 modified — full Argon2id/ES256/MFA/refresh
/mission/session/audit/jwks rollup)
- tests/{blackbox,security,traceability-matrix}
Step 13 (Update Docs) output for cycle 2.
Co-authored-by: Cursor <cursoragent@cursor.com>
3.0 KiB
Module: Azaion.Common.Entities.AuditEvent
Purpose
Append-only audit row for security-relevant events: login outcomes, lockouts, and the MFA enrollment / login lifecycle. Drives both the per-account sliding-window rate limit (AZ-537) and the human-readable security trail.
Added in cycle 2 (2026-05-14). Initial event types from AZ-537 (login_failed / login_success / login_lockout); MFA event types added by AZ-534 in the same cycle.
Public Interface
AuditEvent
| Property | Type | Description |
|---|---|---|
Id |
long |
DB-assigned identity. |
EventType |
string |
One of AuditEventTypes. |
OccurredAt |
DateTime |
now() at insert. |
Email |
string? |
Normalised lowercase. NULL for system events without a subject. |
Ip |
string? |
Caller IP from HttpContext.Connection.RemoteIpAddress. NULL for background tasks. |
Metadata |
string? |
Reserved for future structured payload. Not used today. |
AuditEventTypes (constants)
| Value | When |
|---|---|
login_failed |
Wrong password, locked account, or rate-limit reject. |
login_lockout |
Account just hit MaxAttempts and was locked. |
login_success |
Password verified, MFA not required. |
mfa_enroll |
/users/me/mfa/enroll succeeded. |
mfa_confirm |
/users/me/mfa/confirm succeeded; MFA now active. |
mfa_disable |
/users/me/mfa/disable succeeded. |
mfa_login_success |
/login/mfa succeeded with TOTP. |
mfa_login_failed |
/login/mfa rejected (bad TOTP and bad recovery code). |
mfa_recovery_used |
/login/mfa succeeded with a recovery code (also burns the code). |
Internal Logic
None — pure data class. All write/read logic lives in AuditLog.
Dependencies
None.
Consumers
AuditLog— produces every row; reads viaCountRecentFailedLogins.AzaionDb.AuditEvents—ITable<AuditEvent>access.AzaionDbSchemaHolder— mapsAuditEventto theaudit_eventstable.
Data Models
Maps to PostgreSQL table audit_events (defined in env/db/07_auth_lockout_and_audit.sql).
Columns: id (bigserial PK), event_type (varchar(64)), occurred_at (timestamp default now()), email (varchar(160) NULL), ip (varchar(64) NULL), metadata (text NULL).
Index: audit_events_event_type_email_idx (event_type, email, occurred_at DESC) — supports the per-account sliding-window failed-login count in O(window-rows).
Configuration
None.
External Integrations
None.
Security
- Append-only by convention —
azaion_adminonly hasINSERT, SELECTon the table. - Stores PII (email, IP); access is gated to
azaion_adminandazaion_readeronly. No public endpoint surfaces audit rows. - The table backs CMMC AC.L2-3.1.8 ("limit unsuccessful logon attempts") — tampering with it bypasses the rate limit + lockout enforcement.
Tests
Indirectly tested via RateLimitLockoutTests, MfaEnrollmentTests, MfaLoginTests (assertions on the resulting audit_events rows).