Ce qu’est un client de service
- Appartient au projet. Le projet conserve le client lorsque la personne ou l’agent qui l’a créé part. Ses requêtes sont facturées comme des requêtes d’agent, contre les mêmes requêtes incluses.
- Lecture seule, Proposer ou Gérer. Défini à la création, comme un agent connecté. Lecture seule liste et lit ; Proposer téléverse aussi des médias et propose des modifications de playlists et d’écrans pour qu’une personne les révise ; Gérer modifie, publie et dépense aussi. Lorsque le projet exige une révision pour les modifications des agents, un client travaille au maximum en mode Proposer. Voir Lecture seule, Proposer et Gérer.
- Capacités. N’importe lequel de
screens,content,playlists,advertising,reportsetproject, dans celles du créateur. - Limité. Un client de service ne crée ni ne gère jamais de clients de service, membres, invitations ou connexions d’agents.
- Deux façons de se connecter. Une clé publique enregistrée (
private_key_jwt, la méthode recommandée) ou un secret (client_secret_basic). Un client peut en détenir jusqu’à deux de chaque, pour permettre une rotation sans interruption.
Une organisation contient jusqu’à 50 clients de service actifs, tous projets confondus. Les ID client commencent par scl_ et les secrets par sr_cs_.
Créer une clé
Générer une paire de clés Ed25519 sur la machine qui exécute le client. La clé privée ne la quitte jamais; screenRIG ne conserve que la moitié publique.
openssl genpkey -algorithm ed25519 -out screenrig-client.pem
chmod 600 screenrig-client.pemLes clés EC P-256 et RSA d’au moins 2048 bits fonctionnent aussi.
Créer le client
Dans le tableau de bord. Ouvrez Accès et choisissez Nouveau client de service. Nommez-le, choisissez Lecture seule, Proposer ou Gérer, cochez ses capacités, et collez la clé publique au format JWK, ou choisissez Générer un secret client. Imprimez la clé publique JWK avec Node :
node -e 'const c=require("node:crypto");const k=c.createPublicKey(require("node:fs").readFileSync("screenrig-client.pem"));console.log(JSON.stringify(k.export({format:"jwk"})))'Avec la CLI. L’agent a besoin de l’accès Gestion pour son identité avec la portée identity, de sa propre autorisation Gestion avec la capacité project sur le projet ciblé, et d’une personne responsable vérifiée qui administre l’organisation de ce projet.
Pour un agent nouvellement inscrit, la personne invitée doit d’abord accepter l’invitation d’inscription. L’accès Gestion au projet ne suffit pas à autoriser les modifications d’identifiants.
--key-file accepte un fichier JWK ou PEM et n’envoie que sa partie publique :
screenrig service-client create --name "Nightly report" --access read \
--capability reports --key-file screenrig-client.pemPour utiliser un secret à la place, ajoutez --secret-file PATH. Le CLI écrit le secret dans ce nouveau fichier avec le mode 0600 ; il est affiché une fois et jamais plus.
screenrig service-client show scl_ID liste les identifiants clés du client (kid) et les identifiants secrets. Une clé sans son propre kid est connue par son empreinte RFC 7638.
Clients téléverseurs
L’agent d’une agence reçoit un client limité à des listes de lecture et des dossiers de médias choisis : ajoutez --uploader --folder-id mfd_FOLDER[,mfd_FOLDER] --playlist-id pl_PLAYLIST[,pl_PLAYLIST] à service-client create. Un client téléverseur détient uniquement les capacités content et playlists et agit en Proposer sauf si --access dit autre chose. Avec son jeton, il téléverse dans ses dossiers (screenrig media upload FILE --folder mfd_FOLDER) et ajoute, remplace et supprime des pages de ses listes de lecture (screenrig playlist page add|replace|remove) ; tout le reste répond outside_upload_scope, dont upload_scope énumère ce qu’il peut utiliser. Le propriétaire du projet modifie plus tard les dossiers et les listes de lecture d’un client ; le changement s’applique au jeton déjà émis. Voir Téléverseurs.
Obtenir un jeton d’accès
Demander au point de terminaison du jeton un jeton avec l’octroi client_credentials. La réponse contient access_token, token_type: Bearer, expires_in (900 secondes) et scope. Il n’y a pas de jeton de rafraîchissement : redemandez quand il expire.
Avec un secret, authentifiez-vous avec HTTP Basic. Un secret dans le corps de la requête est refusé :
curl -s https://api.screenrig.ai/oauth/token \
-u "$SCREENRIG_CLIENT_ID:$SCREENRIG_CLIENT_SECRET" \
-d grant_type=client_credentialsAvec une clé, envoyez une assertion client signée au lieu d’un secret :
curl -s https://api.screenrig.ai/oauth/token \
-d grant_type=client_credentials \
-d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
-d client_assertion="$ASSERTION"L’assertion est un JWT signé avec votre clé privée :
| Champ | Valeur |
|---|---|
En-tête alg | EdDSA, ES256 ou RS256, correspondant à la clé |
En-tête kid | L’identifiant de la clé enregistrée |
iss et sub | L’identifiant client |
aud | https://api.screenrig.ai ou https://api.screenrig.ai/oauth/token, en une seule chaîne |
iat, exp | Au maximum 5 minutes d’intervalle |
jti | Une nouvelle valeur aléatoire pour chaque assertion ; une répétition est refusée |
Les en-têtes jku, x5u, jwk et x5c sont refusés.
Demander moins
Deux paramètres optionnels restreignent un jeton :
scope:access:readouaccess:proposedonne au jeton un niveau inférieur à celui du client, et les noms de capacités ne conservent que ces capacités, par exemplescope=access:read reports. Un scope au-delà de celui du client estinvalid_scope.resource:https://api.screenrig.ai/api, par défaut, pour l’API REST, ouhttps://api.screenrig.ai/mcppour le serveur distant.
Appeler l’API
Envoyez le jeton en tant que porteur. Il agit sur le projet propre au client :
curl -s https://api.screenrig.ai/api/screens \
-H "Authorization: Bearer $TOKEN"Node : un jeton mis en cache avec une clé
Cet assistant signe l’assertion avec la crypto intégrée de Node, garde le jeton en mémoire et en demande un nouveau une minute avant son expiration. Définissez CLIENT_KEY_ID sur la kid de la clé depuis service-client show :
import { createPrivateKey, randomUUID, sign } from "node:crypto";
import { readFileSync } from "node:fs";
const ISSUER = "https://api.screenrig.ai";
const { SCREENRIG_CLIENT_ID: clientId, SCREENRIG_CLIENT_KEY_FILE: keyFile, CLIENT_KEY_ID: kid } = process.env;
const key = createPrivateKey(readFileSync(keyFile));
const part = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
let cached;
function assertion() {
const now = Math.floor(Date.now() / 1000);
const input = `${part({ alg: "EdDSA", typ: "JWT", kid })}.${part({ iss: clientId, sub: clientId, aud: ISSUER, iat: now, exp: now + 60, jti: randomUUID() })}`;
return `${input}.${sign(null, Buffer.from(input), key).toString("base64url")}`;
}
export async function accessToken() {
if (cached && cached.expiresAt - 60_000 > Date.now()) return cached.token;
const response = await fetch(`${ISSUER}/oauth/token`, {
method: "POST",
body: new URLSearchParams({
grant_type: "client_credentials",
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: assertion(),
}),
});
const body = await response.json();
if (!response.ok) throw new Error(`screenRIG token request refused: ${body.error}: ${body.error_description ?? ""}`);
cached = { token: body.access_token, expiresAt: Date.now() + body.expires_in * 1000 };
return cached.token;
}Python : un jeton mis en cache avec un secret
import base64, json, os, time, urllib.parse, urllib.request
ISSUER = "https://api.screenrig.ai"
_cached = {"token": None, "expires_at": 0.0}
def access_token() -> str:
if _cached["token"] and _cached["expires_at"] - 60 > time.time():
return _cached["token"]
pair = f'{os.environ["SCREENRIG_CLIENT_ID"]}:{os.environ["SCREENRIG_CLIENT_SECRET"]}'
request = urllib.request.Request(
f"{ISSUER}/oauth/token",
data=urllib.parse.urlencode({"grant_type": "client_credentials"}).encode(),
headers={"Authorization": "Basic " + base64.b64encode(pair.encode()).decode()},
)
with urllib.request.urlopen(request, timeout=10) as response:
body = json.load(response)
_cached.update(token=body["access_token"], expires_at=time.time() + body["expires_in"])
return body["access_token"]Exécuter le CLI en tant que client de service
Connectez le CLI en tant que ce client une seule fois, dans une configuration qui lui est propre. La configuration enregistre le client et le fichier qui contient sa clé privée (recommandée) ou son secret, jamais la clé ou le secret eux-mêmes :
screenrig --config /run/screenrig/ci.json login --client-id scl_ID \
--client-key-file /run/secrets/screenrig-client.pem
screenrig --config /run/screenrig/ci.json screen listUtilisez --client-secret-file FILE au lieu de --client-key-file pour un secret. Chaque commande qui utilise cette configuration s’exécute en tant que ce client sans étape de connexion : elle lit le fichier de clé ou de secret à chaque fois (un fichier renouvelé n’exige donc aucune nouvelle connexion), garde le jeton en mémoire et n’écrit rien d’autre dans la configuration. screenrig logout retire le client de celle-ci. Chaque commande demande son propre jeton, donc un travail qui exécute plus de quelques centaines de commandes par heure est mieux servi par un jeton réutilisé via l’API.
Faire pivoter et révoquer
| À | Tableau de bord (Accès, le client) | CLI |
|---|---|---|
| Ajouter une seconde clé | Ajouter une clé | screenrig service-client add-key scl_ID --key-file new.pem |
| Supprimer une clé | Supprimer la clé | screenrig service-client remove-key scl_ID --kid KID |
| Ajouter un second secret | Générer un secret | screenrig service-client add-secret scl_ID --secret-file new-secret |
| Supprimer un secret | Supprimer le secret | screenrig service-client remove-secret scl_ID --secret-id css_ID |
| Modifier l’accès ou les capacités | Accès, puis Enregistrer l’accès | Utilisez le tableau de bord |
| Terminer le client | Révoquer | screenrig service-client revoke scl_ID --yes |
Pour faire une rotation sans temps d’arrêt, ajoutez la nouvelle clé ou secret, déployez-le, puis retirez l’ancien. Les jetons émis avec une clé ou un secret retiré cessent de fonctionner en quelques secondes, et un client conserve au moins une clé ou un secret.
Restreindre l’accès ou les capacités d’un client annule ses jetons actuels ; son prochain jeton porte le nouvel accès. La révocation est permanente : les clés et secrets sont supprimés, les jetons du client cessent de fonctionner en quelques secondes, et l’enregistrement reste visible sur la page Accès pour audit. Supprimer le projet révoque ses clients.
Un client obtient des jetons uniquement tant que son projet est actif. Un projet annulé ou supprimé n’en émet aucun. Un projet à crédit nul émet toujours des jetons, et ses requêtes payantes répondent 402 payment_required, comme le fait un agent.
Erreurs et limites
| Réponse | Cause | Corriger |
|---|---|---|
401 invalid_client | Secret incorrect ou supprimé, assertion mauvaise ou répétée, kid inconnu, client révoqué, ou projet annulé ou supprimé | Vérifiez les identifiants par rapport à service-client show ; envoyez un jti neuf à chaque fois |
400 invalid_scope | Un périmètre au-delà de l’accès ou des capacités du client | Demander moins, ou changer de client |
400 invalid_target | Un resource autre que les deux listés | Utilisez le serveur par défaut ou distant |
429 rate_limited | Plus de 600 jetons par client, ou 1 200 requêtes de jetons par adresse, en une heure | Réutiliser chaque jeton jusqu’à son expiration |
503 temporarily_unavailable | Réessayez sous peu | Réessayer avec temporisation |
401 de /api | Le jeton a expiré ou a été révoqué | Demander un nouveau jeton une fois, puis arrêter |
403 forbidden lors de la gestion des clients | Il manque l’autorité sur l’identité, une appartenance actuelle au projet avec la capacité project, ou le rôle d’administrateur de l’organisation de la personne responsable | Faire accepter l’invitation d’inscription par la personne invitée; confirmer l’accès Gestion à l’identité, l’autorisation Gestion de l’agent sur le projet et le rôle d’administrateur de l’organisation de la personne responsable |
403 insufficient_access de /api | Le niveau d’accès du jeton ne peut pas effectuer ce changement, ou le projet exige une révision pour les modifications d’agent | Utilisez un client Manage ; pendant que le projet est en révision, les modifications de playlist et d’écran vont dans Révisions |
402 payment_required de /api | Le projet n’a pas de crédit pour une demande payante | Ajouter du crédit au projet |