Why I Added a Go Step-Up Risk Score Gate for Password and Email Changes — 3 Lessons
The page that wakes an on-call engineer is rarely the password form itself. It is usually a tenant support ticket followed by a suspicious email-change event: a property manager is locked out, a new address now owns the account, and the audit trail cannot explain why a step-up verification gate let a risk score pass before the password or email change. Short answer: put a risk score in front of…
The author adds a "Go step-up risk score gate" for password and email changes to prevent unauthorized access and improve security. The reasoning behind this includes the fact that password forms are rarely the source of on-call alerts; instead, suspicious email-change events often trigger these alerts, such as tenants being locked out or accounts being transferred to new addresses without proper verification.
To address this issue, the author suggests implementing a risk score gate before password and email changes, using it to select a step-up verification method, and treating the score as a decision input rather than proof of identity. This approach involves creating a small, auditable state machine where a normal sign-in creates a session, but a sensitive mutation requires a fresh risk decision, a short-lived verification challenge, and a recorded transition.
The author mentions that the effectiveness of this approach was proven by a case study involving a leasing portal used by 4,000 residents and staff. In this scenario, a high-risk score alert was generated at 03:17 UTC due to a suspicious email-change event. The alert included details such as the user ID, the score (86), the fact that a new device was used, and 11 failed password attempts in 90 seconds.
The lack of a linked step-up event allowed the on-call engineer to take appropriate action based on this information. The author emphasizes the importance of instrumentation before policy implementation, as well as the need for a well-designed event schema that can survive retries, clock skew, privacy review, and support requests for decision explanations without compromising device fingerprinting.
The author also discusses the challenges of setting a universal threshold for risk scores, as property portfolios differ, and suggests that the threshold should be determined based on measured policy rather than a hard-coded constant in the code. The optimal risk score gate should sit immediately before the state transition, not only at login.
The score should be set based on various signals, facts, and decision inputs, including device fingerprinting, behavior events, and the decision itself. By separating these roles, the author argues that auditability becomes possible. The author also acknowledges recommendations from OWASP regarding reauthentication and risk-based controls for sensitive account changes, while warning about the need to treat recovery and verification paths with the same care as the primary login path.
The author provides a code example demonstrating how to implement this gate in a Go service, including the creation of a RiskRequest and RiskDecision struct, and a RiskClient interface to score the risk request. The AllowCredentialChange function takes a context, a RiskClient, a userID, and an action, and returns an error if the risk score is above a certain threshold, indicating the need for a step-up verification process.
The author concludes by emphasizing the importance of keeping the API handler simple, validating the session, asking for a risk decision, checking the step-up proof, performing one idempotent transition, and emitting one audit event. The author suggests that a queue or a second service can help with notifications, but it must not become an untracked side door around the gate.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.