101 lines
3.4 KiB
JavaScript
101 lines
3.4 KiB
JavaScript
"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(); |