Skip to content

auth: password-rotation credential invalidation is best-effort, not atomic #398

Closed

Description

@cristim

What

ChangePassword, ConfirmPasswordReset and UpdateUserProfile (internal/auth/service_password.go, service_user.go) persist the new password hash, then invalidate sessions and API keys (invalidateUserCredentialsBestEffort) as a separate, best-effort step: failures are logged, not returned, and never roll back the password change. This has been the behavior for session invalidation since before #102/#392 and now also applies to the API-key revocation added there.

Why this matters

If DeleteUserSessions or RevokeAllUserAPIKeys fails (partially or entirely) after a successful UpdateUser, the new password takes effect but old sessions and/or API keys can remain valid until they naturally expire. For the threat model in #102 (an attacker who briefly held a victim's session and minted an API key), this narrows but does not fully close the window: a transient store failure at exactly the wrong moment leaves the attacker's key alive even though the victim successfully changed their password.

Why it wasn't fixed as part of #392

  • No transaction-capable store interface exists (internal/auth/interfaces.go, store_postgres.go) to atomically commit the password update and the credential-invalidation writes together; adding one is a genuine store-layer change, not a scoped fix.
  • Making invalidation fail-closed (block/rollback the password change on invalidation failure) without fixing session invalidation the same way would be an inconsistent half-measure, and blocking a victim's urgent password rotation on a transient API-key-store failure is arguably worse for this exact attack scenario than the current narrow residual window.

Suggested direction

  • Add a WithTx-style capability to StoreInterface (or an equivalent single write that updates the user row and marks sessions/keys revoked together), OR
  • Introduce a persisted "credential version" / "password generation" counter on User that ValidateSession and ValidateUserAPIKey check against, so revocation becomes implicit in the password-hash write itself rather than a separate best-effort pass over sessions/keys.

Raised by CodeRabbit on #392 (#392 (comment), #discussion_r4119975933); the reordering fix in that PR ensures a failed password persist no longer revokes valid credentials, but does not address invalidation itself failing after persistence succeeds.

Activity

  1. IAmJSD commented on Oct 20, 2026

    @IAmJSD
    Collaborator

    Claiming: working on this

  2. self-assigned this
    on Oct 20, 2026
  3. added a commit that references this issue on Oct 25, 2026
    3504176
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions