Skip to content

fix(auth): persist login bookkeeping atomically - #458

Merged
cristim merged 1 commit into
mainfrom
fix/atomic-login-bookkeeping
Sep 30, 2026
Merged

cristim merged 1 commit into
mainfrom
fix/atomic-login-bookkeeping

Conversation

@cristim

@cristim cristim commented Sep 30, 2026

Copy link
Copy Markdown
Member

Failed and successful login bookkeeping previously wrote a stale copy of the entire user row. A concurrent login could restore an old password, MFA configuration, group membership or activation state, and parallel failures could lose increments.

Replace those writes with two targeted PostgreSQL updates. Failure accounting increments the stored counter atomically; successful login resets the counter and lock and records its timestamp. Existing lockout duration, error responses and best-effort success bookkeeping remain unchanged. Recovery-code consumption remains separately tracked in #228.

Validation on Go 1.26.6:

  • Full go test -race ./..., build, vet and golangci-lint 2.10.1 passed. Normal commit hooks passed.
  • Real local PostgreSQL 17 tests cover concurrent security changes on password and MFA paths, five parallel failures, every sequential lockout count, expired locks, mixed success/failure ordering and session behavior. Accounts are synthetic; no production or cloud mutations were performed.
  • Independent Astra review, the user-authorized substitute, completed two clean implementation passes. Removing only the production service fix made all four security-preservation tests fail and persisted one failure instead of five. The fixed tests passed.

Independent final verdict: approved with no actionable findings at f1685d14d67faec8c98c84d6e72d01aab276cb29. A fresh PostgreSQL run of the committed login scenarios plus service/store error and enumeration tests passed with race detection in 22.680s. Removing only the production service fix at that same revision reproduced all four security rollback failures and the lost counter increment (expected-red exit 1, 1.746s).

The larger test diff moves lockout assertions from mutable mock-user pointers to database readback. Added comments: 2 of 531 nonblank added lines (0.38%).

Closes #229.

Update only login counters, lockout and timestamps so an in-flight login
cannot restore stale account security fields. Increment failed attempts
in PostgreSQL to preserve concurrent failures.

Verify service routing, concurrent security updates, mixed login ordering,
lockout expiry and persistence errors against local PostgreSQL.
@cristim cristim added severity/medium Moderate harm urgency/this-sprint Within the current sprint triaged Item has been triaged priority/p1 Next up; this sprint impact/all-users Affects every user effort/m Days type/bug Defect labels Sep 30, 2026
@coderabbitai

coderabbitai Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 13 billable files and costs up to $3.25.

  • Ask an admin to make reviews automatic

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Or wait 30 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 70 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: LeanerCloud/cloud-commitments-platform/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 139a9e9a-2097-4e9e-94d9-dccc71b41352

📥 Commits

Reviewing files that changed from the base of the PR and between ac96148 and f1685d1.

📒 Files selected for processing (13)
  • internal/auth/interfaces.go
  • internal/auth/service.go
  • internal/auth/service_lockout_test.go
  • internal/auth/service_login_db_test.go
  • internal/auth/service_mfa_test.go
  • internal/auth/service_test.go
  • internal/auth/service_user.go
  • internal/auth/store_postgres_login.go
  • internal/auth/store_postgres_login_test.go
  • internal/auth/test_helpers.go
  • internal/mocks/stores.go
  • internal/server/adapter_test.go
  • internal/server/health_test.go

Comment @coderabbitai help to get the list of available commands.

@cristim
cristim merged commit 86073c7 into main Sep 30, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/m Days impact/all-users Affects every user priority/p1 Next up; this sprint severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(auth): failed-login bookkeeping does an unauthenticated whole-row read-modify-write

1 participant