Cos’è un cliente di servizio
- Proprietà del progetto. Il progetto mantiene il cliente quando la persona o l’agente che l’ha creato se ne va. Le sue richieste sono fatturate come richieste agente, contro gli stessi richieste incluse.
- Solo lettura, Proponi o Gestisci. Impostato alla creazione, come un agente connesso. Solo lettura elenca e legge; Proponi carica anche contenuti multimediali e propone modifiche a playlist e schermo per la revisione di una persona; Gestisci modifica, pubblica e spende. Quando il progetto richiede revisione per modifiche degli agenti, un client lavora al massimo in Proponi. Vedi Solo lettura, Proponi e Gestisci.
- Capacità. Qualsiasi di
screens,content,playlists,advertising,reportseproject, entro quelle del creatore. - Limitato. Un client di servizio non crea né gestisce client di servizio, persone, inviti o accessi agenti.
- Due modi per accedere. Una chiave pubblica registrata (
private_key_jwt, il metodo consigliato) o un segreto (client_secret_basic). Un client può avere fino a due di ciascuno, così puoi ruotare senza interruzioni.
Un’organizzazione contiene fino a 50 client servizio live in tutti i suoi progetti. Gli id client iniziano con scl_ e i segreti con sr_cs_.
Crea una chiave
Genera una coppia di chiavi Ed25519 sulla macchina che esegue il client. La chiave privata non lascia mai la macchina; screenRIG conserva solo la metà pubblica.
openssl genpkey -algorithm ed25519 -out screenrig-client.pem
chmod 600 screenrig-client.pemFunzionano anche chiavi EC P-256 e RSA di almeno 2048 bit.
Crea il client
Nel dashboard. Apri Accesso e scegli Nuovo client di servizio. Dagli un nome, scegli Solo lettura, Proponi o Gestisci, seleziona le sue capacità e incolla la chiave pubblica come JWK, oppure scegli Genera un segreto client. Stampa il JWK pubblico della tua chiave con 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"})))'Con la CLI. L’agente deve avere accesso Gestisci per la propria identità con l’ambito identity, una propria autorizzazione Gestisci con la capacità project sul progetto di destinazione e una persona responsabile verificata che amministri l’organizzazione di quel progetto.
Per un agente appena registrato, la persona invitata deve prima accettare l’invito di registrazione. Il solo accesso Gestisci al progetto non autorizza modifiche alle credenziali.
--key-file accetta un file JWK o PEM e invia solo la sua parte pubblica:
screenrig service-client create --name "Nightly report" --access read \
--capability reports --key-file screenrig-client.pemPer usare invece un segreto, aggiungi --secret-file PATH. La CLI scrive il segreto in quel nuovo file con modalità 0600; viene mostrato una volta e mai più.
screenrig service-client show scl_ID elenca gli ID chiave del client (kid) e gli ID segreti. Una chiave senza il proprio kid è nota tramite il suo thumbprint RFC 7638.
Client uploader
L’agente di un’agenzia riceve un client limitato a playlist e cartelle media scelte: aggiungi --uploader --folder-id mfd_FOLDER[,mfd_FOLDER] --playlist-id pl_PLAYLIST[,pl_PLAYLIST] a service-client create. Un client uploader ha solo le capacità content e playlists e agisce su Proponi a meno che --access non dica altro. Con il suo token carica nelle sue cartelle (screenrig media upload FILE --folder mfd_FOLDER) e aggiunge, sostituisce e rimuove pagine delle sue playlist (screenrig playlist page add|replace|remove); tutto il resto risponde outside_upload_scope, il cui upload_scope elenca ciò che può usare. Il proprietario del progetto modifica in seguito le cartelle e le playlist di un client; la modifica si applica al token già emesso. Vedi Uploader.
Ottieni un token di accesso
Chiedi all’endpoint token un token con la concessione client_credentials. La risposta contiene access_token, token_type: Bearer, expires_in (900 secondi) e scope. Non esiste un token di aggiornamento: chiedi di nuovo alla scadenza.
Con un segreto, autentica con HTTP Basic. Un segreto nel corpo della richiesta è rifiutato:
curl -s https://api.screenrig.ai/oauth/token \
-u "$SCREENRIG_CLIENT_ID:$SCREENRIG_CLIENT_SECRET" \
-d grant_type=client_credentialsCon una chiave, invia un’asserzione client firmata invece di un segreto:
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’asserzione è un JWT firmato con la tua chiave privata:
| Campo | Valore |
|---|---|
Intestazione alg | EdDSA, ES256 o RS256, corrispondenti alla chiave |
Intestazione kid | L’ID della chiave registrata |
iss e sub | L’id client |
aud | https://api.screenrig.ai o https://api.screenrig.ai/oauth/token, come una stringa |
iat, exp | Al massimo 5 minuti di distanza |
jti | Un nuovo valore casuale per ogni asserzione; una ripetizione è rifiutata |
Le intestazioni jku, x5u, jwk e x5c sono rifiutate.
Chiedi meno
Due parametri opzionali restringono un token:
scope:access:readoaccess:proposeassegna al token un livello inferiore rispetto al client, e i nomi delle capacità mantengono solo quelle capacità, per esempioscope=access:read reports. Un ambito oltre il client èinvalid_scope.resource:https://api.screenrig.ai/api, predefinito, per l’API REST, ohttps://api.screenrig.ai/mcpper il server remoto.
Chiamare l’API
Invia il token come bearer. Agisce sul progetto del client:
curl -s https://api.screenrig.ai/api/screens \
-H "Authorization: Bearer $TOKEN"Node: un token memorizzato nella cache con una chiave
Questo helper firma l’asserzione con la crittografia integrata di Node, mantiene il token in memoria e ne richiede uno nuovo un minuto prima della scadenza. Imposta CLIENT_KEY_ID alla kid della chiave da 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 token memorizzato nella cache con un segreto
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"]Esegui la CLI come service client
Accedi con la CLI come client una sola volta, in una configurazione tutta sua. La configurazione registra il client e il file che contiene la sua chiave privata (consigliata) o il suo segreto, mai la chiave o il segreto stessi:
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 listUsa --client-secret-file FILE al posto di --client-key-file per un segreto. Ogni comando con quella configurazione viene eseguito come il client, senza passaggio di accesso: legge ogni volta il file della chiave o del segreto (quindi un file ruotato non richiede un nuovo accesso), mantiene il token in memoria e non scrive nient’altro nella configurazione. screenrig logout rimuove il client da essa. Ogni comando richiede il proprio token, quindi un lavoro che esegue più di qualche centinaio di comandi all’ora è meglio servito da un token riutilizzato tramite API.
Ruota e revoca
| A | Dashboard (Accesso, il cliente) | CLI |
|---|---|---|
| Aggiungi una seconda chiave | Aggiungi chiave | screenrig service-client add-key scl_ID --key-file new.pem |
| Rimuovi una chiave | Rimuovi chiave | screenrig service-client remove-key scl_ID --kid KID |
| Aggiungi un secondo segreto | Genera segreto | screenrig service-client add-secret scl_ID --secret-file new-secret |
| Rimuovi un segreto | Rimuovi segreto | screenrig service-client remove-secret scl_ID --secret-id css_ID |
| Modifica accesso o capacità | Accedi, poi Salva accesso | Usa il dashboard |
| Termina il client | Revoca | screenrig service-client revoke scl_ID --yes |
Per ruotare senza inattività, aggiungi la nuova chiave o segreto, distribuiscilo, poi rimuovi il vecchio. I token emessi con una chiave o segreto rimosso smettono di funzionare in pochi secondi, e un client mantiene almeno una chiave o segreto.
Limitare l’accesso o le capacità di un cliente invalida i suoi token attuali; il suo prossimo token avrà il nuovo accesso. La revoca è permanente: chiavi e segreti vengono eliminati, i token del cliente smettono di funzionare entro pochi secondi e il record rimane nella pagina Accesso per audit. Eliminare il progetto revoca i suoi clienti.
Un client ottiene token solo mentre il suo progetto è attivo. Un progetto annullato o eliminato non ne emette. Un progetto a credito zero emette comunque token, e le sue richieste a pagamento rispondono 402 payment_required, come fa un agente.
Errori e limiti
| Risposta | Causa | Correggi |
|---|---|---|
401 invalid_client | Segreto errato o rimosso, asserzione errata o ripetuta, kid sconosciuto, client revocato o progetto annullato o eliminato | Controlla le credenziali rispetto a service-client show; invia un nuovo jti ogni volta |
400 invalid_scope | Un ambito oltre l’accesso o le capacità del client | Chiedi meno o cambia il client |
400 invalid_target | Un resource diverso dai due elencati | Usa il server predefinito o quello remoto |
429 rate_limited | Più di 600 token per client, o 1.200 richieste token per indirizzo, in un’ora | Riutilizza ogni token fino alla sua scadenza |
503 temporarily_unavailable | Riprova tra poco | Ritenta con backoff |
401 da /api | Il token è scaduto o è stato revocato | Chiedi un nuovo token una volta, poi interrompi |
403 forbidden durante la gestione dei client | Manca l’autorità sull’identità, un’appartenenza attuale al progetto con la capacità project o il ruolo di amministratore dell’organizzazione della persona responsabile | Chiedi alla persona invitata di accettare la registrazione; verifica l’accesso Gestisci all’identità, l’autorizzazione Gestisci dell’agente sul progetto e il ruolo di amministratore dell’organizzazione della persona responsabile |
403 insufficient_access da /api | Il livello di accesso del token non può effettuare quella modifica, oppure il progetto richiede revisione per modifiche agent | Usa un client Manage; mentre il progetto richiede revisione, modifiche a playlist e schermo vanno a Revisioni |
402 payment_required da /api | Il progetto non ha credito per una richiesta a pagamento | Aggiungi credito al progetto |