サービスクライアントとは何か
- プロジェクトが所有しています。 作成者である人物やエージェントが離れても、クライアントはプロジェクトが保持します。リクエストはエージェントリクエストとして請求され、同じ含まれるリクエストに対して行われます。
- 読み取り専用、提案、管理。 作成時に設定され、サインインしたエージェントのように動作します。読み取り専用はリストの表示と読み取りのみ;提案はメディアのアップロードやプレイリスト・スクリーンの変更提案を行い、担当者が確認します;管理は変更、公開、支出も行います。プロジェクトでエージェントの変更にレビューが必要な場合、クライアントは最大で提案モードで動作します。読み取り専用、提案、管理を参照してください。
- 機能。 作成者自身の範囲内で
screens、content、playlists、advertising、reports、projectのいずれか。 - 制限あり。 サービスクライアントは、サービスクライアント、メンバー、招待、エージェントのサインインを作成または管理しません。
- サインイン方法は2つあります。 登録済みの公開鍵(
private_key_jwt、推奨方法)か秘密鍵(client_secret_basic)。クライアントはそれぞれ最大2つまで保持でき、ダウンタイムなしでローテーション可能です。
組織は、すべてのプロジェクトを合わせて最大50件のライブサービスクライアントを保持します。クライアントIDは scl_、シークレットは sr_cs_ で始まります。
キーを作成
クライアントを実行するマシン上でEd25519鍵ペアを生成します。秘密鍵はマシンから出ず、screenRIGは公開鍵のみ保持します。
openssl genpkey -algorithm ed25519 -out screenrig-client.pem
chmod 600 screenrig-client.pemEC P-256キーと2048ビット以上のRSAキーも使用可能です。
クライアントを作成
ダッシュボードで。 アクセスを開き、新しいサービスクライアントを選択します。名前を付け、読み取り専用、提案、管理のいずれかを選択し、権限を確認して公開鍵をJWKとして貼り付けるか、クライアントシークレットを生成を選択します。Nodeで公開JWKを印刷します。
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"})))'CLI の場合。 エージェントには、identity スコープを持つアイデンティティの管理アクセス、対象プロジェクトで project 機能を含むエージェント自身の管理権限の付与、およびそのプロジェクトの組織を管理する本人確認済みの責任者が必要です。
新規登録したエージェントでは、招待された人が最初に登録の招待を承諾する必要があります。プロジェクトの管理アクセスだけでは認証情報を変更できません。
--key-file は JWK または PEM ファイルを受け取り、公開鍵部分のみを送信します。
screenrig service-client create --name "Nightly report" --access read \
--capability reports --key-file screenrig-client.pem代わりにシークレットを使うには--secret-file PATHを追加します。CLIは新しいファイルに0600モードでシークレットを書き込み、一度だけ表示し再表示しません。
screenrig service-client show scl_ID はクライアントのキー ID(kid)とシークレット ID を一覧表示します。独自の kid がないキーは RFC 7638 サムプリントで識別されます。
アップローダークライアント
代理店のエージェントには、選んだプレイリストとメディアフォルダーに限定されたクライアントが与えられます。--uploader --folder-id mfd_FOLDER[,mfd_FOLDER] --playlist-id pl_PLAYLIST[,pl_PLAYLIST]をservice-client createに追加します。アップローダークライアントはcontentとplaylistsの権限のみを持ち、--accessで別に指定しない限り提案として動作します。そのトークンで、自分のフォルダーにアップロードし(screenrig media upload FILE --folder mfd_FOLDER)、自分のプレイリストのページを追加・置換・削除できます(screenrig playlist page add|replace|remove)。それ以外はすべてoutside_upload_scopeで応答し、そのupload_scopeに使用できるものが一覧されます。プロジェクトオーナーは後からクライアントのフォルダーとプレイリストを変更でき、変更は発行済みのトークンに適用されます。アップローダーを参照してください。
アクセストークンを取得
トークンエンドポイントに client_credentials グラントでトークンを要求。レスポンスに access_token、token_type: Bearer、expires_in(900秒)、scope を含む。リフレッシュトークンはなし。期限切れ時に再度要求してください。
シークレットを使う場合、HTTP Basicで認証します。リクエストボディ内のシークレットは拒否されます:
curl -s https://api.screenrig.ai/oauth/token \
-u "$SCREENRIG_CLIENT_ID:$SCREENRIG_CLIENT_SECRET" \
-d grant_type=client_credentialsキーを使う場合、シークレットの代わりに署名済みクライアントアサーションを送信します:
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"アサーションはあなたの秘密鍵で署名された JWT です:
| フィールド | 値 |
|---|---|
ヘッダー alg | EdDSA、ES256、またはRS256、キーに一致 |
ヘッダー kid | 登録された鍵のID |
iss と sub | クライアントIDです。 |
aud | https://api.screenrig.ai または https://api.screenrig.ai/oauth/token(1つの文字列として) |
iat, exp | 最大5分間隔。 |
jti | すべてのアサーションに対して新しいランダム値を生成します。同じ値の繰り返しは拒否されます。 |
ヘッダー jku、x5u、jwk、x5c は拒否されました。
減らすよう依頼
2つのオプションパラメーターがトークンを絞り込みます:
scope:access:readまたはaccess:proposeはトークンにクライアントより低いレベルを与え、機能名は例えばscope=access:read reportsのようにその機能だけを保持します。クライアントを超えるスコープはinvalid_scopeです。resource:REST APIのデフォルトであるhttps://api.screenrig.ai/api、またはリモートサーバー用のhttps://api.screenrig.ai/mcp。
API を呼び出す
トークンをベアラーとして送信します。クライアント自身のプロジェクトに作用します:
curl -s https://api.screenrig.ai/api/screens \
-H "Authorization: Bearer $TOKEN"Node:キー付きのキャッシュされたトークン
このヘルパーは Node の組み込み crypto でアサーションに署名し、トークンをメモリに保持し、有効期限の1分前に新しいトークンを要求します。CLIENT_KEY_ID を service-client show の kid キーに設定してください。
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:秘密付きのキャッシュトークン
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"]CLI をサービスクライアントとして実行
CLI をそのクライアントとして一度サインインさせ、専用の設定に保存します。設定には、クライアントと、秘密鍵(推奨)またはシークレットを保存したファイルが記録され、鍵やシークレット自体は記録されません。
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シークレットを使う場合は、--client-key-file の代わりに --client-secret-file FILE を使います。この設定を使うコマンドはすべて、サインインなしでそのクライアントとして実行されます。毎回キーまたはシークレットのファイルを読み込むため、ローテーションしたファイルでも新たなサインインは不要で、トークンはメモリに保持され、設定にはそれ以外何も書き込まれません。screenrig logout でその設定からクライアントを削除します。各コマンドは独自のトークンを要求するため、1時間に数百コマンド以上実行するジョブはAPI経由で再利用する1つのトークンが適しています。
回転と取り消し
| から | ダッシュボード(アクセス、クライアント) | CLI |
|---|---|---|
| 2番目のキーを追加 | キーを追加 | screenrig service-client add-key scl_ID --key-file new.pem |
| キーを削除 | キーを削除 | screenrig service-client remove-key scl_ID --kid KID |
| 2番目のシークレットを追加 | シークレットを生成 | screenrig service-client add-secret scl_ID --secret-file new-secret |
| シークレットを削除 | シークレットを削除 | screenrig service-client remove-secret scl_ID --secret-id css_ID |
| アクセスまたは機能を変更 | アクセス、次にアクセスを保存 | ダッシュボードを使用してください。 |
| クライアントを終了する | 取り消す | screenrig service-client revoke scl_ID --yes |
ダウンタイムなしでローテーションするには、新しいキーまたはシークレットを追加してデプロイし、古いものを削除します。削除されたキーまたはシークレットで発行されたトークンは数秒で無効になり、クライアントは少なくとも1つのキーまたはシークレットを保持します。
クライアントのアクセスや機能を制限すると、その現在のトークンは無効になります。次のトークンが新しいアクセス権を持ちます。取り消しは永久的で、キーとシークレットは削除され、クライアントのトークンは数秒以内に無効になり、記録は監査のためアクセスページに残ります。プロジェクトを削除すると、そのクライアントも取り消されます。
クライアントはプロジェクトがアクティブな間のみトークンを取得します。キャンセルまたは削除されたプロジェクトは発行しません。クレジットがゼロのプロジェクトもトークンを発行し、有料リクエストはエージェントと同様に 402 payment_required で応答します。
エラーと制限
| レスポンス | 原因 | 修正 |
|---|---|---|
401 invalid_client | 誤ったまたは削除されたシークレット、不正または繰り返しの主張、不明なkid、取り消されたクライアント、キャンセルまたは削除されたプロジェクト。 | 資格情報をservice-client showと照合します。毎回新しいjtiを送信。 |
400 invalid_scope | クライアントのアクセスまたは権限を超えたスコープです。 | 減らすよう依頼、またはクライアントを変更 |
400 invalid_target | 上記以外の resource | デフォルトまたはリモートサーバーのものを使用してください。 |
429 rate_limited | 1時間にクライアントあたり600トークン以上、またはアドレスあたり1,200トークンリクエスト以上。 | トークンは有効期限まで再利用します |
503 temporarily_unavailable | しばらくしてから再試行してください | バックオフ付きで再試行します。 |
/api から 401 | トークンが期限切れか取り消されました。 | 一度だけ新しいトークンを要求し、その後停止 |
クライアントの管理時に 403 forbidden | アイデンティティの権限、project 機能を持つ現在のプロジェクトメンバーシップ、または責任者の組織管理者ロールがありません | 招待された人に登録を承諾してもらい、アイデンティティの管理アクセス、エージェント自身に付与されたプロジェクトの管理権限、および責任者の組織管理者ロールを確認してください |
/api から 403 insufficient_access | トークンのアクセスレベルがその変更を行えないか、プロジェクトがエージェント変更のレビューを要求しています。 | Manage クライアントを使用してください。プロジェクトがレビューを必要とする間、プレイリストとスクリーンの変更はレビューに送られます。 |
/api から 402 payment_required | プロジェクトには有料リクエストのクレジットがありません。 | プロジェクトにクレジットを追加 |