Skip to content

feat: API token management in workspace settings#10624

Open
dnplkndll wants to merge 414 commits into
hcengineering:developfrom
ledoent:feat/api-token-management
Open

feat: API token management in workspace settings#10624
dnplkndll wants to merge 414 commits into
hcengineering:developfrom
ledoent:feat/api-token-management

Conversation

@dnplkndll
Copy link
Copy Markdown
Contributor

@dnplkndll dnplkndll commented Mar 11, 2026

Summary

  • Add full API token CRUD (create, list, revoke) as a new workspace settings page
  • Tokens are workspace-scoped JWTs with configurable expiry (7–365 days), stored in a new api_tokens table
  • Phase 1 token scopes: read:*, write:*, delete:* with server-side enforcement
  • New createApiToken, listApiTokens, revokeApiToken account service RPC methods
  • Frontend: settings page with token list, status/permissions badges, create popup with scope preset selector

Changes

Backend (server/account/):

  • ApiToken type with scopes?: string[] + apiToken DB collection
  • V25 migration: api_tokens table; V26 migration: scopes TEXT[] column
  • Three new RPC methods registered in AccountMethods and getMethods()
  • createApiToken validates scopes, embeds in JWT extra.scopes, persists to DB
  • AccountClient interface + implementation extended with optional scopes param

Scope Enforcement (pods/server/src/rpc.ts):

  • withSession() checks decodedToken.extra.scopes against method requirements
  • read:* required for find-all, search, load-model, account
  • write:* required for tx (create/update), domain requests, ensure-person
  • delete:* additionally required for TxRemoveDoc transactions
  • No scopes (legacy tokens) = full access (backward compatible)

Frontend (plugins/setting-resources/):

  • ApiTokens.svelte — workspace settings page with permissions column (i18n)
  • ApiTokenCreatePopup.svelte — creation modal with scope preset dropdown (default: Read Only)
  • Registered as WorkspaceSettingCategory in the model

Also:

  • Fixed 3 pre-existing TS2322/TS2345 type errors in operations.ts

Test plan

  • Create a token with "Read Only" scope → verify find-all works, tx returns 403
  • Create a token with "Read & Write" → verify find-all and tx work, TxRemoveDoc returns 403
  • Create a token with "Full Access" → verify all operations work
  • Create a token with no scopes (legacy path) → verify full access (backward compat)
  • Token list shows correct permissions badge ("Read Only", "Read & Write", "Full Access")
  • Revoke a token → status changes to "revoked", API returns 401
  • V25+V26 migrations run on fresh and existing databases

Ref: #10622

🤖 Generated with Claude Code

haiodo and others added 30 commits September 16, 2025 13:17
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Kristina Fefelova <kristin.fefelova@gmail.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
This reverts commit 3d469f0.
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
@dnplkndll dnplkndll force-pushed the feat/api-token-management branch from 370a096 to cf87e38 Compare April 18, 2026 19:05
BykhovDenis and others added 22 commits April 19, 2026 23:30
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
…nto staging-new

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Add UI and backend support for creating, listing, and revoking
API tokens scoped to workspaces. Includes owner-level workspace
token visibility, OpenAPI documentation, Mongo/Postgres persistence,
and i18n translations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Don Kendall <kendall@donkendall.com>
Embed apiTokenId in JWT extra field and add a per-token revocation
cache (60s TTL) in the transactor REST handler. Revoked tokens are
now rejected within ~60 seconds instead of remaining valid until
JWT expiry.

Adds checkApiTokenRevoked account service method for the transactor
to query individual token revocation status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Don Kendall <kendall@donkendall.com>
Add coarse-grained scope enforcement for API tokens. Tokens can now
be created with scopes ['read:*'], ['read:*','write:*'], or
['read:*','write:*','delete:*']. Existing tokens without scopes
retain full access (backward compatible).

- DB: v26 migration adds scopes TEXT[] column to api_tokens
- Types: add scopes field to ApiToken and ApiTokenInfo
- Operations: createApiToken accepts/validates/persists scopes,
  embeds in JWT via extra.scopes
- Enforcement: withSession checks scopes against method; tx handler
  additionally requires delete:* for TxRemoveDoc
- Client: createApiToken signature accepts optional scopes param
- UI: scope preset dropdown in create popup (default: Read Only),
  permissions column in token list with i18n labels
- Also fixes 3 pre-existing TS2322/TS2345 errors in operations.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Don Kendall <kendall@donkendall.com>
- scopes.test.ts: 8 tests for hasScope() and getRequiredScope() logic
- apiTokenScopes.test.ts: 7 tests for createApiToken scope validation
  (valid scopes, multiple scopes, no scopes backward compat, invalid
  format rejection, empty array rejection, domain-scope rejection)
  and listApiTokens scopes inclusion
- Export hasScope/getRequiredScope from rpc.ts for testability

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Don Kendall <kendall@donkendall.com>
…tting

- Restrict API token creation/revocation to AccountRole.User or higher
  (guests cannot use API tokens), per reviewer suggestion
- Add 5 missing translation keys (ApiTokenPermissions, ApiTokenScopePreset,
  ApiTokenScopeReadOnly, ApiTokenScopeReadWrite, ApiTokenScopeFullAccess)
  to all non-en locale files to fix locale parity CI test
- Fix prettier formatting in apiTokenScopes.test.ts
- Rename local `extra` to `tokenExtra` in createApiToken to avoid
  shadowing the decoded token's `extra` field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Don Kendall <kendall@donkendall.com>
- rpc.ts: use system service token for checkApiTokenRevoked so the
  revocation check is not coupled to the user's potentially-revoked
  bearer token; systemAccountUuid + service:'server' ensures account
  service always accepts the call
- ApiDocsSection.svelte: derive transactor base URL from
  login.metadata.LoginEndpoint (set on auth) instead of
  window.location.origin, which is not necessarily the transactor host
- ApiTokenCreatePopup.svelte: replace manual translate() calls and
  themeStore language watch with DropdownLabelsIntl + DropdownIntlItem[],
  which handle i18n automatically; error state is now IntlString
- General.svelte: remove legacy GenerateApiToken button, handler, and
  ApiTokenPopup import in favour of the new ApiTokens settings panel

Signed-off-by: Don Kendall <kendall@donkendall.com>
Signed-off-by: Don Kendall <kendall@donkendall.com>
Signed-off-by: Don Kendall <dkendall@ledoweb.com>
Signed-off-by: Don Kendall <dkendall@ledoweb.com>
@dnplkndll dnplkndll force-pushed the feat/api-token-management branch from e0e0743 to a3a8019 Compare May 11, 2026 22:16
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.

8 participants