Skip to content

M2M Audit & Monitoring

Every M2M authentication event is logged, queryable, and surfaced through Prometheus metrics. This page covers the audit model, the query endpoint, anomaly detection, metrics, and retention.

The ApiClientAuditEvent model

Every significant M2M event produces an ApiClientAuditEvent:

Field Type Description
client_id str The ApiClient that triggered the event.
event_type str Event category (see below).
timestamp datetime When the event occurred.
request_ip str Client IP address.
success bool Whether the operation succeeded.
failure_reason str Details when success=false.
actor_user_id str The human user who performed the action (for management operations).

Event types

Event type Trigger
token_issued Access token successfully issued.
token_failed Token request rejected (bad signature, expired, replay, lockout, IP denied).
key_added A new public key was uploaded to the client.
key_rotated A key was replaced (new key added, old key revoked in sequence).
key_revoked A specific key was revoked.
scope_changed The client's capabilities list was updated.
client_created A new ApiClient was registered.
client_disabled The client was disabled (is_enabled set to false).
client_revoked The client was permanently revoked or deleted.
ip_whitelist_changed The client's IP allowlist was updated.
dpop_toggled The dpop_required flag was changed.
anomaly_detected The anomaly detection heuristic flagged unusual activity.

Query endpoint

Retrieve audit events for a specific client:

GET /api-clients/{client_id}/audit?event_type=token_failed&from=2026-04-01&to=2026-04-06&per_page=50
Parameter Type Default Description
event_type str Filter by event type.
from date 30 days ago Start of date range.
to date Today End of date range.
success bool Filter by success/failure.
per_page int 25 Page size.

Example response:

{
  "items": [
    {
      "event_type": "token_failed",
      "timestamp": "2026-04-05T14:32:10Z",
      "request_ip": "203.0.113.99",
      "success": false,
      "failure_reason": "Signature verification failed for kid=key-old123"
    },
    {
      "event_type": "token_issued",
      "timestamp": "2026-04-05T14:30:00Z",
      "request_ip": "10.0.1.42",
      "success": true,
      "failure_reason": null
    }
  ],
  "total": 2,
  "page": 1,
  "per_page": 50
}

cURL

# All failed token requests in the last week
curl "http://localhost:5001/api-clients/664abc.../audit?event_type=token_failed&from=2026-03-30" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

# All events for a client
curl "http://localhost:5001/api-clients/664abc.../audit" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Anomaly detection

Craft Easy includes first-generation heuristics that flag unusual M2M activity:

Heuristic What it detects
Failure spike Sudden increase in token_failed events compared to the client's baseline.
New IP Token request from an IP not previously seen for this client.
Unusual time Token request outside the client's historical activity window.
Rapid key rotation Multiple key additions or revocations in a short period.

When a heuristic fires, an anomaly_detected audit event is created with details in the failure_reason field. Anomaly events are informational — they do not block the request.

Anomaly thresholds are configured per-deployment and documented in the Craft Easy settings reference.

Prometheus metrics

The M2M auth module exposes the following metrics at the configured metrics endpoint (default /metrics):

Metric Type Labels Description
m2m_token_issued_total Counter client_id, tenant_id Total tokens issued.
m2m_token_denied_total Counter client_id, reason Total token denials. reason is one of: invalid_signature, expired_assertion, jti_replay, ip_denied, rate_limited, locked_out, client_disabled, no_matching_key, invalid_scope.
m2m_jti_replay_total Counter client_id Replay attempts detected (subset of m2m_token_denied_total{reason="jti_replay"}).
m2m_dpop_verification_total Counter client_id, result DPoP proof verifications. result is success or failure.
m2m_token_request_duration_seconds Histogram client_id Time taken to process a token request.
m2m_active_clients Gauge tenant_id Number of enabled ApiClient records per tenant.

Example Prometheus queries

# Token denial rate over 5 minutes
rate(m2m_token_denied_total[5m])

# Top denial reasons
topk(5, sum by (reason) (rate(m2m_token_denied_total[1h])))

# Replay attempts per client
sum by (client_id) (rate(m2m_jti_replay_total[1h]))

# P95 token request latency
histogram_quantile(0.95, rate(m2m_token_request_duration_seconds_bucket[5m]))

Alerting examples

# Alert on high token denial rate
- alert: M2MHighDenialRate
  expr: rate(m2m_token_denied_total[5m]) > 10
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "High M2M token denial rate for {{ $labels.client_id }}"

# Alert on replay attempts
- alert: M2MReplayDetected
  expr: increase(m2m_jti_replay_total[10m]) > 0
  labels:
    severity: critical
  annotations:
    summary: "JTI replay detected for client {{ $labels.client_id }}"

Retention

Audit events are retained according to the M2M_AUDIT_RETENTION_DAYS setting (default: 90 days). A built-in retention job runs periodically and deletes events older than the configured threshold.

Events related to security incidents (anomaly_detected, client_revoked, key_revoked) are retained for twice the standard retention period.

To query or export historical audit data beyond the retention window, use the BI Export pipeline to stream api_client_audit events to your data warehouse before they are purged.

Integration with tenant alerting

Audit events can trigger webhooks when configured. Set up a webhook subscription for the m2m.anomaly_detected or m2m.token_denied event types to push alerts to Slack, PagerDuty, or any HTTP endpoint.

For event bus integration, M2M audit events are also published as DomainEvent instances with event types like m2m.token_issued, m2m.token_denied, and m2m.anomaly_detected. Subscribe to these via the event bus for in-process reactions.