Skip to content

feat: structured audit logging system#537

Merged
lakhansamani merged 7 commits intomainfrom
feat/audit-log
Apr 3, 2026
Merged

feat: structured audit logging system#537
lakhansamani merged 7 commits intomainfrom
feat/audit-log

Conversation

@lakhansamani
Copy link
Copy Markdown
Contributor

@lakhansamani lakhansamani commented Apr 3, 2026

Summary

Complete audit logging system for Authorizer — tracks all security-relevant events across user authentication, admin operations, OAuth flows, and token lifecycle.

What's included

  • Storage layer: AuditLog schema with all 13+ database providers (SQL via GORM, MongoDB, ArangoDB, CassandraDB, DynamoDB, Couchbase)
  • GraphQL API: _audit_logs admin query with filtering by action, actor_id, resource_type, resource_id, and timestamp range
  • 35+ audit events instrumented across:
    • User auth: login (success/failed), signup, logout, verify email/OTP, magic link, password reset/forgot, MFA, profile update, account deactivation
    • Admin ops: admin login/logout, user CRUD, access enable/revoke, invite, webhook CRUD, email template CRUD
    • OAuth/HTTP: OAuth callback, token issue/refresh/revoke, session termination
  • Type-safe constants for actor types (user, admin) and resource types (user, session, admin_session, webhook, email_template, token)
  • Safe async pattern: IP/UserAgent extracted before goroutine, context.Background() for DB writes
  • Integration tests: 8 test cases using runForEachDB pattern covering CRUD, filtering, timestamp range, and field round-trip

Architecture

  • internal/constants/audit_event.go — Event, actor type, and resource type constants
  • internal/storage/schemas/audit_log.go — Schema + AsAPIAuditLog() conversion
  • internal/graphql/audit_log_helper.go — Fire-and-forget helper for GraphQL handlers
  • internal/http_handlers/audit_log_helper.go — Fire-and-forget helper for HTTP handlers
  • internal/graphql/audit_logs.go_audit_logs admin query handler

Follow-up items (separate issues)

  • Dashboard UI for viewing/filtering audit logs
  • Scheduled cleanup with configurable retention period (default 1 year per PCI DSS)

Test plan

  • go build ./... passes
  • TEST_DBS=sqlite — 8/8 audit log tests pass
  • make test — full Postgres validation
  • make test-all-db — cross-DB validation
  • Manual: login/signup/admin ops and query _audit_logs via GraphQL playground

* feat: add structured audit log schema and storage layer

Add AuditLog schema with support for all 6 database providers (SQL/GORM,
MongoDB, ArangoDB, Cassandra, DynamoDB, Couchbase). Includes AddAuditLog,
ListAuditLogs (with filtering by actor_id, action, resource_type, etc),
and DeleteAuditLogsBefore for log retention. Ref: RFC #505.

* chore: add more auditlog events
Add AuditLog type, AuditLogs response, and ListAuditLogRequest input
to the GraphQL schema. Supports filtering by action, actor_id,
resource_type, resource_id, organization_id, and timestamp range.

- Add AsAPIAuditLog() conversion method to schemas
- Create audit_logs.go handler with admin-only access control
- Add AuditLogs method to graphql Provider interface
- Wire resolver in schema.resolvers.go
Create logAuditEvent() helper on graphqlProvider that extracts
request info (IP, UserAgent) before spawning a fire-and-forget
goroutine for safe async audit logging.

Instrument all user-facing auth operations:
- login (success + failed), signup, logout
- verify_email, verify_otp (email vs phone conditional)
- magic_link_login, forgot_password, reset_password
- resend_otp, resend_verify_email
- deactivate_account, update_profile
* feat: add audit log helper and instrument user auth flows

Create logAuditEvent() helper on graphqlProvider that extracts
request info (IP, UserAgent) before spawning a fire-and-forget
goroutine for safe async audit logging.

Instrument all user-facing auth operations:
- login (success + failed), signup, logout
- verify_email, verify_otp (email vs phone conditional)
- magic_link_login, forgot_password, reset_password
- resend_otp, resend_verify_email
- deactivate_account, update_profile

* feat: instrument admin operations with audit logging

Add audit events to all admin GraphQL mutations:
- admin_login (success + failed), admin_logout
- delete_user, update_user, enable_access, revoke_access
- invite_members
- add/update/delete webhook
- add/update/delete email_template

* fix: address review findings in admin audit instrumentation

- Remove incorrect ActorEmail from delete_user (was logging deleted
  user's email as actor's email, but admin has no email)
- Capture ResourceID from AddWebhook return value for audit log
- Capture ResourceID from AddEmailTemplate return value for audit log
* feat: add audit log helper and instrument user auth flows

Create logAuditEvent() helper on graphqlProvider that extracts
request info (IP, UserAgent) before spawning a fire-and-forget
goroutine for safe async audit logging.

Instrument all user-facing auth operations:
- login (success + failed), signup, logout
- verify_email, verify_otp (email vs phone conditional)
- magic_link_login, forgot_password, reset_password
- resend_otp, resend_verify_email
- deactivate_account, update_profile

* feat: instrument admin operations with audit logging

Add audit events to all admin GraphQL mutations:
- admin_login (success + failed), admin_logout
- delete_user, update_user, enable_access, revoke_access
- invite_members
- add/update/delete webhook
- add/update/delete email_template

* fix: address review findings in admin audit instrumentation

- Remove incorrect ActorEmail from delete_user (was logging deleted
  user's email as actor's email, but admin has no email)
- Capture ResourceID from AddWebhook return value for audit log
- Capture ResourceID from AddEmailTemplate return value for audit log

* feat: instrument OAuth/HTTP handlers with audit logging

Create logAuditEvent() helper for httpProvider with safe goroutine
pattern (extracts IP/UserAgent before spawn, uses context.Background).

Instrument HTTP handlers:
- oauth_callback: AuditOAuthCallbackSuccessEvent with provider metadata
- token: AuditTokenIssuedEvent / AuditTokenRefreshedEvent by grant type
- revoke_refresh_token: AuditTokenRevokedEvent after session deletion
- logout: AuditSessionTerminatedEvent after session cleanup
Schema cleanup:
- Remove OrganizationID field (no org ID exists in config)
- Remove Timestamp field (duplicated CreatedAt)
- Remove UpdatedAt field (audit logs are immutable)
- Update all 6 DB providers, GraphQL schema, helpers, and conversion
- Fix Cassandra CREATE TABLE to match new schema
- Fix CreatedAt handling: all providers now conditionally set it
  (preserves caller-supplied values for retention testing)

String literal constants:
- Add AuditActorTypeUser, AuditActorTypeAdmin constants
- Add AuditResourceTypeUser, AuditResourceTypeSession,
  AuditResourceTypeAdminSession, AuditResourceTypeWebhook,
  AuditResourceTypeEmailTemplate, AuditResourceTypeToken constants
- Replace all inline string literals across 30 files

Test improvements:
- Migrate audit_logs_test.go to runForEachDB pattern
- Add resource_type filter test
- Add timestamp range filter test
- Add round-trip field preservation test
- Use constants throughout all test files
@lakhansamani lakhansamani merged commit b8dbe62 into main Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant