Autenticazione & chiavi API con scope

Chiavi API con scope (§1.5) - come creare le chiavi, il catalogo dei 27 scope, la gerarchia degli scope, la rotazione e l'endpoint /whoami per l'introspezione.

8 min letto
authenticationapi-keysscopes

Ogni richiesta all’API REST di CodeCourier si autentica con una chiave API di progetto con scope. Ogni chiave porta una allow-list esplicita di 1 o più scope (vedi il catalogo qui sotto). Il server rifiuta le richieste il cui scope non è presente sulla chiave con una risposta 403 SCOPE_DENIED nell’envelope di errore v2 (vedi errors).

Creare una chiave

Nel dashboard: Project Settings → API Keys → Generate. Dai un nome alla chiave (ad es. ci-pipeline) e spunta gli scope di cui ha bisogno. Il secret completo viene mostrato esattamente una volta - copialo subito nel tuo secrets manager.

Creazione programmatica:

curl

curl -X POST https://<your-deployment>.convex.site/api/v1/project/api-keys/generate \
  -H "Authorization: Bearer cc_live_<owner-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ci-pipeline",
    "scopes": ["runs:read", "runs:write", "workflows:read"]
  }'

TypeScript (fetch)

const res = await fetch(
  "https://<your-deployment>.convex.site/api/v1/project/api-keys/generate",
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.CC_OWNER_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "ci-pipeline",
      scopes: ["runs:read", "runs:write", "workflows:read"],
    }),
  }
);
const { data } = await res.json();
console.log(data.key); // cc_live_... - shown once

Python (requests)

import os, requests

res = requests.post(
    "https://<your-deployment>.convex.site/api/v1/project/api-keys/generate",
    headers={
        "Authorization": f"Bearer {os.environ['CC_OWNER_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "name": "ci-pipeline",
        "scopes": ["runs:read", "runs:write", "workflows:read"],
    },
)
print(res.json()["data"]["key"])  # shown once

Il catalogo dei 27 scope

Gli scope seguono un pattern resource:action. read implica list + get; write implica create + update + delete.

  • Projects: projects:read, projects:write
  • Workflows: workflows:read, workflows:write
  • Runs: runs:read, runs:write, runs:cancel
  • Personas: personas:read, personas:write
  • Issues: issues:read, issues:write
  • Sandboxes: sandboxes:read, sandboxes:write, sandboxes:exec
  • Contexts: contexts:read, contexts:write
  • Assets: assets:read, assets:write
  • Learnings: learnings:read, learnings:write
  • Cost Rates: cost-rates:read, cost-rates:write
  • Recurring Tasks: recurring-tasks:read, recurring-tasks:write
  • Webhooks: webhooks:read, webhooks:write
  • Team: team:read, team:write
  • Meta: * (accesso completo - da usare con parsimonia)

Gerarchia degli scope

  • * soddisfa ogni controllo di scope. Riservalo alle automazioni a livello di owner.
  • resource:write non implica resource:read. Concedi entrambi esplicitamente se il chiamante deve elencare prima di modificare.
  • Le chiavi legacy create prima di §1.5 sono state mantenute con * - fai audit e restringi tramite /whoami.

/whoami - Introspezione della chiave

GET /api/v1/whoami restituisce l’identità della chiave corrente e i suoi scope attivi. Utile per i controlli preliminari in CI.

curl

curl https://<your-deployment>.convex.site/api/v1/whoami \
  -H "Authorization: Bearer cc_live_..."

TypeScript

const r = await fetch(
  "https://<your-deployment>.convex.site/api/v1/whoami",
  { headers: { "Authorization": `Bearer ${key}` } }
);
const { data } = await r.json();
// { keyId, projectId, name, scopes: [...], createdAt, lastUsedAt }
if (!data.scopes.includes("runs:write")) throw new Error("scope missing");

Python

import requests
r = requests.get(
    "https://<your-deployment>.convex.site/api/v1/whoami",
    headers={"Authorization": f"Bearer {key}"},
)
data = r.json()["data"]
assert "runs:write" in data["scopes"], "scope missing"

Rotazione

  1. Genera una nuova chiave con lo stesso set di scope (o più ristretto).
  2. Distribuisci la nuova chiave nel tuo runtime; attendi un ciclo di richiesta completo.
  3. POST /api/v1/project/api-keys/revoke sulla vecchia chiave.
  4. Conferma che la chiave revocata restituisca 401 unauthorized.

Fai audit dell’utilizzo tramite lastUsedAt - le chiavi inattive da 90+ giorni dovrebbero essere revocate.

Correlati