Authentification & clés API à scope

Clés API à scope (§1.5) - comment créer des clés, le catalogue des 27 scopes, la hiérarchie des scopes, la rotation et l'endpoint /whoami pour l'introspection.

8 min lire
authenticationapi-keysscopes

Chaque requête vers l’API REST de CodeCourier s’authentifie avec une clé API de projet à scope. Chaque clé porte une liste blanche explicite de 1 scope ou plus (voir le catalogue ci-dessous). Le serveur rejette les requêtes dont le scope n’est pas présent sur la clé avec une réponse 403 SCOPE_DENIED dans l’enveloppe d’erreur v2 (voir errors).

Créer une clé

Dans le dashboard : Project Settings → API Keys → Generate. Nommez la clé (p. ex. ci-pipeline) et cochez les scopes dont elle a besoin. Le secret complet s’affiche exactement une fois - copiez-le immédiatement dans votre secrets manager.

Création programmatique :

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

Le catalogue des 27 scopes

Les scopes suivent un pattern resource:action. read implique list + get ; write implique 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 : * (accès complet - à utiliser avec parcimonie)

Hiérarchie des scopes

  • * satisfait toute vérification de scope. Réservez-le aux automatisations au niveau owner.
  • resource:write n’implique pas resource:read. Accordez les deux explicitement si l’appelant doit lister avant de muter.
  • Les clés héritées créées avant §1.5 ont été maintenues avec * - auditez et resserrez via /whoami.

/whoami - Introspection de clé

GET /api/v1/whoami renvoie l’identité de la clé courante et ses scopes actifs. Utile pour les vérifications préalables en 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"

Rotation

  1. Générez une nouvelle clé avec le même set de scopes (ou plus restreint).
  2. Déployez la nouvelle clé dans votre runtime ; attendez un cycle de requête complet.
  3. POST /api/v1/project/api-keys/revoke sur l’ancienne clé.
  4. Confirmez que la clé révoquée renvoie 401 unauthorized.

Auditez l’usage via lastUsedAt - les clés inactives depuis 90+ jours devraient être révoquées.

Liens connexes