"use strict"; /* * アクセストークンだけを標準出力するヘルパー * lineworks-anythingllm.js の認証ロジックを流用 * * 使い方: * node get-token.js * → アクセストークン文字列だけが標準出力される(それ以外は何も出力しない) */ const fs = require("fs"); const path = require("path"); const crypto = require("crypto"); const LW_TOKEN_URL = "https://auth.worksmobile.com/oauth2/v2.0/token"; const PRIVATE_KEY_FILE = process.env.LW_PRIVATE_KEY_FILE ? path.resolve(process.env.LW_PRIVATE_KEY_FILE) : path.join(__dirname, "private_20260307184804.key"); const LW_CLIENT_ID = process.env.LW_CLIENT_ID || "tre8J_Tk8RblfsMSyZRh"; const LW_CLIENT_SECRET = process.env.LW_CLIENT_SECRET || "O5_R5gcHxg"; const LW_SERVICE_ACCOUNT = process.env.LW_SERVICE_ACCOUNT || "wh4k8.serviceaccount@next-hd.co.jp"; const LW_SCOPE = process.env.LW_SCOPE || "board board.read"; const LW_PRIVATE_KEY = (process.env.LW_PRIVATE_KEY && process.env.LW_PRIVATE_KEY.trim()) ? process.env.LW_PRIVATE_KEY : (fs.existsSync(PRIVATE_KEY_FILE) ? fs.readFileSync(PRIVATE_KEY_FILE, "utf8") : ""); function base64UrlEncode(value) { return Buffer.from(value) .toString("base64") .replace(/=/g, "") .replace(/\+/g, "-") .replace(/\//g, "_"); } function createJwtAssertion() { const now = Math.floor(Date.now() / 1000); const header = { alg: "RS256", typ: "JWT" }; const payload = { iss: LW_CLIENT_ID, sub: LW_SERVICE_ACCOUNT, aud: LW_TOKEN_URL, iat: now, exp: now + 300, }; const encodedHeader = base64UrlEncode(JSON.stringify(header)); const encodedPayload = base64UrlEncode(JSON.stringify(payload)); const signingInput = `${encodedHeader}.${encodedPayload}`; const signer = crypto.createSign("RSA-SHA256"); signer.update(signingInput); signer.end(); const signature = signer .sign(LW_PRIVATE_KEY) .toString("base64") .replace(/=/g, "") .replace(/\+/g, "-") .replace(/\//g, "_"); return `${signingInput}.${signature}`; } // デバッグ用: 実際に使われている値をstderrに出力(トークン取得の可否には影響しない) process.stderr.write(`[DEBUG] LW_CLIENT_ID=${LW_CLIENT_ID}\n`); process.stderr.write(`[DEBUG] LW_SERVICE_ACCOUNT=${LW_SERVICE_ACCOUNT}\n`); process.stderr.write(`[DEBUG] LW_PRIVATE_KEY_FILE=${PRIVATE_KEY_FILE}\n`); process.stderr.write(`[DEBUG] 秘密鍵ファイル存在=${fs.existsSync(PRIVATE_KEY_FILE)}\n`); process.stderr.write(`[DEBUG] LW_SCOPE=${LW_SCOPE}\n`); async function main() { const assertion = createJwtAssertion(); const form = new URLSearchParams({ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion, client_id: LW_CLIENT_ID, client_secret: LW_CLIENT_SECRET, scope: LW_SCOPE, }); const response = await fetch(LW_TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: form, }); if (!response.ok) { process.stderr.write(`アクセストークン取得失敗: ${response.status} ${await response.text()}\n`); process.exitCode = 1; return; } const data = await response.json(); // 標準出力にはトークンのみを出す(PowerShell側で変数に取り込みやすくするため) process.stdout.write(data.access_token); } main();