Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

Recording identity activity

When something looks wrong

On the morning of Day 2, an alert reaches Quinn on Cedar Inc.'s security team. The day before, a new authenticator app was added to the account of Riley, who works in finance, from a network Riley has never used. Quinn's first questions are easy to ask: who added it, from where, during which sign-in, and what else happened in that sign-in?

Answering them is another matter. Everything Quinn can learn now was written down yesterday, by systems that had no idea anything was wrong. If those records name the account, the session and the network, Quinn can rebuild the morning minute by minute. If one of them says only "user updated profile", the investigation stalls at the first question.

Each of those entries is an event record: a description of one thing that happened, saying when it happened, who performed it, what it affected and how it ended.

Events worth keeping

Start with the events that change who can get into an account or what they can do there. Those are the moments an attacker needs, and the moments an investigator looks for first.

  • Sign-ins, successful and failed, with the method used and the reason for each failure.
  • Challenges: a code sent, an approval prompt approved, denied or left to expire.
  • Method and recovery changes: an authenticator, passkey, phone number or recovery address added or removed, and every password reset.
  • Sessions and tokens: sessions started and ended, refresh tokens issued, reused or revoked.
  • Consents: an application granted access to an account, with the permissions it received.
  • Roles and configuration: role assignments, policy changes, trusted identity providers, and credentials added to applications.
  • Support actions: what the help desk did on someone's behalf, including requests it refused.
  • Reads of sensitive records, including searches of the audit history itself.

Failures deserve the same care as successes. A single failed sign-in means nothing, but the wave of guesses that hit Cedar on Day 0 was visible only because every rejected attempt was recorded with its account, network and reason, as Guessing, spraying, and stuffing describes. A refused request shows intent as well, as Cedar's record of a refused call to its help desk later showed.

Not everything is worth keeping. A record for every image a page loads would bury the events that matter and cost money to store. A useful test is whether the event changes access, reveals an attempt to change it, or would help someone reconstruct what happened to an account.

Who did what to whom

Here is a fictional record of the change that worried Quinn, shown decoded so the fields are readable:

{
  "time": "2026-03-10T09:20:41Z",
  "event": "authenticator.added",
  "message": "Authenticator app added to an account",
  "outcome": "success",
  "actor": { "type": "user", "id": "usr_4182" },
  "subject": { "type": "user", "id": "usr_4182" },
  "session": {
    "id": "s-7a91",
    "started": "2026-03-10T09:14:06Z",
    "recent_sign_in_check": "passed"
  },
  "request_id": "req_7f3a2c90",
  "source": {
    "ip": "203.0.113.57",
    "network": "hosting provider",
    "device": "desktop browser, first seen today"
  },
  "details": { "method": "authenticator_app", "authenticator_id": "auth_31c" }
}
What each field in the record tells an investigator
FieldWhat it tells Quinn
timeWhen the server handled the request, by a synchronized clock in one time zone, so records from different systems line up.
event and messageWhat happened: a fixed event name for searching, and a short summary a person can read.
outcomeHow the request ended.
actor and subjectWho performed the action, and which account it affected.
sessionWhich sign-in the request belonged to, when that sign-in began, and whether the recent sign-in check passed.
request_idThe value that links this record to everything else written for the same request.
sourceThe network address, the kind of network, and a summary of the device.
detailsIdentifiers for what changed, such as the new authenticator's ID. Never its secret.

The actor is whoever performed the action, as the system authenticated them. The subject is the account the action affected. When Riley adds an authenticator to their own account, both are Riley's account. When Rowan, on Cedar's help desk, resets someone else's MFA, the actor is Rowan and the subject is the other person. A record that captured only one of them would hide either who acted or who was affected.

Notice what the actor in the example actually is: Riley's account, not Riley. The system knows which account a session belongs to, not who is at the keyboard. Quinn tells the attacker from Riley through the fields around the actor: a session that started six minutes earlier, from a hosting provider's network, on a device Riley has never used.

A failed sign-in has no authenticated actor at all. Someone typed [email protected] into a form, which is a claim that anyone could make. Recording that address as the actor would make thousands of guesses look like Riley's own mistakes. The record should show no actor, and name the account the typed address matched as the subject of the attempt, or note that it matched none.

Two fields connect records to one another. The session ID links everything done within one sign-in. A correlation ID, often called a request ID, is assigned when a request arrives and carried into every record written while handling it, including those of other services the request calls.

Finally, the outcome must be the truth. A request the system refused is recorded as rejected, with its reason, never as a completed change. A request that broke partway through is a failure. When the result is genuinely unknown, for example because a call to another service timed out, the record says so rather than guessing.

What never belongs in a record

Records travel. They are searched, exported, read by support staff and analysts, and kept for months. A record containing a session cookie is a credential store with weaker protection than the session store it came from, and a record containing a password undoes the care that went into storing passwords safely.

So records never contain passwords, one-time codes, reset or verification links, session cookies, tokens, API keys, client secrets, private keys, or the secret behind an authenticator app. When an investigator needs to know which credential was involved, the record carries an identifier instead, such as the authenticator's ID or a token's unique ID. Even the identifier typed into a sign-in form needs care: people sometimes type their password into the username box, so a record that keeps every typed value will eventually keep a password. Personal data calls for the same restraint. A network address helps an investigation. A full copy of a submitted form does not.

