Hive
Tuist app writes account-type OAuth credentials into the CLI’s credentials file, silently downgrading the CLI session
GitHub issue · Open
Summary
Signing in through the Tuist macOS app writes its OAuth credentials into the CLI’s credentials file (~/.config/tuist/credentials/<host>.json). Because those credentials are an account-type token rather than a user session, the CLI is silently downgraded: tuist auth whoami starts reporting “You are not logged in”, and every run the CLI uploads from then on is recorded with no user.
Reproduction
- Remove
~/.config/tuist/credentials/tuist.dev.json. - Launch the Tuist macOS app and sign in.
- The file is recreated. Its
accessTokendecodes to"type": "account", withsubset to the user’s personal account id, alongsideuser_idandpreferred_usernameclaims,all_projects: true, and the default user scope set (includingproject:runs:write). tuist auth whoami→ “You are not logged in.”tuist generate→ succeeds, uploads a run, and the run shows as “Unknown” in the dashboard.
Confirmed on a clean machine state; steps 1–3 reproduce it directly.
Mechanism
The app is meant to use the keychain backend. TuistApp binds it like this:
ServerCredentialsStore.$current.withValue(
ServerCredentialsStore(backend: .keychain)
) { ... }
ServerCredentialsStore.current is a @TaskLocal, and this binding wraps only the SwiftUI ViewBuilder closure. Token storage happens in AuthenticationService.handleTokenResponse(_:), reached from an async flow resumed off an ASWebAuthenticationSession continuation — work that a ViewBuilder-scoped task-local does not reach. Outside the binding, ServerCredentialsStore.current falls back to the macOS default:
#if os(macOS)
@TaskLocal public static var current: ServerCredentialsStoring = ServerCredentialsStore(backend: .fileSystem)
.fileSystem on macOS resolves to <configDirectory>/credentials/<host>.json — the exact file the CLI reads.
Impact
Two effects, and the second is the one that hurts:
- The app’s credentials are stored on disk rather than in the keychain, which is not the intended storage for them.
- Any developer who signs into the app has their CLI session replaced by an account token. From that point the CLI reports itself as logged out while remaining authenticated, and its runs lose attribution. This is not a per-project misconfiguration — it follows the developer to every project they work on, and it is silent.
Open question
There are two defensible fixes and they are not the same decision:
- Bind the store where the storage actually happens, so the app keeps its credentials in the keychain and stops touching the CLI’s file. This restores the intended separation.
- Decide that the app and CLI should share a session on purpose. In that case the app should be obtaining a user session rather than an account token, so the CLI keeps working the way an interactive
tuist auth loginleaves it.
Silently sharing an account token is the one combination that shouldn’t stand, since it authenticates fine while breaking identity everywhere downstream.
Related
- #12218 — server-side attribution loss for account-token runs
- #12219 —
whoamireporting “not logged in” for these credentials
Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.
Start a Flight and preserve its objective, outcome, and session here.
No GitHub comments yet.