Skip to content

feat: API token management in workspace settings#3

Open
dnplkndll wants to merge 33 commits into
developfrom
feat/api-token-management
Open

feat: API token management in workspace settings#3
dnplkndll wants to merge 33 commits into
developfrom
feat/api-token-management

Conversation

@dnplkndll
Copy link
Copy Markdown

CI validation PR (fork-internal)

Testing rebased API token management branch against latest develop.

Upstream PR: hcengineering#10624

Commits

  1. Core feature — API token CRUD + UI
  2. Token revocation enforcement at transactor
  3. Phase 1 token scopes (read/write/delete)
  4. Scope unit tests
  5. Review feedback (role restriction, locale parity)

@dnplkndll dnplkndll force-pushed the feat/api-token-management branch 4 times, most recently from ebdcb02 to 370a096 Compare March 27, 2026 11:30
@dnplkndll dnplkndll force-pushed the feat/api-token-management branch from 370a096 to cf87e38 Compare April 18, 2026 19:05
@dnplkndll dnplkndll force-pushed the feat/api-token-management branch from e0e0743 to a3a8019 Compare May 11, 2026 22:16
dnplkndll and others added 9 commits May 15, 2026 13:46
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.

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.

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

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

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

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 a3a8019 to dfa360b Compare May 15, 2026 17:46
BykhovDenis and others added 14 commits May 17, 2026 14:53
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
Signed-off-by: Igor Loskutov <igor.loskutoff@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: Denis Bykhov <bykhov.denis@gmail.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
ArtyomSavchenko and others added 10 commits May 20, 2026 13:08
Signed-off-by: Artem Savchenko <armisav@gmail.com>
* feat: Configure workspaces during creation

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix question description

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Clean up

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix formatting

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix tests

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix tests

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Potential fix for pull request finding 'CodeQL / Insecure randomness'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: Artyom Savchenko <armisav@gmail.com>

* Revert "Potential fix for pull request finding 'CodeQL / Insecure randomness'"

This reverts commit e37b6de.

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Simplify workspace creation

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix layout

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Clean up

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Keep redesigned version

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix tests

Signed-off-by: Artem Savchenko <armisav@gmail.com>

---------

Signed-off-by: Artem Savchenko <armisav@gmail.com>
Signed-off-by: Artyom Savchenko <armisav@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: Artem Savchenko <armisav@gmail.com>
Report slow client findAll measurements as warnings instead of errors so the browser console reflects that these entries are performance diagnostics, not failed queries.

Preserve the existing threshold, payload, and timing details for performance triage.

Signed-off-by: Ignat Remizov <ignat@ignatremizov.com>
Treat an unset front-service PUSH_PUBLIC_KEY as disabled browser push support instead of passing an empty string into PushManager.subscribe.

Changes:
- Add a small push public key accessor that normalizes undefined and blank metadata to undefined.
- Use that accessor for both push availability checks and push subscription setup.
- Preserve existing behavior when a real VAPID public key is configured.

Behavioral effect:
Instances without web push configured no longer attempt service worker push subscription with an invalid empty ECDSA key, while keeping browser push disabled until VAPID keys and the notification service are configured.

Signed-off-by: Ignat Remizov <ignat@ignatremizov.com>
Resolve conflicts:
- migrations: keep develop's V26 (pending_configuration); api_tokens table
  (with scopes column) becomes V27
- lang files: union of develop's keys and the API token strings

Signed-off-by: Don Kendall <dkendall@ledoweb.com>
Address @aonnikov's review: the bespoke checkApiTokenRevoked RPC and the
ad-hoc revocation cache in the transactor are replaced by a reusable
verifyToken in server-token that checks signature, expiry, and (for
revokable API tokens) revocation via a pluggable checker.

- server-token: add verifyToken + isTokenExpired + setApiTokenRevocationChecker
  (the 'method to verify' metadata the plugin needs, without depending on the
  account client). Revocation cache (60s TTL) now lives here, reusable by any
  service (transactor, blob access, etc.).
- account: the account is now authoritative — wrap() rejects revoked/expired
  API tokens, so any account method (selectWorkspace/getWorkspaceInfo/...)
  naturally 401s. Removed the redundant checkApiTokenRevoked method.
- account-client: drop checkApiTokenRevoked.
- transactor: withSession uses verifyToken; the registered checker reuses the
  existing getLoginInfoByToken instead of a dedicated boolean RPC. Scopes are
  parsed once and threaded through (no re-decode in the tx handler).
- tests: verifyToken/isTokenExpired unit coverage.

Signed-off-by: Don Kendall <dkendall@ledoweb.com>
…ndpoint

Address @aonnikov: the REST API host must come from the transactor endpoint
returned by the account service (the login endpoint, a ws(s):// URL), not be
constructed from window.location. Convert ws->http and append /api/v1, matching
the existing ServerManagerGeneral pattern.

Signed-off-by: Don Kendall <dkendall@ledoweb.com>
Address @ArtyomSavchenko: the new API token UI strings were left in English
in non-en locales. Provide translations for ru/de/es/fr/it/pt/pt-br/zh/ja/cs/tr.
The en/ru locale-parity test passes (the prior failure was an en/ru key
mismatch, resolved by the develop merge).

Signed-off-by: Don Kendall <dkendall@ledoweb.com>
…DocsSection)

Signed-off-by: Don Kendall <dkendall@ledoweb.com>
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.

5 participants