"use strict"; /* * LINE WORKS User Account認証 (OAuth 2.0 認可コードフロー)。 * Drive/共有ドライブAPIはService Account認証(JWT)では利用できないため、 * backup.js のドライブ操作はこちらの実ユーザー認証を使う。 * * 初回セットアップ: `node lineworks-authorize.js` を一度だけ実行し、 * ブラウザでの認可 → refresh_token取得 → .envへの保存 を行う。 * 以降 backup.js は getUserAccessToken() で refresh_token から * access_token を都度再発行する(ユーザー操作不要)。 * * 仕様書: https://developers.worksmobile.com/jp/docs/auth-oauth */ const { updateEnvValue } = require("./env"); const LW_AUTHORIZE_URL = "https://auth.worksmobile.com/oauth2/v2.0/authorize"; const LW_TOKEN_URL = "https://auth.worksmobile.com/oauth2/v2.0/token"; function requiredEnv(key) { const value = process.env[key]; if (!value) { throw new Error(`環境変数が不足しています: ${key}`); } return value; } function buildAuthorizeUrl({ state }) { const clientId = requiredEnv("LW_CLIENT_ID"); const redirectUri = requiredEnv("LW_REDIRECT_URI"); const scope = requiredEnv("LW_SCOPE_DRIVE"); const params = new URLSearchParams({ client_id: clientId, redirect_uri: redirectUri, response_type: "code", scope, state, }); return `${LW_AUTHORIZE_URL}?${params.toString()}`; } async function requestToken(form) { const response = await fetch(LW_TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: form, }); const raw = await response.text(); let data; try { data = raw ? JSON.parse(raw) : {}; } catch (_err) { throw new Error(`トークンエンドポイントのレスポンスがJSONではありません: ${raw}`); } if (!response.ok) { throw new Error(`トークン取得失敗: ${response.status} ${raw}`); } return data; } async function exchangeCodeForToken(code) { const clientId = requiredEnv("LW_CLIENT_ID"); const clientSecret = requiredEnv("LW_CLIENT_SECRET"); const redirectUri = requiredEnv("LW_REDIRECT_URI"); return requestToken( new URLSearchParams({ code, grant_type: "authorization_code", client_id: clientId, client_secret: clientSecret, redirect_uri: redirectUri, }) ); } // refresh_token から access_token を再発行する。Refresh Token Rotation が ON のため、 // レスポンスに含まれる新しい refresh_token を必ず .env へ書き戻してから access_token を返す。 async function getUserAccessToken() { const clientId = requiredEnv("LW_CLIENT_ID"); const clientSecret = requiredEnv("LW_CLIENT_SECRET"); const refreshToken = process.env.LW_USER_REFRESH_TOKEN; if (!refreshToken) { throw new Error( "LW_USER_REFRESH_TOKEN が未設定です。先に 'node lineworks-authorize.js' を実行してください。" ); } const data = await requestToken( new URLSearchParams({ refresh_token: refreshToken, grant_type: "refresh_token", client_id: clientId, client_secret: clientSecret, }) ); if (!data.access_token) { throw new Error("アクセストークン再発行失敗: access_token が返却されませんでした"); } if (data.refresh_token) { updateEnvValue("LW_USER_REFRESH_TOKEN", data.refresh_token); } return data.access_token; } module.exports = { buildAuthorizeUrl, exchangeCodeForToken, getUserAccessToken, LW_AUTHORIZE_URL, LW_TOKEN_URL, };