Commit Graph

4 Commits

Author SHA1 Message Date
Anso d0e140444a feat(api-tokens): make API tokens available on every tier (#1136)
API tokens are credential management, not a tier-gated capability. Remove
the Admiral gate from the POST/GET/DELETE handlers, drop the AdmiralGate
wrapper from the settings UI, set the registry entry's tier to null so the
tab renders on every tier, and update the docs Note to state availability
plainly.

The three permission scopes (read-only, deploy-only, full-admin), the
25-token-per-user cap, the per-token 200 req/min rate limit, the
sen_sk_ prefix format, and the SHA-256 hashed storage are all unchanged.

The Vitest suite now runs at Community tier to prove every code path works
without a paid license. A new "API token tier accessibility" describe block
mints all three scopes via POST /api/api-tokens to lock the behavior.
2026-05-21 11:45:37 -04:00
Anso d882f223f4 feat(api-tokens): switch to sen_sk_ prefixed opaque keys (#1062)
* feat(api-tokens): switch to sen_sk_ prefixed opaque keys

Replace JWT-shaped API tokens with 56-char opaque keys of the form
`sen_sk_<43-char base62 random><6-char base62 checksum>` (256-bit
entropy, sha256-truncated checksum). Node-proxy tokens stay JWTs.

Why:
* The api_token path was already a sha256 DB lookup; the JWT signature
  was wasted work and the 400d JWT ceiling vs DB expires_at was a
  confusing dual bound.
* Opaque tokens carry a verifiable checksum so malformed/typoed values
  are rejected before any SQLite lookup.
* `sen_sk_` prefix is recognizable to GitHub, TruffleHog, GitGuardian
  and makes the on-wire shape visually distinct from node_proxy JWTs.

Changes:
* New `utils/apiTokenFormat.ts` (generate + checksum-verify, CSPRNG via
  randomInt, timingSafeEqual on the checksum compare).
* `middleware/auth.ts` and `websocket/upgradeHandler.ts` route opaque
  tokens before any jwt.verify; 401 messages unified to avoid a
  token-existence oracle.
* `middleware/rateLimiters.ts` short-circuits opaque tokens in the
  node_proxy detection and keys per-token via a non-reversible sha256
  slice so each token keeps its own bucket without a DB hit.
* All six existing tests migrated from jwt.sign({scope:'api_token'})
  to generateApiToken(); new format-only test suite covering prefix,
  length, alphabet, checksum reject paths, and a 10k-iteration
  collision/integrity loop.
* Docs (features/api-tokens.mdx, api-reference/overview.mdx) describe
  the shape and drop the obsolete JWT-ceiling note.

* fix(api-tokens): clear CI lint and CodeQL false positives

* Drop unused TEST_USERNAME import in remote-console-session.test.ts;
  the migration to generateApiToken() left it orphaned.
* Add a CodeQL barrier model so `generateApiToken`'s ReturnValue does
  not flow into the `insufficient-password-hash` query. The function
  emits 256-bit CSPRNG opaque keys; sha256 of the raw token is the
  correct construction for high-entropy API tokens (bcrypt-class
  hashes target low-entropy human passwords). CodeQL's name heuristic
  was treating "Token" as a password source and flagging the standard
  sha256 wrapping at all 9 call sites.

* ci(codeql): exclude js/insufficient-password-hash for token paths

The previous barrierModel data extension was a no-op for this rule: the
js/insufficient-password-hash query identifies its "password" sources via
SensitiveExpr's name heuristic ("token", "secret", "key" substrings),
which is upstream of the taint-tracking layer where barrierModel applies.
Verified by post-push re-analysis: 9 alerts still open, all undismissed.

Replace the dead extension with a path-scoped query-filter in
codeql-config.yml so the rule no longer fires on apiTokenFormat,
apiTokens, and the test directory. Real user-password hashing code
elsewhere in the repo (auth, users, setup routes) remains analyzed.

The 9 existing alerts on PR #1062 are dismissed via API as false
positives with a justification pointing at this config. Future runs
will not re-flag them because of the path filter.
2026-05-15 18:21:28 -04:00
Anso e0d1ca9dc0 fix(api-tokens): harden with security fixes, design compliance, and test coverage (#567)
Security: add JWT-level expiry ceiling (400d), per-user token count limit (25),
and token name uniqueness enforcement. Fix async clipboard copy.

Design: migrate Select to Combobox, apply card bevel styling, fix icon
strokeWidth, add tabular-nums to timestamps, fix destructive button pattern.

Tests: expand from ~20 to 47 test cases covering creation validation, token
limits, name uniqueness, last_used_at tracking, ownership constraints, delete
edge cases, and registry blocked endpoints.

Docs: update API Tokens docs with token limits, name uniqueness, registry
restrictions, and JWT expiry ceiling. Update OpenAPI spec with 409 response.
2026-04-13 18:32:07 -04:00
Anso 5b607de227 fix(api-tokens): harden scope enforcement and block sensitive endpoints (#228)
* fix(api-tokens): harden scope enforcement and add expiration support

- Fix deploy-only allowlist to match actual routes (deploy, down, restart,
  stop, start, update) instead of non-existent /up, /pull, /compose/* paths
- Block API tokens from auth-sensitive routes (password change, node token
  generation) that bypass scope enforcement middleware
- Add WebSocket scope enforcement: read-only/deploy-only tokens can only
  access logs and notifications, not host console or container exec
- Prevent API token self-replication: tokens cannot create, list, or revoke
  other tokens regardless of scope
- Map deploy-only tokens to admin role so they pass requireAdmin on deploy
  routes (scope middleware still restricts which endpoints they can reach)
- Add optional token expiration (30, 60, 90, 365 days or no expiry)
- Add token name length validation (max 100 characters)
- Surface fetchTokens errors in frontend instead of swallowing silently
- Fix docs: correct deploy-only scope description and GitHub Actions example

* fix(api-tokens): block all sensitive management endpoints from API tokens

User management, SSO configuration, node management, license management,
and console access are now human-session-only. Add comprehensive unit
tests for scope enforcement, blocked endpoints, expiration, and revocation.

* fix(api-tokens): fix TS18048 possibly-undefined in test
2026-03-28 22:14:22 -04:00