Skip to content

Build1 publisher3 min readPublished

A recovery-decision event can be written before the provider's own event id exists

An identity provider's history proves a factor challenge succeeded, so SOC 2 evidence for what the application decided next has to come from its own append-only event, with the policy version recorded at the moment of the decision.

The Engineer · Build desk

Photograph accompanying A recovery-decision event can be written before the provider's own event id exists
Photo: okta.com

What happened

  • A dev.to post argues an identity provider's event history cannot stand as the full record of a risky login, because the provider cannot say why an internal service opened an account-recovery path.
  • Its recommendation is to keep the provider history as source evidence, write your own append-only audit events for application decisions, and join the two with stable correlation identifiers.
  • For SOC 2, the advice is to export the smallest time-bounded bundle proving who acted, what the system decided and what happened next, not a bulk copy of every provider payload into a second database.
  • The worked example is a single TypeScript event type for one recovery decision, which the post explicitly declines to present as a universal security schema.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Naming the control assertion first decides what gets instrumented; teams that begin from an event schema end up paying to store and test fields no evidence reviewer will ask for.
  • cost Bulk-copying provider payloads buys retention and test-fixture surface without buying authority, since the copy is no more authoritative than the provider's own record.
  • exposure Whatever the audit stream carries reaches exports, dashboards and test fixtures, so the choice between a raw fingerprint and a keyed pseudonymous reference sets how far sensitive device data travels inside the company.

Follow one recovery request through both systems and count the moments. The identity service observes credentials, factors, sessions and some account changes [4], and in the example it shows a successful factor challenge [6]. The support application routes the account into assisted recovery anyway, because the device is new and the requested action would expose private case data [6]. An agent approves the exception later. Those records can land at different times under different access controls, and an evidence reviewer is still expected to follow one causal chain without guessing from timestamps [7].

The published type puts that ordering into the schema. RecoveryDecisionEvent has twelve top-level fields, eleven of them required; the only one marked optional with a `?` is providerEventRef, the pointer back to the identity provider's event [11]. correlationId is required, and so is the policy object with its id and version [9][10]. An application can therefore record its decision before it knows the provider's event id, and it cannot record one at all without naming the policy version that ran.

I would copy the validator first. It throws when decision is "allow_self_service" and challengeOutcome is anything other than "passed" [12]. The post's example control assertion is that high-risk login attempts cannot enter account recovery without the required challenge or an authorized human approval [13]. Enforced at write time, that assertion means a self-service allow with a failed challenge never reaches the store.

"I would also keep raw device fingerprints out of the general audit stream," the author wrote on dev.to [16]. The risk engine keeps the underlying inputs under its own access and retention rules, which the post concedes is a real trade-off: investigators cannot reconstruct every risk input from the audit store alone, so the protected risk system has to support a separately authorized lookup for the cases that need it [18]. Somebody has to build that lookup.

None of this transfers unless your application makes decisions the provider never sees. The post's list of what the support application owns is device-risk scoring, recovery eligibility, case state and agent permissions [5]. A service that reads a boolean from the provider and branches on nothing else has no second decision to record, and its audit store would hold copies of someone else's records at its own retention cost.

The claim that a compact contract is cheaper to retain and easier to test than arbitrary JSON copied from every dependency is the author's, and the post does not give retention figures or name an auditor who accepted a joined bundle [21][24]. The one external anchor is OWASP, which recommends logging and reviewing authentication failures and password failures, and treats recovery as an alternate authentication mechanism that should not be weaker than normal authentication [19]. The post reads that as support for collecting authentication signals, and not as grounds for treating a third-party event feed as a record of private application logic [20].

What to watch

  • Whether the single recovery-decision type survives a second flow, such as agent permission changes, or grows into the universal schema the post disclaims.
  • Whether a SOC 2 auditor accepts a time-bounded joined bundle in place of a full provider export.
  • Whether the separately authorized lookup into the risk engine gets built before the first investigation needs it.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories