Files
admin/_docs/02_document/modules/common_entities_audit_event.md
Oleksandr Bezdieniezhnykh a77b3f8a59 [AZ-529] [AZ-530] Cycle-2 documentation refresh
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>
2026-05-14 09:22:53 +03:00

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 via CountRecentFailedLogins.
  • AzaionDb.AuditEventsITable<AuditEvent> access.
  • AzaionDbSchemaHolder — maps AuditEvent to the audit_events table.

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_admin only has INSERT, SELECT on the table.
  • Stores PII (email, IP); access is gated to azaion_admin and azaion_reader only. 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).