A customer-support system cannot treat an identity provider's event history as the complete record of a risky login. The provider can record authentication activity, but it cannot explain why an internal service accepted a device fingerprint, opened an account-recovery path, or let an agent override a challenge.
TL;DR: retain the provider history as source evidence, then write your own append-only audit events for application decisions. Join the two with stable correlation identifiers. For SOC 2 evidence, export the smallest time-bounded bundle that proves who acted, what the system decided, which policy version ran, and what happened next. Do not copy every provider payload into a second database by reflex.
This is the practical difference: provider history says an authentication event occurred; the application audit log says what that event meant to your control.
Why isn't provider event history enough?
Authentication and authorization meet at an awkward boundary. An identity service observes credentials, factors, sessions, and some account changes. A support application owns device-risk scoring, recovery eligibility, case state, and agent permissions. Neither side can truthfully reconstruct the other from inference alone.
Consider a login from a device fingerprint the application has never seen. The identity layer may show a successful factor challenge. The support system may still route the account into assisted recovery because the device is new and the requested action would expose private case data. If an agent later approves recovery, the evidence question is not merely, "Did authentication succeed?" It is, "Which recovery rule was evaluated, what inputs did it use, and who approved the exception?" The provider record, risk result, recovery decision, and approval can occur at different moments, under different access controls, yet an evidence reviewer needs to follow one causal chain without guessing from timestamps.
Those are separate claims.
That gap matters.
OWASP recommends logging and reviewing authentication failures and password failures, and it treats recovery as an alternate authentication mechanism that should not be weaker than normal authentication. That guidance supports collecting authentication signals. It does not make a third-party event feed a record of private application logic.
Draw the evidence boundary before choosing fields
Start with control assertions, not a giant event schema. For this customer-support flow, a useful assertion might be: high-risk login attempts cannot enter account recovery without the required challenge or an authorized human approval. That sentence tells you which facts have evidentiary value.
| Evidence question | Provider history | Application audit log |
|---|---|---|
| Was an authentication factor attempted and what was its outcome? | Primary source | Store a reference and the consumed outcome |
| Which device-risk policy ran? | Usually outside its decision boundary | Primary source |
| Why was self-service recovery denied or allowed? | Usually outside its decision boundary | Primary source |
| Which support actor approved an exception? | May identify the actor's login | Primary source for the business action |
| Was the resulting session or recovery grant used? | Partial, depending on the boundary | Primary source for protected application actions |
The word "primary" matters. Duplicating a provider event does not make the copy more authoritative, and recording only a provider event does not make it complete. Preserve provenance: record the provider event identifier and occurrence time, but give the application's decision its own identifier and timestamp.
I would also keep raw device fingerprints out of the general audit stream. A stable, keyed pseudonymous device reference can support correlation while reducing how much sensitive device data spreads through exports, dashboards, and test fixtures. The risk engine can retain the underlying inputs under its own access and retention rules. The audit event needs the decision-relevant reference, not an accidental shadow profile. This creates a real trade-off: investigators cannot reconstruct every risk input from the audit store alone, so the protected risk system must support a separately authorized lookup for the cases that require it.
Record decisions, not a replay of every payload
A compact event contract is easier to test and cheaper to retain than arbitrary JSON copied from every dependency. More important, it forces the producer to name the control decision at the moment it happens. The following TypeScript shape covers one recovery decision without pretending to be a universal security schema.
type RecoveryDecisionEvent = {
eventId: string;
eventType: "recovery.decision_recorded";
occurredAt: string;
actor: {
kind: "system" | "support_agent";
subjectRef: string;
};
accountRef: string;
deviceRef: string;
correlationId: string;
providerEventRef?: string;
policy: {
id: string;
version: string;
};
decision: "allow_self_service" | "require_assisted" | "deny";
reasonCodes: string[];
challengeOutcome: "passed" | "failed" | "not_attempted";
};
function validateRecoveryEvent(event: RecoveryDecisionEvent): void {
if (event.decision === "allow_self_service" && event.challengeOutcome !== "passed") {
throw new Error("Self-service recovery requires a passed challenge");
}
if (!event.eventId || !event.correlationId || !event.policy.version) {
throw new Error("Evidence identifiers and policy version are required");
}
}
The validator is intentionally narrow. It demonstrates an invariant; it is not a complete recovery policy. In a real design, reason codes should come from a controlled vocabulary, timestamps should be validated, and producers should be authenticated before an event reaches durable storage.
Do not log secrets, recovery codes, credential material, or the full device fingerprint in these events. OWASP's logging guidance calls out data that should usually be removed, masked, sanitized, hashed, or encrypted rather than recorded directly. The same guidance recommends protecting logs from tampering and restricting access. An audit log is a security asset, not a convenient dumping ground.
Make the join survive partial failure
The weak implementation writes the business change, then makes a best-effort network call to an audit service. A timeout between those steps creates the exact gap an assessor will ask about. Reversing the order is no better: the log can claim a recovery approval that the application never committed.
Neither failure is subtle.
For decisions stored in your database, commit the state change and an outbox record in one transaction. A separate publisher can deliver the event to append-oriented storage and retry safely using eventId as an idempotency key. When a provider event participates in the decision, carry its immutable reference into the application event; do not make a timestamp-only join your normal path.
Failure still needs a visible state. Monitor outbox age, rejected events, duplicate identifiers, schema-validation failures, and the share of recovery decisions missing an expected provider reference. Alerting on aggregate request errors alone can miss an evidence gap that leaves the customer flow apparently healthy.
Retention deserves its own decision. A provider's available history and export behavior may change independently of your evidence window, while retaining all detailed identity telemetry forever creates another liability. Define retention from the control, legal, and investigation needs that apply to your system. Then test retrieval across the whole chosen window. Avoid claiming a universal SOC 2 retention period; the relevant scope and criteria depend on the system and engagement. An internal audit log is also not suitable as the sole record when the application makes no meaningful decision of its own; copying complete provider history in that case adds storage, access, and redaction work without filling an evidence gap. At the other extreme, relying only on internal events is a poor fit when you must establish the actual factor outcome, because an application should not promote its interpretation into proof of what happened inside the identity boundary.
Build an evidence bundle that answers one control question
During evidence collection, produce a bounded export rather than handing over unrestricted log access. A recovery sample can include the application event, the referenced authentication event, the policy version, the approval record when a human intervened, and the eventual recovery outcome. Include a manifest with the export time, query boundaries, record counts, and integrity digests so reviewers can see what the bundle contains.
Access to that export should itself be auditable. Separate the ability to operate recovery from the ability to alter audit storage, and keep evidence-generation permissions narrow. NIST's log-management guidance describes log management as a lifecycle covering generation, transmission, storage, access, and disposal; the export step is part of that lifecycle, not an afterthought.
Keep the bundle narrow.
Test the negative cases. Can a support agent approve their own recovery request? Does a missing device-risk result fail closed into a defined path? Can an operator change a policy label without changing its version? Does a delayed provider event attach to the original correlation chain? These tests are more persuasive than a screenshot of a busy event-history page because they exercise the control you say exists.
For a small team, the lean design is two records joined by identifiers, not a new analytics platform: keep provider-originated authentication evidence at its source, emit application-owned decisions through a transactional outbox, and generate scoped bundles on demand. Measure missing joins, publication lag, export completeness, and redaction failures before copying this architecture. Those numbers reveal whether the evidence chain works under pressure.
Sources
- OWASP Authentication Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- OWASP Logging Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html
- NIST SP 800-92, Guide to Computer Security Log Management: https://csrc.nist.gov/pubs/sp/800/92/final
- AICPA, Trust Services Criteria: https://www.aicpa-cima.com/resources/download/2017-trust-services-criteria-with-revised-points-of-focus-2022
Top comments (0)