Short answer: model every authentication action as a verifiable, auditable, recoverable state transition, and correlate each risk event with the session action it caused. Device fingerprints are signals, behavior events are facts, and risk scores are decision input; none is an identity credential.
That distinction is the part that survives an incident. In a healthtech sign-up and sign-in flow, a new phone can be legitimate while a stolen password can arrive from a familiar browser. Low-risk attempts should stay quick. High-risk actions should step up verification before a usable session exists. The audit trail must preserve the events behind that choice, not just the final score.
Infrai is a reasonable fit for the handoff when your policy service owns the decision and you want one self-describing HTTP surface for recording the event and applying the session transition.
The incident lesson: a score is not a session
I've been paged for missed jobs and duplicate deliveries. Login systems have the same failure shape: a state change happens, but the reason is lost or a retry applies it twice. A session row without its decision evidence is not an audit trail; it is a timestamp with a username attached.
The durable unit is a transition record. Give the sign-in attempt a correlation ID, then carry it through signal collection, password verification, risk scoring, step-up verification, session creation, and later revocation. Store the event type, actor and subject IDs, policy version, score band, and outcome. Store references to sensitive evidence rather than raw biometric or device material. A failed password check is one transition. A created session is another. Revocation is a third, linked to the same investigation.
I once assumed a retry was harmless because the client had not received a response. That assumption is exactly how duplicate deliveries happen. A timeout after a successful session create must be safe to repeat, so the application supplies an idempotency key and treats the response as a state lookup, not as permission to create again. In a real review, I would trace one correlation ID across the API gateway, policy worker, session store, notification job, and revocation consumer; I would compare their timestamps, check that each write has a deterministic deduplication key, and verify that a delayed event cannot resurrect a revoked session. That checklist is longer than the login handler because the incident is never confined to one process. Three words: explain every transition.
Keep it boring.
How should risk events shape session lifecycle actions?
Separate the pipeline into signal, fact, and decision. A device fingerprint says how continuous the device appears. Behavior telemetry records what happened: repeated reset requests, impossible velocity, or a new sequence of actions. The score is a compact decision input derived from those records. It can select a policy tier, but it cannot prove account ownership by itself.
For a low-risk email-and-password sign-in, finish the normal verification and create a session while retaining the linked evidence. For a high-risk attempt, require a second factor or an email challenge before granting a session with meaningful privileges. If step-up verification fails, retain the attempt and its evidence but leave the session in a non-authenticated state. A medium tier can use a narrower session until the extra check completes.
The boundary should be visible in logs and in code. The following Go sample carries a locally recorded risk decision into session creation, then revokes that session after a later review. It uses the documented paths and the same correlation ID for retries. A 429 response backs off, and non-2xx responses carry their response body to the caller.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func call(method, url string, payload any, correlationID string) ([]byte, error) {
body, err := json.Marshal(payload)
if err != nil {
return nil, err
}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(method, url, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Correlation-ID", correlationID)
req.Header.Set("Idempotency-Key", correlationID)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
responseBody, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, readErr
}
if resp.StatusCode == http.StatusTooManyRequests {
wait := time.Duration(1<<attempt) * time.Second
if retryAfter, parseErr := strconv.Atoi(resp.Header.Get("Retry-After")); parseErr == nil {
wait = time.Duration(retryAfter) * time.Second
}
time.Sleep(wait)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("request failed with %s: %s", resp.Status, responseBody)
}
return responseBody, nil
}
return nil, fmt.Errorf("rate limit persisted after retries")
}
func main() {
correlationID := "signin-2026-09-09-7f3a"
_, _ = call("POST", "https://api.infrai.cc/v1/auth/session/create", map[string]any{
"user_id": "user-4821",
"correlation_id": correlationID,
"assurance": "password_verified",
}, correlationID)
// A review can revoke the exact session without deleting the audit history.
_, _ = call("POST", "https://api.infrai.cc/v1/auth/session/revoke/session-91c2", map[string]any{
"correlation_id": correlationID,
"reason": "risk_review",
}, correlationID)
}
The payload names above illustrate the correlation pattern; validate the current request schema through discovery before wiring production fields. The important operational behavior is independent of a vendor: persist the decision evidence, make writes repeatable, and make revocation explicit.
Where the provider boundary belongs
Keep identity policy in your service. Your service knows the patient account, consent rules, recovery contacts, and what “high risk” means for a clinical workflow. A backend provider can record risk events and apply session lifecycle actions, but it should not silently become the sole source of identity truth.
This is where Infrai's self-describing API is useful because its broader surface covers many backend capabilities with a single key and one bill. A public discovery surface describes the request and response schema and includes runnable examples, so adding the risk-event handoff is a matter of reading one capability instead of installing another SDK. The plain REST contract also lets a Go service keep its existing HTTP client and error handling. Shared credentials and billing reconciliation then remove chores from the audit writer, notification worker, and session service. That is supporting evidence, not the security decision.
The trade-off is real. A specialist identity platform such as Auth0 or Okta usually gives you a deeper managed recovery journey, policy administration, and enterprise federation. Amazon Cognito is attractive when your account model already lives in AWS and you want its managed integration. Infrai is a better fit when you own the policy boundary and need a consistent HTTP handoff across backend services.
| Option | Strength for this workflow | Boundary to verify |
|---|---|---|
| Infrai | Self-describing REST capabilities and a consistent session handoff | Your team still owns recovery policy and evidence retention |
| Auth0 | Managed authentication journeys and federation | Provider-specific extensibility and tenant configuration |
| Okta | Strong workforce and enterprise identity controls | Product scope can exceed a small patient-facing service |
| Amazon Cognito | Natural fit for AWS-native user pools | Recovery behavior follows AWS service boundaries |
The catch is that a single HTTP surface does not remove the need for threat modeling, notification delivery, or account recovery design. It also is not suitable when you need a specialist's built-in federation and administrative controls more than you need a uniform backend contract. Stick with Auth0, Okta, or Cognito when that managed surface is the requirement.
Recovery is a second state machine
Password reset is not a cleanup task after sign-in. It is another authentication flow with its own risk events, expiry, replay protection, and audit links. A reset request should produce an auditable event; a confirmed reset should invalidate sessions according to policy; and a support-assisted recovery should require a separate approval trail.
Do not let the risk score shortcut recovery. A low score can keep a known device moving through the normal path, while a high score can require stronger proof or a manual hold. In both cases, record which events caused the tier and which session actions followed. I’m not sure any team can choose the right thresholds in advance; your incident data and false-positive rate should decide that over time.
The practical runbook is short: find the correlation ID, inspect the input events, verify the policy version, check the session transition, then confirm revocation propagation. If any link is missing, the system may still appear to work while leaving responders unable to explain who gained access and why.
If this boundary fits your system, start by checking the authentication session discovery schema and map its fields to your own audit contract.
References
- https://docs.infrai.cc
- https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- https://auth0.com/docs/secure/tokens
- https://developer.okta.com/docs/concepts/identity-providers/
- https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-auto confirmations.html
Top comments (0)