DEV Community

GarrisonSterling2693
GarrisonSterling2693

Posted on

High-Risk Login Signals, Event Reporting, and Step-Up Verification for Account Recovery

Healthtech account recovery is a high-risk login controls problem with an adversary in the loop. Device fingerprints and event reporting can help, but the control that blocks a stolen password can also block a patient who has changed phones, so the decision cannot be “challenge everyone” or “trust every familiar device.”

Short answer: use device signals and event reporting to assign risk, then require step-up verification only when the recovery action crosses a defined risk threshold; keep a slower, auditable human path for people who cannot satisfy the challenge.

The recovery incident that changed my runbook

In a design review for email-and-password sign-in, I model the ugly case first: an attacker has the password, the real user has lost the enrolled phone, and both can receive mail. A recovery link alone proves control of an inbox, not control of the account holder. The dangerous operation is changing the destination for future recovery, not merely opening a session. I write the sequence on a whiteboard as four timestamps: password accepted, link issued, link consumed, destination changed; if the last two events arrive from a new device within five minutes, the policy must explain why it allowed or delayed the change, and the explanation must survive a later support investigation.

No magic.

That distinction gives the service a useful invariant: every recovery decision must record the evidence available at the time, the policy version, and the resulting action. A rejected attempt is still an event. Without that record, a support engineer sees only “reset failed” and cannot tell whether the account was protected or the detector was blind.

I keep the first policy deliberately boring. A new device, a sudden country change, five failed passwords in ten minutes, or a recovery address that has never appeared before raises risk. None of those facts proves fraud. They are inputs to a decision, and the decision needs an expiry so that yesterday's travel does not poison next month's login.

The operational numbers matter. For a 99.9% monthly login SLO, the risk service cannot become a single synchronous dependency on the critical path without a defined degraded mode. A 300 ms budget for risk evaluation is reasonable only if the timeout behavior is specified: preserve a known-low-risk session, or step up, depending on the operation. “Fail open” and “fail closed” are not security philosophies; they are per-action choices.

What should device fingerprints and event reporting prove before step-up verification?

Device fingerprints should provide continuity, not identity. A browser cookie, platform attestation, IP reputation, and coarse network geography can say that a request resembles prior activity. They should not be treated as a permanent identifier, because browsers reset storage, mobile networks move, and shared clinical workstations are real. Store a short-lived, rotating device reference and the features used to derive its score; avoid retaining raw identifiers longer than the threat model requires.

Event reporting is the other half of the control. Emit one structured event for password failure, successful sign-in, recovery-link request, recovery-link consumption, factor enrollment, factor removal, and recovery destination change. Include a correlation ID, account pseudonym, timestamp, policy version, risk score range, and outcome. Do not put passwords, recovery tokens, or full addresses in logs. OWASP's Authentication Cheat Sheet also recommends generic authentication errors so that the response does not reveal whether an account exists.

The event stream should be useful at two speeds. The inline policy needs a small, bounded view such as “new device in the last 24 hours.” Security operations needs the complete sequence for investigation and replay. I prefer an append-only event sink with a queue between the login service and the analytics consumer; the login request can proceed under its explicit timeout while delivery is retried and monitored separately.

Here is the shape of a recovery decision in Go. The interface is intentionally generic so the policy can be tested without a vendor SDK.

package recovery

import "time"

type Signal struct {
    NewDevice       bool
    PasswordFails10 int
    NewRecoveryAddr bool
    GeoShift        bool
}

type Decision struct {
    Action string // allow, step_up, or manual_review
    Reason string
}

func Decide(s Signal, now time.Time) Decision {
    // Changing the recovery destination is the highest-impact operation.
    if s.NewRecoveryAddr && (s.NewDevice || s.GeoShift) {
        return Decision{Action: "manual_review", Reason: "destination_change_with_new_context"}
    }
    if s.PasswordFails10 >= 5 || s.NewDevice || s.GeoShift {
        return Decision{Action: "step_up", Reason: "elevated_context"}
    }
    return Decision{Action: "allow", Reason: "baseline_context"}
}
Enter fullscreen mode Exit fullscreen mode

The now argument is present because production policies need time windows, even though this compact example leaves the window lookup to the caller. In the real service, the decision and the evidence snapshot are written atomically to the audit stream. A retry with the same request ID returns the original decision; otherwise a flaky client can create two valid recovery links and two contradictory audit records.

How do you make high-risk login controls observable without leaking account data?

Start with counters that describe behavior, not people: step-up rate by policy version, recovery-link consumption latency, manual-review queue age, and false-positive reports from support. Set an alert on a change in rate, not on one suspicious user. A spike from 2% to 18% step-up challenges after a policy rollout is an SLO and usability regression even if no account is compromised.

Keep authorization and authentication telemetry separate. Authentication events answer “how was access established?” Authorization events answer “what could that session do?” A low-risk sign-in should not automatically authorize changing a recovery address. Re-evaluate risk at that boundary and bind the elevated claim to a short session and a specific operation.

For incident response, retain enough detail to reconstruct the sequence while applying access controls to the logs themselves. Hashing an account identifier with a rotating key supports grouping without making the log a second directory. Redact at the emitter, not in a downstream dashboard where one missed parser can expose a token.

Testing needs adversarial cases and ordinary patients. I run table-driven tests for clock skew, repeated request IDs, deleted cookies, shared devices, and a recovery link opened from a different network. I also test the support path: a reviewer must see the evidence and policy explanation without seeing the password or token. A 401 from the password endpoint and a 429 from throttling should be distinguishable to metrics while the user-facing message remains deliberately generic.

Buy, build, or combine the controls

The choice is mostly about operational ownership, not a feature checklist.

Approach Strength Cost or limit Fits when
Managed identity service Factor enrollment, token handling, and baseline telemetry arrive together Policy detail and event retention may be constrained; an outage is an external dependency A small team needs a short path to a documented SLO
Self-hosted identity stack Full control of data residency, recovery workflow, and release timing The team owns patching, key rotation, abuse response, and 24/7 capacity The organization can staff security operations and exercise recovery drills
Hybrid policy layer Keeps credentials in a standard identity component while risk scoring and event policy stay local Two contracts and two failure modes must be traced end to end Recovery rules vary by product or clinical risk tier

I would not select a design that cannot export an auditable event or define its timeout behavior. The cheapest integration is expensive when a reviewer cannot explain why a patient was locked out.

The catch is that device signals are not suitable when the same workstation legitimately serves many users, or when privacy rules prohibit the chosen telemetry. In those settings, reduce fingerprinting, rely on phishing-resistant factors and verified support procedures, and accept a slower recovery path. Stick with a simpler password-plus-email flow when the account has no sensitive data and recovery cannot change a high-impact destination; adding step-up there creates friction without reducing a meaningful loss.

A rollout rule for platform teams

Ship the event schema before shipping the score. Run the detector in report-only mode for at least one representative traffic cycle, compare challenge rates by device and geography, and have support review the proposed manual queue. Then enable step-up for one operation, usually recovery-destination change, with a rollback flag and a named owner.

Review the policy monthly against the SLO, support contacts, and confirmed abuse. Your mileage may vary: a pediatric portal, a clinician console, and a consumer wellness account have different harm from delay, so they should not share one threshold just because their login forms look alike. I am not sure any static fingerprint can stay useful for years; rotating signals and explicit expiry are a better assumption.

Security is the outcome of the whole recovery path. Device evidence, event reporting, and step-up verification are valuable only when their limits, latency, and human fallback are visible to the team operating them.

Sources

Top comments (0)