a094460580
ci-arm64-advisory / fast-checks (push) Waiting to run
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (push) Successful in 9s
Block internal-flavored paths / Block forbidden paths (push) Successful in 34s
CI / Python Lint & Test (push) Successful in 14s
CI / Detect changes (push) Successful in 19s
publish-workspace-server-image / build-and-push (push) Successful in 2m58s
E2E Chat / detect-changes (push) Successful in 37s
E2E API Smoke Test / detect-changes (push) Successful in 37s
E2E Staging Canvas (Playwright) / detect-changes (push) Successful in 31s
E2E Staging SaaS (full lifecycle) / pr-validate (push) Successful in 1m8s
Handlers Postgres Integration / detect-changes (push) Successful in 13s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (push) Successful in 2m2s
Harness Replays / detect-changes (push) Successful in 14s
publish-canvas-image / Build & push canvas image (push) Successful in 4m45s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (push) Successful in 9s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (push) Successful in 15s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (push) Has been skipped
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (push) Successful in 20s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (push) Successful in 8s
review-check-tests / review-check.sh regression tests (push) Successful in 16s
Secret scan / Scan diff for credential-shaped strings (push) Successful in 8s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (push) Successful in 1m31s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (push) Successful in 1m29s
Ops Scripts Tests / Ops scripts (unittest) (push) Successful in 1m37s
CI / Shellcheck (E2E scripts) (push) Successful in 42s
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (push) Successful in 5m20s
E2E API Smoke Test / E2E API Smoke Test (push) Successful in 2m6s
Harness Replays / Harness Replays (push) Successful in 9s
Sweep stale e2e-* orgs (staging) / Sweep e2e orgs (push) Successful in 8s
Sweep stale AWS Secrets Manager secrets / Sweep AWS Secrets Manager (push) Successful in 10s
CI / Platform (Go) (push) Successful in 5m35s
E2E Chat / E2E Chat (push) Successful in 4m7s
Handlers Postgres Integration / Handlers Postgres Integration (push) Successful in 1m48s
CI / Canvas (Next.js) (push) Successful in 6m59s
CI / all-required (push) Successful in 14m21s
CI / Canvas Deploy Reminder (push) Successful in 2s
publish-workspace-server-image / Production auto-deploy (push) Successful in 13m0s
Staging SaaS smoke (every 30 min) / Staging SaaS smoke (push) Successful in 4m57s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (push) Successful in 7m2s
Continuous synthetic E2E (staging) / Synthetic E2E against staging (push) Successful in 5m23s
test(e2e): add real staging image upload smoke (#1790) Remove legacy test-token references, keep production test-token unavailable, add explicit tenant-header diagnostics, and verify real staging image upload/download through the live tenant workflow.
118 lines
3.4 KiB
Markdown
118 lines
3.4 KiB
Markdown
# Token Management API
|
|
|
|
Workspace bearer tokens authenticate agents and API clients against the Molecule AI platform. Each token is scoped to a single workspace — a token from workspace A cannot access workspace B.
|
|
|
|
## Endpoints
|
|
|
|
All endpoints are behind `WorkspaceAuth` middleware — you need an existing valid token to manage tokens for a workspace. The first token is issued during workspace registration (`POST /registry/register`).
|
|
|
|
### List Tokens
|
|
|
|
```
|
|
GET /workspaces/:id/tokens
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
Returns non-revoked tokens for the workspace. Only metadata is returned — never the plaintext or hash.
|
|
|
|
```json
|
|
{
|
|
"tokens": [
|
|
{
|
|
"id": "uuid-of-token-row",
|
|
"prefix": "abc12345",
|
|
"created_at": "2026-04-16T12:00:00Z",
|
|
"last_used_at": "2026-04-16T15:30:00Z"
|
|
}
|
|
],
|
|
"count": 1
|
|
}
|
|
```
|
|
|
|
### Create Token
|
|
|
|
```
|
|
POST /workspaces/:id/tokens
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
Mints a new token. The plaintext is returned **exactly once** — save it immediately.
|
|
|
|
```json
|
|
{
|
|
"auth_token": "dGhpcyBpcyBhIHRlc3QgdG9rZW4...",
|
|
"workspace_id": "ws-uuid",
|
|
"message": "Save this token now — it cannot be retrieved again."
|
|
}
|
|
```
|
|
|
|
### Revoke Token
|
|
|
|
```
|
|
DELETE /workspaces/:id/tokens/:tokenId
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
Revokes a specific token by its database ID (from the List response). The token is immediately invalidated.
|
|
|
|
```json
|
|
{
|
|
"status": "revoked"
|
|
}
|
|
```
|
|
|
|
Returns 404 if the token doesn't exist, belongs to a different workspace, or is already revoked.
|
|
|
|
## Token Lifecycle
|
|
|
|
```
|
|
Issue (register or POST /tokens)
|
|
→ Active (used via Authorization: Bearer)
|
|
→ Revoked (DELETE /tokens/:id or workspace deleted)
|
|
```
|
|
|
|
- Tokens have no expiration — they remain valid until explicitly revoked or the workspace is deleted
|
|
- On workspace deletion, all tokens are automatically revoked
|
|
- Multiple tokens can exist simultaneously per workspace (for rotation)
|
|
|
|
## Token Rotation
|
|
|
|
To rotate credentials without downtime:
|
|
|
|
1. **Create** a new token: `POST /workspaces/:id/tokens`
|
|
2. **Update** your agent to use the new token
|
|
3. **Verify** the new token works (check `last_used_at` in List)
|
|
4. **Revoke** the old token: `DELETE /workspaces/:id/tokens/:oldTokenId`
|
|
|
|
## Security Properties
|
|
|
|
- **256-bit entropy**: Tokens are 32 random bytes, base64url-encoded (43 characters)
|
|
- **Hash-only storage**: Only `sha256(token)` is stored in the database — plaintext is never persisted
|
|
- **Workspace-scoped**: Token from workspace A cannot authenticate as workspace B
|
|
- **One-time display**: Plaintext returned only at creation — not recoverable from the database
|
|
- **Prefix for identification**: First 8 characters stored for log correlation without revealing the token
|
|
|
|
## Bootstrap: Getting Your First Token
|
|
|
|
The first token is issued during workspace registration:
|
|
|
|
```bash
|
|
# 1. Create workspace
|
|
curl -X POST http://localhost:8080/workspaces \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"name": "My Agent", "tier": 2}'
|
|
|
|
# 2. Register (returns auth_token)
|
|
curl -X POST http://localhost:8080/registry/register \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"workspace_id": "<id>", "url": "http://...", "agent_card": {...}}'
|
|
# Response: {"auth_token": "...", ...}
|
|
```
|
|
|
|
Tenant admins can mint a real workspace token through the production-safe admin route:
|
|
```bash
|
|
curl -X POST http://localhost:8080/admin/workspaces/<id>/tokens \
|
|
-H "Authorization: Bearer <ADMIN_TOKEN>"
|
|
# Response: {"auth_token": "...", "workspace_id": "..."}
|
|
```
|