Skip to content

Migrate flowctl to gql operations and token-exchange rest endpoints#2983

Draft
GregorShear wants to merge 3 commits into
greg/service-accounts-phase1from
greg/sa/flowctl
Draft

Migrate flowctl to gql operations and token-exchange rest endpoints#2983
GregorShear wants to merge 3 commits into
greg/service-accounts-phase1from
greg/sa/flowctl

Conversation

@GregorShear
Copy link
Copy Markdown
Contributor

@GregorShear GregorShear commented May 29, 2026

Migrate flowctl auth off PostgREST onto GraphQL + token-exchange endpoint

Updates flowctl to use the new application-layer auth surfaces introduced in phase 1, retiring the dependence on the PostgREST RPCs create_refresh_token and generate_access_token.

What changed

Refresh-token creation now goes through the createRefreshToken GraphQL mutation instead of the create_refresh_token PostgREST RPC.

Access-token exchange now POSTs to /api/v1/auth/token with grant_type: refresh_token (unauthenticated) instead of the generate_access_token RPC.

Both client implementations are updated in lockstep:

  • flow-client (legacy, used by flowctl) — refresh_authorizations in client.rs
  • flow-client-nextUserTokenSource / create_refresh_token / exchange_refresh_token in user_auth.rs

UserTokenSource swaps its pg_client: postgrest::Postgrest field for rest_client: crate::rest::Client.

New helpers

A small GraphQL request helper is added to each crate so callers can issue a query/mutation and deserialize the data field directly:

  • flow_client::graphql(...)anyhow-based.
  • flow_client_next::rest::Client::graphql(...) — typed errors via a new GraphqlError enum (adds the thiserror dependency).

Notes for review

  • valid_for moves from the PostgREST-style "90d" to ISO-8601 "P90D", matching the GraphQL mutation's contract.
  • The multi_use: true argument is dropped — it's no longer part of the createRefreshToken signature.
  • The token-exchange call is deliberately unauthenticated; the refresh token's id + secret are the only credentials needed, and any stale/expired access token on the client would otherwise cause a spurious rejection.

Testing

@GregorShear GregorShear changed the base branch from master to greg/service-accounts-phase1 May 29, 2026 03:17
@GregorShear GregorShear force-pushed the greg/service-accounts-phase1 branch from 312d85f to dc5e4cd Compare May 29, 2026 03:33
@GregorShear GregorShear force-pushed the greg/service-accounts-phase1 branch from dc5e4cd to dbbeaeb Compare May 29, 2026 04:21
Adds support for running flowctl as a service account using a `flow_sa_`
API key supplied through FLOW_AUTH_TOKEN. The key is exchanged for a
short-lived access token via POST /api/v1/auth/token (grant_type: api_key)
on each invocation.

The API key is a long-lived secret and is held in memory only: it is
sourced exclusively from FLOW_AUTH_TOKEN, never read from or written to the
config file, and a run authenticated by one skips the config write-back
entirely so nothing derived from it lands on disk and a human's existing
config is left untouched.

Credential refresh is unified behind a single helper shared by Cli::run and
the auth post-command refresh, with the API key taking precedence over a
refresh token. Under API-key auth the refresh-token branches -- including
auto-creating a refresh token -- are skipped, so a service account never
attempts to mint a refresh token (which the control-plane guard rejects
regardless).

- flow-client: add refresh_api_key_authorization()
- flowctl config: in-memory user_api_key, flow_sa_ dispatch on FLOW_AUTH_TOKEN
- flowctl: skip config write under API-key auth; login/token clear an
  ambient API key so explicit human auth wins
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant