Skip to content

Service Accounts#3058

Open
GregorShear wants to merge 2 commits into
masterfrom
greg/service-accounts-v2
Open

Service Accounts#3058
GregorShear wants to merge 2 commits into
masterfrom
greg/service-accounts-v2

Conversation

@GregorShear

@GregorShear GregorShear commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds service accounts: non-login identities (backed by auth.users rows) that authenticate via API keys and are authorized through the existing user_grants / role_grants system.
  • Adds one new table, internal.service_accounts and adds the column public.refresh_tokens.created_by.
  • Adds GraphQL mutations createServiceAccount, addServiceAccountGrant, removeServiceAccountGrant, createServiceAccountToken, revokeServiceAccountToken, and a paginated serviceAccounts query.
  • Adds a fine-grained ManageServiceAccount capability (included in the TeamAdmin bundle) that gates service-account management.

Key design decisions

  • Service accounts are auth.users rows. All existing RLS policies, PostgREST authorization, user_roles() resolution, and role_grants traversal work unchanged. Each account gets a synthetic, non-login address (<catalog_name>@service.estuary.dev) and stores its catalog name as full_name.
  • catalog_name is a management anchor It is unique and determines who may manage the account (admins of a covering prefix, via ManageServiceAccounts) and how the account is addressed.
  • Access is governed solely by the service_account's user_grants, which may span multiple prefixes.
  • Authorization is split to prevent privilege escalation. Managing an account (create, mint/revoke keys, add/remove grants) requires ManageServiceAccount on the catalog name. Adding a grant additionally requires CreateGrant on the granted prefix, so a caller can't hand a service account reach they couldn't grant anyone. Removing a grant requires only management capability, since narrowing access is always safe.
  • Creating a grant where one already exists overwrites the old one (i.e. you can downgrade capabilities this way)

Test plan

  • Create service account with grants → mint refresh token → exchange for an access token via POST /api/v1/auth/token
  • A caller without ManageServiceAccount cannot create, or manage service accounts
  • addServiceAccountGrant requires CreateGrant on the target prefix; removeServiceAccountGrant does not
  • refresh token validFor validation: reject non-ISO-8601 durations, non-positive durations, and durations over 1 year
  • serviceAccounts query is scoped to the caller's ManageServiceAccounts prefixes
  • Duplicate catalogName is rejected with a clear error

@GregorShear GregorShear force-pushed the greg/service-accounts-v2 branch 2 times, most recently from faf9471 to 92dd836 Compare June 18, 2026 21:45
@GregorShear GregorShear changed the title Greg/service accounts v2 Service Accounts Jun 18, 2026
@GregorShear GregorShear requested review from jshearer and removed request for jshearer June 18, 2026 21:58
@GregorShear GregorShear force-pushed the greg/service-accounts-v2 branch from 92dd836 to 35ff08b Compare June 19, 2026 00:27
.map_err(duplicate_err)?;

for grant in &grants {
crate::grants::overwrite_user_grant(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have mixed feelings about doing this in the create mutation - maybe we need an "update grant" mutation that lets us downgrade capabilities, and the create mutation just upserts (upgrade capabilities only)?

}
Self::Billing => EnumSet::empty(),
Self::TeamAdmin => CreateGrant | DeleteGrant | CreateInviteLink,
Self::TeamAdmin => CreateGrant | DeleteGrant | CreateInviteLink | ManageServiceAccount,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's talk about this

@GregorShear GregorShear requested a review from jshearer June 19, 2026 00:29
@GregorShear GregorShear marked this pull request as ready for review June 19, 2026 00:29
@GregorShear GregorShear mentioned this pull request Jun 19, 2026
8 tasks

# Run last so its flakiness can't skip the test steps above. It still gates
# the job on failure, surfacing genuine Stripe regressions.
- name: Stripe integration test

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving this flaky test to the end of the line, at least for now

@GregorShear GregorShear self-assigned this Jun 22, 2026
@GregorShear GregorShear force-pushed the greg/service-accounts-v2 branch from 2052d3d to 03fa4db Compare July 2, 2026 22:02
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.

2 participants