Most secrets reach records by accident. A debug line prints a whole request, cookies included. An error handler copies an exception message that quotes the request body. A token sent in a URL lands in an access log, one of the leaks in How sessions and tokens are stolen. The reliable defense is to build each record from chosen fields only, and to write its message from fixed text, never from request data or exception text.

Each of the records below has one serious problem. See whether you can find it.

Fix this record

Simulation. These records are made up, and each one has a serious problem. Choose the problem for each record; your answers are checked on this page and sent nowhere.

ITEM 1 OF 4

What is wrong with this record of a failed sign-in?
2026-03-09T07:12:44Z WARN sign-in rejected reason=wrong_password request_id=req_51d0e2
  body={"username":"[email protected]","password":"Spring2026!"}

Enable JavaScript to check an answer here, or open the explanation below.

Show the answer and why

It copies the request body, so the submitted password is now in the logs. Anyone who can read the logs can now read the password, and logs are copied and kept far more widely than password hashes. Records should be built from chosen fields, never from the request body.

ITEM 2 OF 4

This record describes a rejected sign-in. What is wrong with it?
{
  "time": "2026-03-10T10:48:13Z",
  "event": "sign_in.rejected",
  "message": "Sign-in rejected: wrong password",
  "outcome": "rejected",
  "actor": "[email protected]",
  "request_id": "req_88b21f",
  "source": { "ip": "192.0.2.77" }
}

Enable JavaScript to check an answer here, or open the explanation below.

Show the answer and why

The actor is only a typed address. Nobody was authenticated, so it is a claim. Anyone can type Rowan's address. The record should show no actor, and name the account the address matched as the subject of the attempt.

ITEM 3 OF 4

An administrator's change produced this record. What is wrong with it?
{
  "time": "2026-03-10T13:05:56Z",
  "event": "admin.action",
  "message": "Operation completed",
  "outcome": "success",
  "actor": { "type": "user", "id": "usr_2290" },
  "request_id": "req_4c19aa"
}

Enable JavaScript to check an answer here, or open the explanation below.

Show the answer and why

It does not say what was done or to whom: no specific event, subject or detail. Was a role assigned, a policy changed or a method reset? The record needs the specific action, the subject and identifiers for what changed, for example a "role.assigned" event naming the role and the account that received it.

ITEM 4 OF 4

The service refused this attempt to change a recovery email, because the confirmation code had expired. What is wrong with the record?
{
  "time": "2026-03-10T11:02:09Z",
  "event": "recovery_email.changed",
  "message": "Recovery email changed",
  "outcome": "success",
  "actor": { "type": "user", "id": "usr_3317" },
  "subject": { "type": "user", "id": "usr_3317" },
  "request_id": "req_2e7d41"
}

Enable JavaScript to check an answer here, or open the explanation below.

Show the answer and why

It records a completed change that never happened, instead of a rejection. Anyone reading it would believe the recovery address changed. The record should describe the attempt, with the outcome rejected and the reason, for example an expired code.

Audit history and operational logs

Identity systems usually keep two kinds of records, for different readers. Audit history answers who did what to whom, and how it ended. It is written for administrators, auditors and investigators, kept for a long time, and must be complete for the actions it covers.

Operational logs describe how the software handled each request: which checks ran, which services were called, how long they took and what went wrong. Engineers use them to diagnose problems. There are far more of them, and they are kept for a shorter time.

The correlation ID joins the two. Suppose an audit record says a password reset failed. It tells you who asked and which account was affected. The log entries written under the same request ID tell you why: the service that stores password hashes did not answer in time. Neither record explains the event on its own.

The two also need separate access. Someone reviewing an organization's audit history does not automatically need the service's diagnostic logs, which describe the system's internals and may mention other customers. A service that shares operational information with the organizations it hosts shares a filtered view, limited to each one's own requests.

Keeping records trustworthy

An attacker who gains administrative access would like the record of their actions to disappear. Audit history should therefore be append-only: new records can be added, but nobody can edit or delete an individual record, administrators included. Keeping the history outside the system it describes, and making tampering detectable, means a compromise of the main system cannot quietly rewrite its own past.

Reading needs control as well, because records reveal where people work and which accounts were targeted. Access is limited to the people who need it, and in a service that hosts many organizations, an administrator of one never sees another's records. Every search of the audit history is itself recorded, so a curious or compromised reviewer leaves a trail.

Retention sets how long records are kept. Attackers can stay inside an account for weeks before anyone notices, so records that vanish after a few days end investigations early. Records kept forever become a privacy liability and a bigger prize. A documented retention period, enforced by routine cleanup, balances the two.

Two quieter properties matter just as much. Records from different systems only form a reliable timeline if their clocks agree. And when a source stops recording, perhaps during an outage, that gap should be visible, so an investigator does not mistake missing records for nothing happening.

Records like these are what Detecting identity attacks searches for patterns.

Try it in the Lab

PUT IT INTO PRACTICE

Check your understanding

Try these questions before moving on. If an answer isn't right, use the feedback and try again.

0 of 2 answered correctly

Enable JavaScript to answer these questions and save progress in this browser.

QUESTION 1 OF 2Avery asks Cedar's help desk for an MFA reset, and Rowan performs it after checking Avery in person. What should the record show as the actor and the subject?

QUESTION 2 OF 2An audit record says a password reset failed. The service's operational logs hold hundreds of entries from that same second. Which field lets you find the entries that explain this failure?

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

Learn identity