mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
feat: personal access tokens with scoped bearer auth
Add cnp_ personal access tokens so the CLI, MCP server, and external agents authenticate without a browser session — the agent-first spine from ROADMAP2 phase A. Modeled on agent_auth_tokens: raw token shown once, only SHA-256 hash stored. - user_tokens table (goose migration + sqlc), name/prefix/scope/expiry, owner-scoped revoke, last_used_at stamping - middleware: cnp_ bearer resolution, read scope gates mutations with 403 SCOPE_INSUFFICIENT, non-admin scopes cap is_admin even for admin owners — a token never outranks its scope or its owner - GET/POST/DELETE /api/v1/user/tokens + OpenAPI + generated types - Settings UI: create with scope/expiry pickers, one-time token display with copy, list with last-used/expiry, confirm-revoke - docs: OPERATIONS.md section, README feature line Verified end-to-end against live Postgres: create/list/revoke, read scope blocks PUT (403), write PAT on admin owner blocked from admin routes (403), admin scope works, revoked token returns 401. Authored By: TDvorak <[email protected]>
This commit is contained in:
1 parent
f9ae5b962c
commit
e881747388
17 files changed
+1141
-30
No files matched your search
@@ -90,3 +90,30 @@ backend restart.
|
||||
once), copy the prefilled install command, revoke tokens when done. Agents
|
||||
register over `POST /api/agents/register` and heartbeat via
|
||||
`POST /api/agents/heartbeat`.
|
||||
|
||||
## Personal access tokens
|
||||
|
||||
**Settings → Personal Access Tokens**: issue `cnp_…` bearer tokens for the
|
||||
CLI, MCP server, and scripts — the same privileges as your account, bounded
|
||||
by scope. The raw token is shown once at creation; only its SHA-256 hash is
|
||||
stored.
|
||||
|
||||
Scopes:
|
||||
|
||||
- `read` — `GET`/`HEAD`/`OPTIONS` only; mutations return `403 SCOPE_INSUFFICIENT`.
|
||||
- `write` — everything except admin routes.
|
||||
- `admin` — full access; requires a platform admin account, and the token is
|
||||
still capped at the owner's privileges.
|
||||
|
||||
A token can never outrank its owner: non-admin scopes report
|
||||
`is_admin = false` even on admin accounts.
|
||||
|
||||
```
|
||||
GET /api/v1/user/tokens list (metadata only, never hashes)
|
||||
POST /api/v1/user/tokens {name, scope?, expires_in_days?} → raw token once
|
||||
DELETE /api/v1/user/tokens/:id revoke (owner-scoped)
|
||||
```
|
||||
|
||||
Use as `Authorization: Bearer cnp_…`, or `containr auth login cnp_…`.
|
||||
Revocation takes effect on the next request; `last_used_at` updates on each
|
||||
authenticated call.
|
||||
Reference in new issue
Block a user