Clear the leftover StepFun Oasis token on revoke (SBS-920) - #322
Conversation
A refresh wrote the new token to the keyring, and Sign out only cleared Preferences, so the next fetch stayed signed in.
|
Warning Review limit reached
Next review available in: 12 minutes Limit details: You’ve used all 1 included review currently available under your plan. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ceiling | 0337a78 | Commit Preview URL Branch Preview URL |
Aug 18 2026, 02:32 AM |
Automated reviewNew in this pass: 4 issues.
Resolved since the previous pass: 3. For coding agents: fix BLOCK and FIX IF QUICK findings now; everything else is tracked or informational; never exceed one CodeRev fix round per PR. Advisory. Findings generated by |
Two ordering problems around the StepFun keyring copy. The refresh path wrote the refreshed Oasis token with no lock and no check that the credential still existed. A refresh only runs after an auth failure, which is also when someone is most likely to be signing out, so revoke could delete the keyring copy, confirm it gone, report success, and then have the in-flight write put a live token back. The session stayed signed in while the UI said otherwise. The write now takes the same state lock revoke holds and re-reads Preferences under it. An environment variable still counts as configured: revoke cannot clear one, and it authenticates on its own. Revoke itself cleared the file stores first and the keyring last. A hook error then returned failure with every file store already empty, which is exactly the state that hides the Revoke control, leaving no way to retry while the keyring token still worked. A crash in that gap left the same thing. The keyring copy now goes first, so a partial revoke fails closed and the credential stays visible to retry. The body of the revoke takes its paths and its hook as arguments so both orders can be asserted rather than reasoned about.
stepfun_credential_configured used ApiKeys::load, which turns a read or decode failure into an empty store. A machine that could not decode its own Preferences therefore looked like one where StepFun had been revoked, so a freshly refreshed Oasis token was dropped instead of stored. The next poll would go back to the old keyring copy, and if StepFun had rotated the refresh token by then the session would die without anyone signing out. Only a store that read cleanly and has no StepFun key now counts as revoked. A store that will not read keeps the token. The locked persist path also takes its check as a parameter, so a test drives the real function rather than the decision it reaches; removing the lock or hard-coding the check would otherwise leave every test here green.
…evoke-keyring # Conflicts: # CHANGELOG.md
Summary
codexbar-stepfun/api_key).resolve_tokenreads that copy after Preferences is empty.revoke_managed_credentialsonly clearedapi_keys.json,manual_cookies.json, andtoken-accounts.json. After Revoke stored credentials, the next fetch still authenticated from the leftover keyring token.clear_persisted_credentialshook. StepFun deletes that keyring entry. Missing is success. Any other keyring error, or a delete that reports success while the token is still there, fails the revoke.Closes nothing in Linear: SBS-920 stays In Progress until review.
A user who hits this now sees
Someone who pastes a StepFun Oasis token, lets it refresh, then clicks Revoke stored credentials gets
CredentialRevokedonly if the keyring copy is gone (or was never there). The next Auto refresh cannot pick the leftover token back up. If the keyring is locked or the delete cannot be confirmed, revoke fails and the UI shows the error instead of success while the token remains. Preferences /api_keys.jsonwas already cleared by revoke; that path is unchanged.Environment variables (
STEPFUN_OASIS_TOKEN/STEPFUN_TOKEN) are not managed credentials and still resolve after revoke, same as before.Fail without the fix
Reverted only
clear_token_secretto a no-opOk(())(pre-SBS-920 revoke: do not delete the keyring copy), then ran the new test:Restored the delete + post-delete confirmation. The same test then passed.
Tests use an in-memory store, not the OS keychain. A suite that talks to a real keychain is what went red on a headless runner before.
Pattern sweep
Live
set_passwordof a refreshed token: only StepFun. That is the write this PR clears.Other hits, left alone on purpose:
rust/src/core/credentials.rs/credential_migration.rsset_password/delete_credentialdead_code). Not on the revoke path.persist_refreshed_credentialsapi_keys.json.persist_refreshed_credentials.credentials.jsonproviders::resolve_api_key/ Claude oauthload_from_keyringIf another provider later writes a live token to the keyring, it needs a branch in
providers::clear_persisted_credentials. Settings does not name StepFun in code.What this change makes more likely
Revoke for StepFun now talks to the OS keyring. A locked, missing, or lying backend fails the whole revoke even after the three shared files are already cleared. The user sees an error and can retry (both the file removals and the hook are idempotent). That is the intended fail-closed tradeoff: reporting
CredentialRevokedwhile the leftover token still authenticates is the bug.Fetch still falls through to env on a keyring read error, matching the previous resolver. Only revoke fails closed.
Quality gate
Required CI (
.github/workflows/ci.yml):cargo fmt --all --checkcargo test --manifest-path rust/Cargo.tomlcodex_sessionsWSL slash vs backslash,grok_costs/cost_scannerattributingC:\projects\...instead of the leaf name). None are in this diff. CIrust-sharediswindows-latest. New StepFun tests: 8 passed.cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warningssecure_file.rs:489unusederror,updater.rs:442verify_installer_signature_or_delete(Windows-only in practice). CI clippy iswindows-latest..\scripts\ci\test-store-submission-preparation.ps1cargo test/clippydesktop crateglib-2.0, which is not installed. CIrust-desktopiswindows-latest. This PR does not change the desktop crate.target/dirs deleted after the runs.Gaps
OsTokenSecretStore) is the samekeyringAPI persist already used; tests cover the store contract, not gnome-keyring.revoke_managed_credentialsagainst realapi_keys.jsonpaths — that function uses the machine config dir. Wiring is a one-line call after the existing file writes.Note
Clear the StepFun Oasis keyring token on credential revoke
revoke_managed_credentialsnow calls a provider-specificclear_persisted_credentialshook before writing cleared stores to disk; for StepFun, this deletes the OS keyring token and confirms its absence.persist_refreshed_tokenin StepFun now checks under the state lock whether the credential is still configured before writing to the keyring, preventing a background refresh from restoring a token after revoke.TokenSecretStoretrait is introduced to abstract keyring operations, enabling the new delete-then-confirm semantics and making the logic testable.Macroscope summarized 0337a78.