Skip to content

fix(invites): revoke pending user invites by ID - #367

Open
azzzy wants to merge 3 commits into
Infisical:mainfrom
azzzy:fix/user-invite-revoke-by-id
Open

fix(invites): revoke pending user invites by ID#367
azzzy wants to merge 3 commits into
Infisical:mainfrom
azzzy:fix/user-invite-revoke-by-id

Conversation

@azzzy

@azzzy azzzy commented Aug 13, 2026

Copy link
Copy Markdown

Summary

Fixes pending user-invite revocation.

The Users page listed pending invitations with a plaintext token field. The token is available only when an invitation is created. The database stores only its hash. As a result, listed invitations had an empty token and could not be revoked.

The page also did not render the confirmation dialog for pending invitations. Clicking Revoke invite closed the menu without sending a request.

This change:

  • returns the invitation ID from GET /v1/users/invites;
  • uses authenticated ID-based endpoints for revoke and reinvite;
  • keeps the secret token for invitation details and acceptance only;
  • shows a pending-invitation confirmation dialog;
  • updates the CLI to list and revoke invitations by ID;
  • shares the authorization check for revoke and reinvite.

This follows the pending-agent invite pattern from #172.

Security follow-up

The ID is sequential. An unrelated authenticated member must not learn whether an invitation exists or whether it is pending. Revoke and reinvite now authorize the caller before checking invitation status. Unauthorized callers receive 404 for both pending and non-pending invitations.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation
  • CI / build

Test plan

  • Existing tests pass (make test)
  • Added/updated tests for new behavior
  • Manual testing (describe below)

Passed:

  • go test ./internal/store ./internal/server
  • make build
  • go test ./internal/server after the security follow-up

Added coverage for:

  • listing a pending invitation with its ID and no plaintext token;
  • revoking a pending invitation by ID;
  • invalid, missing, accepted, and unauthorized revoke attempts;
  • store lookup and revocation by ID;
  • non-disclosure of pending and accepted invitation state to unrelated members for revoke and reinvite.

make test has one unrelated environment-dependent CLI failure: TestResolveVaultWithProjectFile/falls_back_to_default_when_no_file reads an existing local default vault (user-default) instead of the test's expected default. The store and server packages pass.

Manual verification still needed:

  1. Create a pending user invitation.
  2. Open Users as an owner.
  3. Select Revoke invite.
  4. Confirm using the invitee email.
  5. Verify one DELETE request to /v1/users/invites/by-id/{id}.
  6. Verify the pending row disappears.

Security checklist

  • No secrets or credentials in code
  • No new unauthenticated endpoints
  • Input validation on new API surfaces
  • Checked for OWASP top 10 (injection, XSS, etc.)

@infisical-cla-app

Copy link
Copy Markdown

📝 Contributor License Agreement required

Before this PR can merge, every contributor must sign the Infisical CLA.
Signing is quick: sign in with GitHub, review the CLA, and accept.

👉 Sign the CLA

Still needs to sign:

Once everyone has signed, the check updates automatically — no need to close and reopen the PR.

@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces plaintext-token-dependent invitation management with authenticated ID-based revoke and reinvite operations.

  • Returns invitation IDs from the list API and updates the CLI and web UI to use them.
  • Adds ID-based store operations, shared authorization logic, and revoke coverage.
  • Adds the missing pending-invitation confirmation dialog.

Confidence Score: 3/5

The PR should not merge until the new by-ID handlers authorize callers before returning distinguishable invitation existence or lifecycle responses.

Predictable invitation IDs and pre-authorization 404/409 responses allow any authenticated instance member to enumerate invitation existence and pending state.

Files Needing Attention: internal/server/handle_users.go

Security Review

The new handlers expose invitation existence and pending state through distinguishable responses before authorization. Because invitation IDs are sequential, unrelated authenticated members can enumerate this state. How this was verified: The new handlers return 404 or 409 for a supplied ID before invoking the invitation-management authorization check.

Important Files Changed

Filename Overview
internal/server/handle_users.go Adds shared authorization and ID-based revoke/reinvite handlers, but checks authorization only after disclosing invitation existence and status.
internal/server/server.go Replaces authenticated token-based administrative routes and store contracts with by-ID variants.
internal/store/sql_store.go Adds invitation lookup and conditional revocation by database ID while removing obsolete token-based update methods.
web/src/pages/home/AllUsersTab.tsx Maps listed invitations to IDs, revokes by ID, and renders the pending-invitation confirmation dialog.
cmd/user_invite.go Displays invitation IDs and updates CLI revocation to call the authenticated by-ID endpoint.
internal/server/server_test.go Updates the mock store and adds HTTP coverage for listing, revocation, invalid IDs, lifecycle state, and authorization.
internal/store/user_invite_test.go Adds SQL-store coverage for ID lookup and pending-only revocation.

Reviews (1): Last reviewed commit: "refactor(invites): share invite authoriz..." | Re-trigger Greptile

Comment thread internal/server/handle_users.go Outdated
Comment on lines 633 to 641
inv, err := s.store.GetUserInviteByID(ctx, id)
if err != nil || inv == nil {
jsonError(w, http.StatusNotFound, "Invite not found")
return
}
if inv.Status != "pending" {
jsonError(w, http.StatusConflict, "Invite is not pending")
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 security Invite state leaks before authorization

When an unrelated authenticated member submits predictable invitation IDs, this handler returns distinct 404 and 409 responses before calling canManageUserInvite, exposing which invitations exist and whether they remain pending; the reinvite handler has the same ordering. Move the authorization boundary ahead of these distinguishable state responses. How this was verified: Both handlers test ID existence and pending status before invoking the invitation-management authorization check.

@azzzy

azzzy commented Aug 13, 2026

Copy link
Copy Markdown
Author

I signed the CLA hours ago, but for some reason it isn't reflected in the PR.

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