Documentação

Dê a servidores e trabalhos de CI seu próprio acesso

Um cliente de serviço é um servidor, script ou trabalho CI que trabalha em um projeto com suas próprias credenciais. Ele faz login com uma chave privada ou um segredo, recebe um token de acesso de 15 minutos e nunca precisa que uma pessoa aprove cada execução.

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, reports e project, 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.pem

Chaves 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.pem

Para 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_credentials

Com 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:

CampoValor
Cabeçalho algEdDSA, ES256 ou RS256, correspondendo à chave
Cabeçalho kidO id da chave registrada
iss e subO id do cliente
audhttps://api.screenrig.ai ou https://api.screenrig.ai/oauth/token, como uma única string
iat, expNo máximo 5 minutos de intervalo
jtiUm 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:read ou access:propose dá ao token um nível inferior ao do cliente, e os nomes das capacidades mantêm apenas essas capacidades, por exemplo scope=access:read reports. Um escopo além do cliente é invalid_scope.
  • resource: https://api.screenrig.ai/api, o padrão, para a API REST, ou https://api.screenrig.ai/mcp para 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 list

Use --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

ParaDashboard (Access, o cliente)CLI
Adicionar uma segunda chaveAdicionar chavescreenrig service-client add-key scl_ID --key-file new.pem
Remover uma chaveRemover chavescreenrig service-client remove-key scl_ID --kid KID
Adicionar um segundo segredoGerar segredoscreenrig service-client add-secret scl_ID --secret-file new-secret
Remover um segredoRemover segredoscreenrig service-client remove-secret scl_ID --secret-id css_ID
Alterar acesso ou capacidadesAcesse, depois Salve o acessoUse o dashboard
Encerrar o clienteRevogarscreenrig 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

RespostaCausaCorrigir
401 invalid_clientSegredo errado ou removido, uma asserção ruim ou repetida, um kid desconhecido, um cliente revogado ou um projeto cancelado ou excluídoVerifique as credenciais contra service-client show; envie um jti novo a cada vez
400 invalid_scopeUm escopo além do acesso ou capacidades do clientePeça menos, ou mude o cliente
400 invalid_targetUm resource diferente dos dois listadosUse o padrão ou o servidor remoto
429 rate_limitedMais de 600 tokens por cliente, ou 1.200 requisições de token por endereço, em uma horaReutilizar cada token até expirar
503 temporarily_unavailableTente novamente em breveTentar novamente com recuo
401 de /apiO token expirou ou foi revogadoPeça um novo token uma vez, depois pare
403 forbidden ao gerenciar clientesFalta 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ávelPeç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 /apiO nível de acesso do token não pode fazer essa alteração, ou o projeto exige revisão para alterações de agenteUse um cliente Manage; enquanto o projeto requer revisão, alterações em playlist e tela vão para Revisões
402 payment_required de /apiO projeto não tem crédito para uma solicitação pagaAdicionar crédito ao projeto