Skip to content

feat(service-accounts): Add details page with token list, create and revoke - #130485

Draft
sentry-junior[bot] wants to merge 5 commits into
feat/service-accounts-settings-list-createfrom
feat/service-accounts-tokens-ui
Draft

sentry-junior[bot] wants to merge 5 commits into
feat/service-accounts-settings-list-createfrom
feat/service-accounts-tokens-ui

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Stacks on #130140. Adds the service account details page and token management to Settings.

  • Each name in the service accounts list now links to /settings/:orgId/service-accounts/:serviceAccountId/. The page loads the account from the existing detail endpoint and shows its permissions, project access and created date. Editing or deleting the account is out of scope here.
  • The Tokens section lists active tokens with a masked preview (sntrym_************abcd), a scopes summary, the created date, the last used date ("Never" when null), and the expiration date ("Never" when null, with an Expired tag once it has passed). Each row has a Revoke button with a confirm step. After a revoke, the list refetches.
  • New Token goes to .../tokens/new/. The form has a required name and the same searchable permission scope multi-select as the create service account form, limited to the account's own scopes and all selected by default.
  • The form also has an Expiration select: 30 days (default), 60 days, 90 days, a custom date, or no expiration. Picking no expiration shows a warning. A custom date must be after today, and the token works through the end of that day in the user's timezone. The form sends expiresAt as an ISO timestamp, or null for no expiration.
  • After the create succeeds, the plaintext token appears once in the existing displayNewToken modal (copy input plus a "won't be shown again" warning), and closing the modal returns to the details page. Field errors in the response's detail object (name, scopes, expiresAt) show on their fields. A string detail shows as a toast.
  • New Token and Revoke are disabled without service_account:write. The tooltip matches the list page's create button.
  • Adds the two token routes to controlsiloUrlPatterns.ts so the frontend sends them to the control silo.

Depends on the backend token endpoint PRs, one per endpoint and stacked on #130456: list #130483, create #130507 and revoke #130508. Without them, these pages work only against mocks. The two token URLs added to knownSentryApiUrls.generated.ts here match those PRs' generated entries.

The backend PRs add the same two controlsiloUrlPatterns.ts lines, list in #130483 and revoke in #130508, so test_no_missing_urls passes on them. Whichever side lands second gets an identical change.

via @skaasten.

--

View Session [Sentry]

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

No findings on the latest commit.

skaasten pushed a commit that referenced this pull request Nov 10, 2026
#130483)

Adds a new endpoint for listing service account tokens, gated by service
account feature flag

------

Adds `GET /organizations/{org}/service-accounts/{id}/tokens/`. This is
the first of the small token endpoint PRs, one per endpoint. It's
stacked on #130456, which adds token generation:

1. **List tokens (this PR)**
2. Create a token: #130507
3. Revoke a token: #130508

The endpoint runs in control, is private, and is gated on
`organizations:service-accounts` like the other service account
endpoints. It needs `service_account:read`.

It returns the account's active tokens, newest first: `id`, `name`,
`scopes`, `tokenLastCharacters`, `dateCreated`, `dateLastUsed`,
`dateExpires`. Revoked tokens aren't listed, and the response never
includes a token's value. Pagination works the same way as the service
account list. The account in the URL must be active and in the URL's
org, otherwise it returns 404.

Only a person signed in as themselves can call it. A service account
gets 403 `user-required` (`requires_user`).

The account lookup from the details endpoint moved into a shared
`ServiceAccountScopedEndpointBase`. The details endpoint behaves the
same.

The new URL is added to
`static/app/utils/api/knownSentryApiUrls.generated.ts`, so the frontend
PR (#130485) can type-check against it.

<!-- junior-request-attribution:start -->
via @skaasten.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0CA89MBDQB%3A1793911581.772709 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0CA89MBDQB%3A1793911581.772709)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0CA89MBDQB%3A1793911581.772709/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
@sentry-junior
sentry-junior Bot force-pushed the feat/service-accounts-tokens-ui branch from 07a85cd to 1a9b971 Compare November 12, 2026 20:11
sentry-junior Bot and others added 5 commits November 12, 2026 17:07
…revoke

Link each service account to a details page that shows its permissions and
project access, lists its active tokens, and lets managers revoke them or
create new ones. A new token is shown once in the same modal org auth tokens
use, then the page returns to the details view.

Co-Authored-By: Shaun Kaasten <900809+skaasten@users.noreply.github.com>
…n the token form

Match the create service account form: pick token scopes with the searchable
multi-select, limited to the account's own scopes, and read validation errors
from the response's detail object as the token endpoints return them.

Co-Authored-By: Shaun Kaasten <900809+skaasten@users.noreply.github.com>
master moved Confirm into the scraps package (#130251), and CI builds this
stack merged with master.
Frontend half of the service account token endpoints in #130483, split out because frontend and backend deploy separately. It lands first: the patterns are harmless before the backend routes exist, and #130483's test_no_missing_urls passes once this is in.

Co-Authored-By: shaun.kaasten <900809+skaasten@users.noreply.github.com>
The New Token form gets an Expiration select: 30 (default), 60 or 90 days,
a custom date, or no expiration with a warning. The token list gets an
Expires column, and expired tokens show an Expired tag.
@skaasten
skaasten force-pushed the feat/service-accounts-tokens-ui branch from 1a9b971 to 18d078d Compare November 12, 2026 22:08
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Nov 12, 2026
@sentry-junior
sentry-junior Bot force-pushed the feat/service-accounts-tokens-ui branch from 18d078d to 967e1ba Compare November 12, 2026 22:11
@github-actions github-actions Bot removed the Scope: Backend Automatically applied to PRs that change backend components label Nov 12, 2026
@sentry-junior
sentry-junior Bot force-pushed the feat/service-accounts-tokens-ui branch from 967e1ba to 18d078d Compare November 12, 2026 22:11
skaasten added a commit that referenced this pull request Nov 13, 2026
…ens (#130508)

Adds `DELETE
/organizations/{org}/service-accounts/{id}/tokens/{token_id}/`. This is
the last of three small token endpoint PRs, one per endpoint:

1. List tokens: #130483
2. Create a token: #130507
3. **Revoke a token (this PR)**

The endpoint needs `service_account:write`. It locks the token row, sets
`date_deactivated` and saves it, so the token's outbox updates the
replica in the org's cell. It returns 204. The token must belong to the
account in the URL, and the account must be active and in the URL's org.
Otherwise, or if the token is already revoked, it returns 404.

It uses the same guards as create: only a person signed in as themselves
can call it, and Seer agent tokens and impersonation sessions are
refused.

Each revoke writes a `SERVICE_ACCOUNT_TOKEN_REMOVE` (227) audit log
entry. Its data holds the token name, scopes and account, never the
token or its hash.

The settings UI's Revoke button in #130485 calls this endpoint.

**Tests:** the revoke test drains the token's outbox and checks that the
cell's `ServiceAccountTokenReplica` gets the token's new
`date_deactivated`. This works because #130265, the token replication
handler, is merged.

<!-- junior-request-attribution:start -->
via @skaasten.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:slack%3AC0CA89MBDQB%3A1794494200.498819 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/slack%3AC0CA89MBDQB%3A1794494200.498819)
[[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0CA89MBDQB%3A1794494200.498819/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Shaun Kaasten <900809+skaasten@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — 18d078d4 Deployed Nov 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant