Hive Hive
Sign in

Tuist app writes account-type OAuth credentials into the CLI’s credentials file, silently downgrading the CLI session

GitHub issue · Open

Metadata
Source
tuist/tuist #12220
Updated
Aug 5, 2026
Domains
CLI
Details

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

  1. Remove ~/.config/tuist/credentials/tuist.dev.json.
  2. Launch the Tuist macOS app and sign in.
  3. The file is recreated. Its accessToken decodes to "type": "account", with sub set to the user’s personal account id, alongside user_id and preferred_username claims, all_projects: true, and the default user scope set (including project:runs:write).
  4. tuist auth whoami → “You are not logged in.”
  5. 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:

  1. The app’s credentials are stored on disk rather than in the keychain, which is not the intended storage for them.
  2. 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 login leaves 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 — whoami reporting “not logged in” for these credentials
Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments

No GitHub comments yet.