Skip to content

feat(api): self-serve API token management in Settings → Account#5750

Open
holden093 wants to merge 3 commits into
odysseus-dev:devfrom
holden093:feat/self-serve-api-tokens-reopen
Open

feat(api): self-serve API token management in Settings → Account#5750
holden093 wants to merge 3 commits into
odysseus-dev:devfrom
holden093:feat/self-serve-api-tokens-reopen

Conversation

@holden093

Copy link
Copy Markdown
Contributor

Note: Re-opening of #4659, auto-closed 2026-07-23 during the odysseus-dev org transfer (the dev history rewrite force-closed all open PRs). Same feature, cleanly rebased onto the current dev. Original review thread: #4659.

Summary

Adds a self-serve API token management flow so regular users can create, list, and revoke their own API tokens from the Account settings page — no admin required. Previously, POST /api/tokens was gated behind require_admin, and the only user-facing token creation happened as a side effect of setting up a Codex/Claude agent in the Integrations panel.

Three cookie-only /api/tokens/self endpoints (GET, POST, DELETE) let users manage their own tokens with owner-scoping enforced server-side. A new "Personal API Tokens" card in Settings → Account provides a UI with scope checkboxes, one-time token reveal + copy, and revoke with confirmation.

Security: require_user rejects bearer tokens (403 — no token bootstrapping). Owner is forced server-side from the session, never trusted from the client. Tokens are isolated per-user; one user cannot see, modify, or delete another's tokens.

Target branch

  • This PR targets dev, not main.

Linked Issue

Fixes #4658

Type of Change

  • New feature (non-breaking — adds new behaviour)

Checklist

  • I searched open issues and open PRs — this is not a duplicate.
  • This PR targets dev
  • My changes are limited to the scope described above — no unrelated refactors or whitespace changes mixed in.
  • I actually ran the app and verified the change works end-to-end. Tested with live token creation, scope verification via /api/codex/capabilities, and revocation on a production instance.

How to Test

  1. Log in as a non-admin user.
  2. Go to Settings → Account. Scroll to the "Personal API Tokens" card.
  3. Click + New Token, enter a name, check several scopes (e.g. Email, Todos, Documents), click Create.
  4. Verify the token is shown once with a copy button — copy it.
  5. Use the token against /api/companion/ping and /api/codex/capabilities to confirm it works and has the correct scopes.
  6. Refresh the Account page — the token should appear in the list with correct prefix and scopes.
  7. Click the trash icon to revoke it — confirm the dialog. Verify the token no longer works (401 Invalid API token).
  8. Repeat as an admin user — same flow, no regression on existing admin /api/tokens endpoints.
  9. Verify a bearer token cannot call /api/tokens/self (returns 403).

Visual / UI changes

  • Screenshots attached below.
  • Style match: reuses existing CSS variables (--bg, --fg, --border, --accent, --red) and button classes (admin-btn-add, admin-btn-delete, admin-btn-sm). No new colors or spacing units.
  • No new component patterns: follows the same card layout as the Password and 2FA cards in the Account tab.
  • Monospaced font for the revealed token code block.
  • Dark theme default, no hard-coded light-mode values.

Screenshots

Schermata del 2026-06-20 22-07-48 - TOKEN Schermata del 2026-06-20 22-07-29 - TOKEN Schermata del 2026-06-20 22-07-13 - TOKEN

Backend:
- GET /api/tokens/self — list current user's own tokens
- POST /api/tokens/self — create a token (cookie-only, owner forced)
- DELETE /api/tokens/self/{id} — revoke own token
- All self-serve endpoints use require_user (reject bearer tokens)

Frontend:
- 'Personal API Tokens' card in Settings → Account tab
- List existing tokens with name, prefix, scopes, last used, revoke button
- Create flow with scope checkboxes, one-time token reveal + copy
- Styled confirm dialog before revocation
…rve tests

- Return 404 (not 403) when a user tries to delete another user's token
  via the self-serve endpoint, closing an information leak.
- Add tests covering: bearer-token rejection (403), owner isolation
  (list only returns own tokens), not-yours delete → 404, unknown scope
  rejection (400), and owner forced from session on create.
@github-actions github-actions Bot added the ready for review Description complete — ready for maintainer review label Jul 25, 2026
@holden093

Copy link
Copy Markdown
Contributor Author

Heads-up: the failing Python tests (pytest) check is a pre-existing dev bug, not this PR

The pytest job fails at test collection, before any of this PR's changes are exercised:

NameError: name 'Any' is not defined

Root cause is on dev itself: src/agent_loop.py annotates with dict[str, Any] (lines 1503 and 1877) but only imports AsyncGenerator, List, Dict, Optional, Set — no Any, and no from __future__ import annotations. So importing the module raises NameError, which breaks collection for every test that transitively imports agent_loop (hence the repo-wide failure).

This PR doesn't touch agent_loop.py; every other check (syntax, CodeQL, lint, security scans) passes here, and this PR's own tests pass locally.

One-line fix (add Any to the typing import) is included in #5751. Once dev carries that fix, this PR's pytest will go green with no changes needed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review Description complete — ready for maintainer review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(auth): self-serve API token management for regular users in Settings → Account

1 participant