BeeBridge AIBeeBridge AI
API リファレンス
管理API管理 PAT の接続と移行
⚠️コンプライアンスに関する注意:本プロジェクトは、適法に許可された API ゲートウェイ、内部管理、プライベート環境への導入に限ってご利用ください。上流サービスの利用規約、プラットフォーム規則、規制要件、コンテンツ安全要件を遵守してください。

管理 PAT の接続と移行

rc.44 のログインから操作証明、PAT の一回限りの発行まで

前提条件

管理連携の送信先は https://api.beeaicloud.com です。管理認証を先に確認してください。PAT の作成と管理には既存 PAT ではなくログインセッションが必要です。例は仮の値や一時変数を使用します。

最終的な権限を正確に紐付ける

最上位の scope="access_token.generate" は PAT 作成操作、context.scopes は PAT に与える権限です。context には scopes と expires_at だけを指定します。欠落、空オブジェクト、不正な型、空の権限、余分な項目は拒否されます。検証と作成では同じ権限集合と同じ Unix 秒の期限を再利用し、期限を再計算しません。正の期限は作成時から少なくとも 1 時間後です。作成時の name は空白除去後 1–64 文字で、検証 context には含めません。

{"method":"password","scope":"access_token.generate","context":{"scopes":["profile:read"],"expires_at":1794679200},"password_encrypted":"<encrypted-password>","encryption_key_id":"<current-kid>"}

パスワード方式の完全な例

次の Node.js 例は現在の公開鍵、v2 暗号化、本人確認、同一権限の紐付けを扱います。password が許可された場合のみ使用してください。ログイン MFA や CAPTCHA などの手順を回避してはいけません。BB_PAT_SCOPES の既定は profile:read のみです。PAT は表示しないため、終了前に pat を秘密管理ストアへ保存してください。

import { constants, createCipheriv, publicEncrypt, randomBytes } from 'node:crypto';

const base = 'https://api.beeaicloud.com';
const username = process.env.BB_USERNAME;
const password = process.env.BB_PASSWORD;
const expectedUserID = process.env.BB_EXPECTED_USER_ID;
if (!username || !password || !expectedUserID) throw new Error('Missing account configuration');

async function call(path, { method = 'GET', access, proof, body } = {}) {
  const response = await fetch(base + path, {
    method,
    headers: {
      ...(body ? { 'Content-Type': 'application/json' } : {}),
      ...(access ? { Authorization: `Bearer ${access}` } : {}),
      ...(proof ? { 'X-Security-Proof': proof } : {}),
    },
    ...(body ? { body: JSON.stringify(body) } : {}),
    redirect: 'error',
  });
  const result = await response.json();
  if (!response.ok || result.success !== true) {
    const code = result.code ?? result.data?.code ?? 'REQUEST_REJECTED';
    throw new Error(`${method} ${path}: HTTP ${response.status} ${code}`);
  }
  return result.data;
}

async function passwordFields() {
  const key = await call('/api/user/login/encryption-key');
  if (!key.enabled) return { password };
  const aesKey = randomBytes(32);
  const nonce = randomBytes(12);
  const cipher = createCipheriv('aes-256-gcm', aesKey, nonce);
  cipher.setAAD(Buffer.from(`password-v2:${key.kid}`));
  const ciphertext = Buffer.concat([
    cipher.update(password, 'utf8'), cipher.final(), cipher.getAuthTag(),
  ]);
  const wrappedKey = publicEncrypt({
    key: key.public_key, padding: constants.RSA_PKCS1_OAEP_PADDING,
    oaepHash: 'sha256', oaepLabel: Buffer.from('password-v2'),
  }, aesKey);
  return {
    password_encrypted: ['v2', wrappedKey.toString('base64'),
      nonce.toString('base64'), ciphertext.toString('base64')].join('.'),
    encryption_key_id: key.kid,
  };
}

