fix(users): revoke interactive credentials when an account is re-enabled - #878
Merged
remyluslosius merged 2 commits intoSep 25, 2026
Merged
Conversation
Enable cleared disabled_at and revoked nothing, so a credential that existed while the account was disabled authenticated again the moment an administrator re-enabled it. The integrated closure run on a22c8b2 measured it: a session cookie, an access token and a body refresh token written during the disabled window all answered 200 after Enable. This implements I10, approved 2026-09-23. Enable now takes the per-user lock, reads the account state under it, and on a real disabled-to-enabled transition clears disabled_at and performs the user-wide interactive revocation in the same transaction. Enable on an account that is not disabled changes no row and revokes nothing. Service-account tokens are untouched: enable neither revokes one nor restores a revoked one. The AC-70 fixture writes its stray credentials directly, bypassing every handler. It proves what Enable does with state an older release or a leaked issuance path could leave. It does not show that a current handler issues credentials while an account is disabled. Specs: system-auth-identity 1.8.0 (C-34, C-36 amended; AC-70 to AC-72), api-users 1.4.0 (C-07 amended; AC-17 amended so it can fail without this change). Refs: bugs/OW-069, bugs/doing/OW-073, bugs/doing/OW-062 section 14.7
The enable handler emitted admin.user.enabled with target_user_id only, identically for a real disabled-to-enabled transition and for a call on an account that was already enabled. I10 requires the two to be distinguishable in the audit record. Enable now returns the transition from the locked transaction that made it, and the handler records detail.transition and detail.revocation_scope (interactive or none). The value is never inferred from a later read, which a concurrent enable or disable could already have changed. Nothing is claimed about service-account tokens. api-users 1.4.0: C-08 amended, AC-17 given explicit inputs and expected output, AC-20 added. AC-20 reads the audit row the request itself produced, by correlation id, for both outcomes. The admin.user.enabled declaration now describes both outcomes; the old description said "re-enabled a previously disabled user account", which was false for a no-op. Refs: bugs/OW-069, bugs/doing/OW-062 sections 9.1, 9.2 and 11.2
remyluslosius
deleted the
fix/ow-069-enable-revokes-interactive-credentials
branch
September 25, 2026 01:00
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Partial. This PR lands part of the closure scope for
bugs/OW-069andbugs/doing/OW-073. It closes neither record. Both close only against the single matrix inbugs/doing/OW-062section 14.7, run as integrated acceptance onmain.What was wrong
users.Enablecleareddisabled_atand revoked nothing. A credential that existed while the account was disabled authenticated again as soon as an administrator re-enabled the account. The first integrated closure run ona22c8b29measured it: a session cookie, an access token and a body refresh token written during the disabled window all answered 200 afterEnable(OW-062 criterion AC-59, second variant).What changed
This implements I10, approved 2026-09-23.
disabled_atand revokes every session and refresh family in the same transaction. The user signs in again.updated_atis not bumped.ErrUserNotFound, as before (404 at the API).Audit record (added after review, 1e1bab6)
Enablereturns whether it made a transition, from the locked transaction itself. The handler recordsdetail.transitionanddetail.revocation_scope(interactiveornone) onadmin.user.enabled, and never infers them from a later read. The event declaration describes both outcomes and claims nothing about service-account tokens.api-usersC-08 is amended, AC-17 now has explicit inputs and expected output, and AC-20 reads the audit row each request itself produced, by correlation id, for both outcomes.What the fixture proves
AC-70 writes its stray credentials directly, bypassing every handler and the lock. It proves what
Enabledoes with state an older release or a leaked issuance path could leave. It does not show that a current handler issues credentials while an account is disabled.Specs
system-auth-identity1.8.0: C-34 and C-36 amended; AC-70 (transition, controlled failure, serialized), AC-71 (no-op), AC-72 (service tokens).api-users1.4.0: C-07 and C-08 amended; AC-20 added. AC-17 amended so it can fail. As written it passed before and after this change, because disable had already revoked the only credential it checked.Not changed: the Manage modal wording (
frontend-settingsC-08). The proposed sentences in OW-062 section 9.3 also describe API-token behavior from P8, which is not implemented.Evidence
go test -race -p 1 ./...passed, 65 packages.make spec-checkpassed: 121 specs, structural coverage 100%.make lintcannot run locally (pinned golangci-lint 1.64.8 against Go 1.26), so lint is checked only by hosted CI.Refs:
bugs/OW-069,bugs/doing/OW-073,bugs/doing/OW-062sections 11.2 (I10) and 14.7.