Essayez FFxF : 10 $ offerts avec le code TRY10, sans carte de crédit

API

OIDC : la CI sans clé d'API

Appeler l'API depuis GitHub Actions ou GitLab CI avec un jeton OIDC de quelques minutes, sans clé stockée : fournisseur, liaison, conditions, refus.

Une clé d'API rangée dans les secrets d'un dépôt finit par se copier, fuiter dans un journal ou survivre à la personne qui l'a créée. Avec l'OIDC, votre intégration continue n'en stocke plus aucune : GitHub Actions, GitLab CI ou tout autre émetteur OIDC signe pour chaque tâche un jeton qui décrit le dépôt, la branche et le workflow, et FFxF l'accepte à la place d'une clé quand il correspond à une liaison que vous avez déclarée.

Le principe

  • Un fournisseur désigne un émetteur de confiance, par son URL. FFxF lui attribue une audience propre à votre compte, de la forme ffxf-oidc-….
  • Une liaison dit quels jetons de cet émetteur sont acceptés (des conditions sur leurs claims) et ce qu'ils peuvent faire (des portées, les mêmes que celles des clés).
  • La tâche demande un jeton pour votre audience et l'envoie en jeton porteur, exactement comme une clé : Authorization: Bearer <jeton>.

Un tel jeton vit quelques minutes (cinq chez GitHub). Il ne sert donc à rien à qui le trouve plus tard, et il n'y a rien à faire tourner.

1. Déclarer le fournisseur

Dans la console, Compte → OIDC, donnez un nom et l'URL de l'émetteur :

Intégration continueURL de l'émetteur
GitHub Actionshttps://token.actions.githubusercontent.com
GitLab.comhttps://gitlab.com
GitLab auto-hébergél'adresse de votre instance, en https://

Tout émetteur qui publie /.well-known/openid-configuration en HTTPS convient. L'URL doit être en https:// sur le port 443, sans adresse IP, identifiants, paramètres ni fragment, et le document de découverte doit annoncer exactement le même issuer. Dix fournisseurs par compte, un par émetteur.

L'audience se copie depuis la console et ne change jamais. Elle n'est pas un secret au sens d'une clé, puisqu'elle ne suffit pas à s'authentifier, mais ne la publiez pas sans raison : chez un émetteur partagé comme GitHub, elle est ce qui relie un jeton à votre compte plutôt qu'à un autre. Pour en changer, supprimez le fournisseur et recréez-le.

2. Écrire une liaison

Une liaison réunit des conditions sur les claims du jeton, des portées et une date d'expiration facultative. Toutes les conditions doivent correspondre :

  • * remplace une suite de caractères, ? un seul ; la comparaison respecte la casse et porte sur la valeur entière.
  • Un claim qui contient une liste correspond si l'un de ses éléments correspond ; un claim imbriqué se désigne avec un point.
  • Une condition sur sub est obligatoire, et elle doit nommer le propriétaire avant tout joker : repo:acme/* est accepté, repo:* et repo:acme* sont refusés (le second laisserait passer le compte acme-evil).

Le sub de GitHub Actions selon ce qui déclenche la tâche :

Déclencheursub
une brancherepo:acme/app:ref:refs/heads/main
une étiquetterepo:acme/app:ref:refs/tags/v1.4.0
un environnementrepo:acme/app:environment:production
une pull requestrepo:acme/app:pull_request

Chez GitLab, il prend la forme project_path:groupe/projet:ref_type:branch:ref:main. Ajoutez une seconde condition pour épingler le fichier du workflow, par exemple workflow_ref = acme/app/.github/workflows/deploy.yml@refs/heads/main : une autre tâche du même dépôt n'obtient alors rien. Vingt liaisons par fournisseur, vingt conditions par liaison.

3. Depuis GitHub Actions

La tâche a besoin de la permission id-token: write. Elle demande son jeton à GitHub pour votre audience, le masque dans le journal, puis appelle l'API :

yaml
permissions:
  contents: read
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      FFXF_OIDC_AUDIENCE: ffxf-oidc-…   # copiée depuis la console
    steps:
      - name: Jeton FFxF
        run: |
          token=$(curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
            "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$FFXF_OIDC_AUDIENCE" | jq -r .value)
          echo "::add-mask::$token"
          echo "FFXF_TOKEN=$token" >> "$GITHUB_ENV"

      - name: Lister les machines
        run: curl -sS https://api.ffxf.net/v1/vms -H "Authorization: Bearer $FFXF_TOKEN"

Le jeton expire cinq minutes après sa délivrance. Pour une tâche plus longue, redemandez-en un avant chaque étape qui appelle l'API : l'appel à GitHub est instantané.

4. Depuis GitLab CI

GitLab délivre le jeton directement dans une variable :

yaml
deploy:
  id_tokens:
    FFXF_TOKEN:
      aud: ffxf-oidc-…
  script:
    - curl -sS https://api.ffxf.net/v1/vms -H "Authorization: Bearer $FFXF_TOKEN"

Ce que FFxF vérifie

  • L'émetteur et l'audience du jeton désignent un de vos fournisseurs actifs ; sinon, rien n'est appelé et la réponse est 401.
  • La signature, avec les clés publiées par l'émetteur, gardées une heure en cache et relues dès qu'un jeton présente une clé inconnue. Seuls RS, ES et PS (256, 384, 512) sont acceptés.
  • iss à l'identique, aud, exp, iat et sub présents ; avec plusieurs audiences, azp doit valoir la vôtre.
  • Les liaisons actives du fournisseur, dans l'ordre : la première dont toutes les conditions correspondent, qui n'a pas expiré et qui porte la portée de l'appel, l'emporte.

Un jeton accepté agit ensuite comme une clé de votre compte : même quota, même crédit, mêmes suspensions, adresse électronique vérifiée exigée. Les seaux de débit et les clés d'idempotence sont comptés par liaison. Il n'y a pas de liste d'adresses autorisées : l'identité vient du jeton, pas de l'adresse d'appel.

Les refus

CodeHTTPCe qui s'est passé
invalid_oidc_token401jeton mal formé, mal signé, expiré, ou demandé pour une audience qu'aucun de vos fournisseurs ne déclare
oidc_claim_mismatch403jeton valide, mais aucune liaison active ne correspond à ses claims
binding_expired403la liaison qui correspond a passé sa date d'expiration
insufficient_scope403la liaison correspond mais n'a pas la portée de l'appel, nommée dans details.required
oidc_provider_unavailable503le document de découverte ou les clés de l'émetteur sont injoignables ; l'échec est retenu cinq minutes

invalid_oidc_token ne précise pas la cause : la réponse ne dit jamais si une audience ou une liaison existe dans un autre compte.

Avec le serveur MCP

Le serveur MCP relaie l'en-tête Authorization tel quel. Un agent IA qui tourne dans une tâche d'intégration continue peut donc se servir du jeton OIDC de la tâche au lieu d'une clé, avec les portées de la liaison et rien de plus.

Bonnes pratiques

  • Une liaison par usage : la lecture pour les pull requests, la création pour le déploiement depuis main, la destruction pour la seule tâche qui nettoie.
  • Épinglez workflow_ref, ou un environment protégé par une règle d'approbation, en plus de sub.
  • Donnez une date d'expiration aux liaisons d'essai.
  • Pour couper un accès tout de suite, désactivez la liaison ou le fournisseur : l'appel suivant est refusé.

Support & discussions

Questions techniques, retours d'incidents ou discussions d'infrastructure, l'équipe est présente sur Discord, Telegram, X, Instagram, Reddit et IRC.