Skip to content

connect-only members cannot open a team vault, and the client calls it "Access revoked" #187

Description

@kipavy

Found while live-testing #70 (PR #186) with two real accounts against an isolated server.

What happens

A member holding only the built-in connect-only role opens the team vault they were just invited to and sees:

Access revoked — You no longer have access to this team vault. Contact the team owner.

They were never revoked. They are in team_members, a key-holder has wrapped the vault key for them, and the row exists in team_vault_keys.

Why

GET /v1/teams/:team_id/vault-key (server/src/routes/team_sync.rs) requires PERM_VIEW_SECRETS:

crate::permissions::require_all_team_permissions(
    &pool, team_id, auth.0,
    &[crate::permissions::PERM_VIEW_SECRETS],
).await?;

connect-only is 28676 in BUILTIN_ROLES = CONNECT | START_TERMINAL_SESSION | JOIN_TERMINAL_SESSION | VIEW_TERMINAL_SESSIONS. No VIEW_SECRETS, so the route returns 403, and getTeamVaultKey (src/services/teamVaultSync.ts:119) maps 403 to "forbidden" — the revocation state. From the client's side a role denial and a revocation are indistinguishable.

Note the asymmetry: reconcileTeamVaultKeys happily wraps and uploads a key for a connect-only member (put_vault_keys only validates team membership), so the server stores a key that the same server then refuses to hand back.

When it bites

Exactly at first access. fetchTeamData tries listTeamObjects first; once the vault holds objects that path serves them and the vault opens for a connect-only member anyway. It is the empty team vault — the one a joiner meets right after the owner converts a private vault and invites them — that falls through to the key route and 403s.

So the very first thing a connect-only invitee sees is a false revocation notice, and the "land them on the connections they can use" behaviour from #70 has nothing to land on.

Reproduction

  1. Owner A converts a private vault into a team vault (no hosts pushed yet).
  2. A invites B with initial role connect-only only.
  3. B accepts, A comes online so the key is wrapped (Vault keys upserted key_count=2).
  4. B selects the vault → "Access revoked". Server log: GET .../vault-key → 403.
  5. A adds one host to the team vault → B reloads → the vault opens normally.

The decision this needs

Should a connect-only member hold a wrapped vault key at all?

  • Yes — they need the vault contents to connect to anything, and they already receive one in practice. Then get_my_vault_key should gate on PERM_CONNECT || PERM_VIEW_SECRETS rather than VIEW_SECRETS alone, and the credential-hiding stays where it already is (team_vault_secrets / the VIEW_SECRETS checks on the secret payloads).
  • No — then reconcileTeamVaultKeys should stop wrapping keys for members who may never fetch them, and the client needs a real state for "your role cannot unlock this vault" instead of borrowing the revocation copy.

Either way the client should stop rendering a bare 403 as "Access revoked" when the user is still a listed member. No copy was invented for it in PR #186 because the answer changes what the copy should say.

Related: #70, #41, #68.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingteam-uxTeam & collaboration UX flows (sessions, vaults, invites, presence)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions