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 continue | URL de l'émetteur |
|---|---|
| GitHub Actions | https://token.actions.githubusercontent.com |
| GitLab.com | https://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
subest obligatoire, et elle doit nommer le propriétaire avant tout joker :repo:acme/*est accepté,repo:*etrepo:acme*sont refusés (le second laisserait passer le compteacme-evil).
Le sub de GitHub Actions selon ce qui déclenche la tâche :
| Déclencheur | sub |
|---|---|
| une branche | repo:acme/app:ref:refs/heads/main |
| une étiquette | repo:acme/app:ref:refs/tags/v1.4.0 |
| un environnement | repo:acme/app:environment:production |
| une pull request | repo: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 :
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 :
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,iatetsubprésents ; avec plusieurs audiences,azpdoit 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
| Code | HTTP | Ce qui s'est passé |
|---|---|---|
invalid_oidc_token | 401 | jeton mal formé, mal signé, expiré, ou demandé pour une audience qu'aucun de vos fournisseurs ne déclare |
oidc_claim_mismatch | 403 | jeton valide, mais aucune liaison active ne correspond à ses claims |
binding_expired | 403 | la liaison qui correspond a passé sa date d'expiration |
insufficient_scope | 403 | la liaison correspond mais n'a pas la portée de l'appel, nommée dans details.required |
oidc_provider_unavailable | 503 | le 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 unenvironmentprotégé par une règle d'approbation, en plus desub. - 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é.