feat: personal API tokens
Tokens for scripts, made, listed and revoked on the account page. Each grants some of the person's own permissions at chosen places (a product of theirs, a catalog whose team they're on, or the hub for moderators); every check is the intersection with what they hold now. Tokens work only on routes that check a permission (session-only routes answer token_not_allowed), are shown once and stored as hashes, note their last use, and mark the audit entries of what they do. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
58a3e70a63
commit
2b88a52bcb
17 files changed
+4358
-98
No files matched your search
+19
-1
@@ -182,6 +182,24 @@ Bearer t5de_pat_<random>`; only the hash is stored; shown once. A token never ha
|
||||
check is the intersection, so losing a role takes it from tokens too. Listed and revocable in the account page;
|
||||
every use updates `last_used_at`; actions are audited as the person, marked "by token <name>".
|
||||
|
||||
Built (2026-11-05):
|
||||
- Made, listed and revoked on the account page (a website session only: a token can't make tokens). Each grant is
|
||||
a permission the person holds now at the hub (moderators and admins), a catalog whose team they're on, or a
|
||||
product of theirs (400 `not_held` otherwise). At most 20 active tokens, 50 grants, 365 days.
|
||||
- Usable on the routes that check a permission (product info, publish, locks, history; uploading new revisions of a
|
||||
product, `revision:upload`, and retracting them; a catalog's settings, key, team, members, invites and hosted
|
||||
products; blocks; assigning cases). Other routes refuse tokens (403 `token_not_allowed`), including making a new
|
||||
product, reports and the console. On those routes every check is the intersection, so a moderator's token granted
|
||||
`product:remove` hub-wide can block files but not suspend catalogs, and losing the role takes it from the token.
|
||||
- The audit log marks actions by token (`detail.token`: its name); making and revoking one is audited
|
||||
(`token.create`, `token.revoke`).
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| `GET /api/v1/account/tokens` | `{tokens: [{id, name, grants: [{permission, scope, name}], status (active, expired, revoked), createdAt, expiresAt, lastUsedAt, revokedAt}], scopes: [{scope, name, permissions}], maxDays}`: `scopes` is what a token could be given |
|
||||
| `POST /api/v1/account/tokens` | `{name, days, grants: [{permission, scope: {type: "hub"} or {type: "catalog" or "product", id}}]}` → 201 `{token, info}`; `token` is shown only here |
|
||||
| `DELETE /api/v1/account/tokens/:id` | revokes it → 204 |
|
||||
|
||||
```
|
||||
api_token(id, person, name, hash, grants jsonb, expires_at, last_used_at, created_at, revoked_at)
|
||||
```
|
||||
@@ -224,7 +242,7 @@ for meaning.
|
||||
| `POST /api/v1/cases/:id/decide` | `{decision: "resolve" or "decline", resolution}`: by those who may decide it (below); resolving carries out a rating review, removal request or deletion request; the opener is told |
|
||||
| `GET /api/v1/notifications?before=` | `{notifications: [{id, type, tags, data, at, read}], unread, before}`, newest first, 50 a page |
|
||||
| `POST /api/v1/notifications/read` | `{ids?}` (all if left out) → `{unread}` |
|
||||
| `GET/POST /api/v1/account/tokens`, `DELETE /api/v1/account/tokens/:id` | API tokens |
|
||||
| `GET/POST /api/v1/account/tokens`, `DELETE /api/v1/account/tokens/:id` | API tokens (above) |
|
||||
| `GET/PUT/DELETE /api/v1/roles/:scopeType/:scopeId/:person` | role assignments at a scope |
|
||||
| `POST /api/v1/console/view-as/:person` | (admins) start viewing as someone, read-only |
|
||||
|
||||
|
||||
Reference in new issue
Block a user