O que é um cliente de serviço
- De propriedade do projeto. O projeto mantém o cliente quando a pessoa ou agente que o criou sai. As solicitações são cobradas como solicitações de agente, contra as mesmas solicitações incluídas.
- Somente leitura, Propor ou Gerenciar. Definido na criação, como um agente conectado. Somente leitura lista e lê; Propor também envia mídia e propõe alterações em playlists e telas para revisão de uma pessoa; Gerenciar também altera, publica e gasta. Quando o projeto exige revisão para alterações do agente, um cliente trabalha no máximo em Propor. Veja Somente leitura, Propor e Gerenciar.
- Capacidades. Qualquer um de
screens,content,playlists,advertising,reportseproject, dentro das próprias do criador. - Limitado. Um cliente de serviço nunca cria ou gerencia clientes de serviço, membros, convites ou acessos de agentes.
- Duas formas de entrar. Uma chave pública registrada (
private_key_jwt, o método recomendado) ou um segredo (client_secret_basic). Um cliente pode ter até dois de cada, para que você possa rotacionar sem tempo de inatividade.
Uma organização mantém até 50 clientes de serviço ativos em todos os seus projetos. IDs de cliente começam com scl_ e segredos com sr_cs_.
Criar uma chave
Gerar um par de chaves Ed25519 na máquina que executa o cliente. A chave privada nunca sai dela; screenRIG mantém apenas a metade pública.
openssl genpkey -algorithm ed25519 -out screenrig-client.pem
chmod 600 screenrig-client.pemChaves EC P-256 e chaves RSA de pelo menos 2048 bits também funcionam.
Criar o cliente
No dashboard. Abra Acesso e escolha Novo cliente de serviço. Nomeie, escolha Somente leitura, Propor ou Gerenciar, marque suas capacidades e cole a chave pública como JWK, ou escolha Gerar um segredo de cliente. Imprima o JWK público da sua chave com 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"})))'Com a CLI. O agente precisa de acesso Gerenciar para sua identidade com o escopo identity, de sua própria autorização Gerenciar com a capacidade project no projeto de destino e de uma pessoa responsável verificada que administre a organização desse projeto.
Para um agente recém-inscrito, a pessoa convidada deve primeiro aceitar o convite de inscrição. O acesso Gerenciar ao projeto, por si só, não autoriza alterações de credenciais.
--key-file aceita um arquivo JWK ou PEM e envia apenas sua parte pública:
screenrig service-client create --name "Nightly report" --access read \
--capability reports --key-file screenrig-client.pemPara usar um segredo em vez disso, adicione --secret-file PATH. O CLI grava o segredo nesse novo arquivo com modo 0600; é mostrado uma vez e nunca mais.
screenrig service-client show scl_ID lista as chaves do cliente (kid) e os IDs secretos. Uma chave sem seu próprio kid é conhecida pela sua impressão digital RFC 7638.
Clientes uploader
O agente de uma agência recebe um cliente limitado a playlists e pastas de mídia escolhidas: adicione --uploader --folder-id mfd_FOLDER[,mfd_FOLDER] --playlist-id pl_PLAYLIST[,pl_PLAYLIST] a service-client create. Um cliente uploader tem apenas as capacidades content e playlists e age em Propor, a menos que --access diga outra coisa. Com seu token, ele envia para suas pastas (screenrig media upload FILE --folder mfd_FOLDER) e adiciona, substitui e remove páginas de suas playlists (screenrig playlist page add|replace|remove); todo o resto responde outside_upload_scope, cujo upload_scope lista o que ele pode usar. O proprietário do projeto altera depois as pastas e playlists de um cliente; a mudança vale para o token já emitido. Veja Uploaders.
Obter um token de acesso
Peça ao endpoint de token um token com a concessão client_credentials. A resposta traz access_token, token_type: Bearer, expires_in (900 segundos) e scope. Não há token de atualização: peça novamente quando expirar.
Com um segredo, autentique com HTTP Basic. Um segredo no corpo da solicitação é recusado:
curl -s https://api.screenrig.ai/oauth/token \
-u "$SCREENRIG_CLIENT_ID:$SCREENRIG_CLIENT_SECRET" \
-d grant_type=client_credentialsCom uma chave, envie uma asserção de cliente assinada em vez de um segredo:
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"A asserção é um JWT assinado com sua chave privada:
| Campo | Valor |
|---|---|
Cabeçalho alg | EdDSA, ES256 ou RS256, correspondendo à chave |
Cabeçalho kid | O id da chave registrada |
iss e sub | O id do cliente |
aud | https://api.screenrig.ai ou https://api.screenrig.ai/oauth/token, como uma única string |
iat, exp | No máximo 5 minutos de intervalo |
jti | Um novo valor aleatório para cada afirmação; uma repetição é recusada |
Cabeçalhos jku, x5u, jwk e x5c são recusados.
Peça menos
Dois parâmetros opcionais restringem um token:
scope:access:readouaccess:proposedá ao token um nível inferior ao do cliente, e os nomes das capacidades mantêm apenas essas capacidades, por exemploscope=access:read reports. Um escopo além do cliente éinvalid_scope.resource:https://api.screenrig.ai/api, o padrão, para a API REST, ouhttps://api.screenrig.ai/mcppara o servidor remoto.
Chamar a API
Envie o token como bearer. Ele atua no próprio projeto do cliente:
curl -s https://api.screenrig.ai/api/screens \
-H "Authorization: Bearer $TOKEN"Node: um token em cache com uma chave
Este auxiliar assina a asserção com o crypto embutido do Node, mantém o token na memória e solicita um novo um minuto antes de expirar. Defina CLIENT_KEY_ID para o kid da chave em 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: um token em cache com um segredo
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"]Execute o CLI como cliente de serviço
Conecte o CLI como o cliente uma única vez, em uma configuração própria. A configuração registra o cliente e o arquivo que contém sua chave privada (recomendada) ou seu segredo, nunca a chave ou o segredo em si:
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 listUse --client-secret-file FILE em vez de --client-key-file para um segredo. Cada comando com essa configuração é executado como o cliente, sem etapa de login: ele lê o arquivo de chave ou de segredo a cada vez (por isso um arquivo rotacionado não exige novo login), mantém o token na memória e não grava mais nada na configuração. screenrig logout remove o cliente dela. Cada comando solicita seu próprio token, então um trabalho que executa mais de algumas centenas de comandos por hora é melhor atendido por um token reutilizado via API.
Rotacionar e revogar
| Para | Dashboard (Access, o cliente) | CLI |
|---|---|---|
| Adicionar uma segunda chave | Adicionar chave | screenrig service-client add-key scl_ID --key-file new.pem |
| Remover uma chave | Remover chave | screenrig service-client remove-key scl_ID --kid KID |
| Adicionar um segundo segredo | Gerar segredo | screenrig service-client add-secret scl_ID --secret-file new-secret |
| Remover um segredo | Remover segredo | screenrig service-client remove-secret scl_ID --secret-id css_ID |
| Alterar acesso ou capacidades | Acesse, depois Salve o acesso | Use o dashboard |
| Encerrar o cliente | Revogar | screenrig service-client revoke scl_ID --yes |
Para rotacionar sem tempo de inatividade, adicione a nova chave ou segredo, implante, depois remova o antigo. Tokens emitidos com chave ou segredo removidos param de funcionar em segundos, e um cliente mantém pelo menos uma chave ou segredo.
Restringir o acesso ou as capacidades de um cliente encerra seus tokens atuais; seu próximo token terá o novo acesso. Revogar é permanente: as chaves e segredos são excluídos, os tokens do cliente param de funcionar em segundos, e o registro permanece na página de Acesso para auditoria. Excluir o projeto revoga seus clientes.
Um cliente recebe tokens apenas enquanto seu projeto está ativo. Um projeto cancelado ou excluído não emite nenhum. Um projeto com crédito zero ainda emite tokens, e suas solicitações pagas respondem 402 payment_required, assim como um agente.
Erros e limites
| Resposta | Causa | Corrigir |
|---|---|---|
401 invalid_client | Segredo errado ou removido, uma asserção ruim ou repetida, um kid desconhecido, um cliente revogado ou um projeto cancelado ou excluído | Verifique as credenciais contra service-client show; envie um jti novo a cada vez |
400 invalid_scope | Um escopo além do acesso ou capacidades do cliente | Peça menos, ou mude o cliente |
400 invalid_target | Um resource diferente dos dois listados | Use o padrão ou o servidor remoto |
429 rate_limited | Mais de 600 tokens por cliente, ou 1.200 requisições de token por endereço, em uma hora | Reutilizar cada token até expirar |
503 temporarily_unavailable | Tente novamente em breve | Tentar novamente com recuo |
401 de /api | O token expirou ou foi revogado | Peça um novo token uma vez, depois pare |
403 forbidden ao gerenciar clientes | Falta autoridade de identidade, uma associação atual ao projeto com a capacidade project ou a função de administrador da organização da pessoa responsável | Peça à pessoa convidada que aceite a inscrição; confirme o acesso Gerenciar à identidade, a autorização Gerenciar do agente no projeto e a função de administrador da organização da pessoa responsável |
403 insufficient_access de /api | O nível de acesso do token não pode fazer essa alteração, ou o projeto exige revisão para alterações de agente | Use um cliente Manage; enquanto o projeto requer revisão, alterações em playlist e tela vão para Revisões |
402 payment_required de /api | O projeto não tem crédito para uma solicitação paga | Adicionar crédito ao projeto |