express/LINEWORKS_Pleasanter/LINEWORKS_sendMessage、いずれも過去の Express実装。Gitea移行に伴い保管。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
78 lines
2.2 KiB
JavaScript
78 lines
2.2 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const jwt = require('jsonwebtoken');
|
|
|
|
let cachedToken = null;
|
|
let cachedExpiry = 0;
|
|
let cachedScopeKey = '';
|
|
|
|
const CLIENT_ID = 'V58hNobcAqRcQPerySMz';
|
|
const CLIENT_SECRET = 'gJ84XQtigD';
|
|
const SERVICE_ACCOUNT = 'x94xh.serviceaccount@works-nextgroup2';
|
|
const TOKEN_ENDPOINT = 'https://auth.worksmobile.com/oauth2/v2.0/token';
|
|
const PRIVATE_KEY_PATH = path.join(__dirname, 'private_20250530180606.key');
|
|
const TOKEN_TTL_BUFFER = 30; // seconds
|
|
const DEFAULT_SCOPES = ['bot', 'contact'];
|
|
|
|
function readPrivateKey() {
|
|
return fs.readFileSync(PRIVATE_KEY_PATH, 'utf8');
|
|
}
|
|
|
|
function makeScopeKey(scopes) {
|
|
return scopes.slice().sort().join(' ');
|
|
}
|
|
|
|
function isTokenValid(scopeKey) {
|
|
return Boolean(cachedToken)
|
|
&& cachedScopeKey === scopeKey
|
|
&& cachedExpiry > Math.floor(Date.now() / 1000) + TOKEN_TTL_BUFFER;
|
|
}
|
|
|
|
async function getAccessToken(scopes = DEFAULT_SCOPES) {
|
|
const normalizedScopes = Array.isArray(scopes) && scopes.length > 0 ? scopes : DEFAULT_SCOPES;
|
|
const scopeKey = makeScopeKey(normalizedScopes);
|
|
|
|
if (isTokenValid(scopeKey)) {
|
|
return cachedToken;
|
|
}
|
|
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const payload = {
|
|
iss: CLIENT_ID,
|
|
sub: SERVICE_ACCOUNT,
|
|
iat: now,
|
|
exp: now + 5 * 60,
|
|
aud: TOKEN_ENDPOINT
|
|
};
|
|
|
|
const assertion = jwt.sign(payload, readPrivateKey(), { algorithm: 'RS256' });
|
|
const body = new URLSearchParams({
|
|
assertion,
|
|
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
client_id: CLIENT_ID,
|
|
client_secret: CLIENT_SECRET,
|
|
scope: normalizedScopes.join(' ')
|
|
});
|
|
|
|
const response = await fetch(TOKEN_ENDPOINT, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: body.toString()
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
throw new Error(`LINE WORKSアクセストークン取得失敗: ${errorText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
cachedToken = data.access_token;
|
|
cachedExpiry = now + (data.expires_in || 300);
|
|
cachedScopeKey = scopeKey;
|
|
return cachedToken;
|
|
}
|
|
|
|
module.exports = {
|
|
getAccessToken
|
|
};
|