const login = await call('/api/user/login', {
  method: 'POST', body: { username, ...await passwordFields() },
});
if (login.require_verification || !login.access_token) {
  throw new Error('Complete the required sign-in verification flow first');
}
const access = login.access_token;
const profile = await call('/api/user/self', { access });
if (String(profile.id) !== expectedUserID || profile.username !== username) {
  throw new Error('Account identity mismatch');
}
const catalog = await call('/api/user/access_tokens/catalog', { access });
const grantable = new Set(catalog.groups.flatMap(group => group.resources.flatMap(
  resource => resource.actions.map(action => `${resource.resource}:${action.action}`),
)));
const grant = {
  scopes: JSON.parse(process.env.BB_PAT_SCOPES ?? '["profile:read"]'),
  expires_at: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60,
};
if (!Array.isArray(grant.scopes) || grant.scopes.length === 0 ||
    grant.scopes.some(scope => !grantable.has(scope))) {
  throw new Error('Select grantable permissions from the catalog');
}
const methods = await call('/api/verify/methods?scope=access_token.generate', { access });
if (!methods.methods.some(method => method.method === 'password' && method.available)) {
  throw new Error('Use an available verification method for this account');
}
const verification = await call('/api/verify', {
  method: 'POST', access,
  body: { method: 'password', scope: 'access_token.generate',
    context: grant, ...await passwordFields() },
});
const created = await call('/api/user/access_tokens', {
  method: 'POST', access, proof: verification.proof_token,
  body: { name: 'business-integration', ...grant },
});
const pat = created.token;
// Store pat in your secret manager here. Never log it or blindly retry creation.
console.log({ id: created.item.id, expires_at: created.item.expires_at });

一覧・変更・取消

GET /api/user/access_tokens は平文を返しません。作成応答の data.token は一回だけ返り、token_ref や token_hint から復元できません。PATCH /api/user/access_tokens/{id} は name が必須で、名前だけなら証明は不要です。scopes も変更する場合は access_token.update と {"token_id":123,"scopes":["profile:read"]} を検証します。DELETE /api/user/access_tokens/{id} には access_token.revoke と {"token_id":123}、旧 PAT の DELETE /api/user/access_tokens/legacy には {"legacy":true} を使用します。各書き込みに新しい証明が必要です。

エラーの診断

以下は rc.44 のエラーコードです。HTTP 状態だけで成功と判断したり、password_proof という呼出段階名だけでパスワード不正と判断したりしないでください。

コード意味と確認事項
SECURITY_CONTEXT_INVALIDHTTP 400。context の欠落や操作構造の不一致。
SECURITY_VERIFICATION_FAILEDパスワード、暗号文、コードの検証失敗。HTTP 200 と success=false の場合もあります。
SECURITY_VERIFICATION_FLOW_REQUIREDHTTP 400。専用の対話フローが必要です。
AUTH_SESSION_REQUIREDHTTP 403。PAT 管理にはログインセッションが必要です。
SECURITY_PROOF_CONTEXT_MISMATCH証明と実際の操作のパラメーターが異なります。
SECURITY_PROOF_EXPIRED / SECURITY_PROOF_CONSUMED証明の期限切れまたは消費済みです。結果を照合してください。
ACCESS_TOKEN_SCOPE_INVALID / ACCESS_TOKEN_SCOPE_FORBIDDEN権限が不正、またはこのアカウントでは付与できません。
ACCESS_TOKEN_EXPIRY_INVALID / ACCESS_TOKEN_LIMIT期限が 1 時間未満、または PAT 数の上限です。

登録と初期化完了の違い

初期化はアカウント作成 → ログイン → 操作証明 → PAT 作成 → 安全な保存と連携確認の順です。アカウントの存在は完了を意味しません。既存の旧 PAT の利用と新規発行は別です。再開時はアカウントを保持し、既存 PAT を確認して未完了と確認できた手順だけを再開します。結果不明の発行は先に照合してください。

このガイドはいかがですか?

管理 PAT の接続と移行 | BeeBridge AI