Skip to content

feat(service-accounts): Add an endpoint to list service account tokens - #130483

Merged
skaasten merged 2 commits into
masterfrom
feat/service-account-token-endpoints
Nov 10, 2026
Merged

skaasten merged 2 commits into
masterfrom
feat/service-account-token-endpoints

Conversation

@sentry-junior

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

Copy link
Copy Markdown
Contributor

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: feat(service-accounts): Add an endpoint to create service account tokens #130507
  3. Revoke a token: feat(service-accounts): Add an endpoint to revoke service account tokens #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.

via @skaasten.

--

View Session [Sentry]

@sentry-junior
sentry-junior Bot added this pull request to stack #130484 November 9, 2026 19:28
@github-actions github-actions Bot added Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components labels Nov 9, 2026
@sentry

sentry Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Sentry Snapshot Testing

Name Added Removed Changed Renamed Unchanged Skipped Status
sentry-frontend
sentry-frontend
0 0 0 0 804 0 ✅ Unchanged

⚙️ sentry-frontend Snapshot Settings

sentry-junior Bot added a commit that referenced this pull request Nov 9, 2026
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>
@sentry-junior
sentry-junior Bot force-pushed the feat/service-account-token-endpoints branch from 9fe67ed to 7f2622d Compare November 9, 2026 20:48
@sentry-junior sentry-junior Bot changed the title feat(service-accounts): Add endpoints to list, create and revoke tokens feat(service-accounts): Add an endpoint to list service account tokens Nov 9, 2026
@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

🚨 Warning: This pull request contains Frontend and Backend changes!

It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently.

Have questions? Please ask in the #discuss-dev-infra channel.

@skaasten
skaasten force-pushed the feat/service-account-token-endpoints branch from 78f6a69 to 55189db Compare November 10, 2026 14:55
Base automatically changed from feat/service-account-token-generation to master November 10, 2026 15:33
…okens

GET /organizations/{org}/service-accounts/{id}/tokens/ returns the account's
active tokens, newest first. It never returns token values. Only a person
signed in as themselves can call it, not a service account.

Gated by organizations:service-accounts.
@skaasten
skaasten force-pushed the feat/service-account-token-endpoints branch from 55189db to 2cf08eb Compare November 10, 2026 15:33
@skaasten
skaasten marked this pull request as ready for review November 10, 2026 16:44
@skaasten
skaasten requested a review from a team as a code owner November 10, 2026 16:44
@skaasten
skaasten merged commit 2696547 into master Nov 10, 2026
90 checks passed
@skaasten
skaasten deleted the feat/service-account-token-endpoints branch November 10, 2026 17:18
sentry-junior Bot added a commit that referenced this pull request Nov 12, 2026
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>
skaasten added a commit that referenced this pull request Nov 12, 2026
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>
sentry-junior Bot added a commit that referenced this pull request Nov 12, 2026
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>
skaasten pushed a commit that referenced this pull request Nov 13, 2026
…ens (#130507)

Adds a new endpoint for creating a service account access token

We ensure the scopes for the token do not exceed the scopes for the
current user.

------------------


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

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

The endpoint needs `service_account:write` and takes `{name, scopes,
expiresAt}`:

- Each scope must already be granted to the account, otherwise it
returns 400 with an error on `scopes`.
- `expiresAt` defaults to 30 days from now (`DEFAULT_TOKEN_LIFETIME`). A
value you set must be in the future, otherwise it returns 400 with an
error on `expiresAt`. Send `null` for a token that never expires.
- The token is generated with `generate_token(org slug, cell locality
URL)`, using the same cell URL lookup as org auth tokens. Only the hash
and the last 4 characters are stored.
- The 201 response is the list item plus `token`. It's the only response
that includes the token value.

Seer agent tokens and impersonation sessions can't create tokens
(`DisallowAgentTokenWrites`, `DisallowImpersonatedTokenCreation`). A
service account gets 403 `user-required`, the same as on the list
endpoint.

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

Review notes:
- This stores `date_expires` but doesn't enforce it. Nothing
authenticates service account tokens on requests yet, so the
authenticator that comes later must reject a token once `date_expires`
has passed.
- `ServiceAccountTokenEndpointBase.permission_classes` is a longer tuple
than its parent's, so
`OrganizationServiceAccountEndpointBase.permission_classes` now has an
explicit `tuple[type[BasePermission], ...]` type for mypy.

<!-- 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>
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 — 2cf08eb7 Deployed Nov 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants