GCPでOAuthクライアントを設定する際に少々戸惑ったのでまとめます。
フロー
-
GCPでOAuthクライアントを準備する
- Google Auth Platform/クライアント
- クライアントを作成
- 諸々設定後、jsonをダウンロードし、プロジェクトのrootにcredentials.jsonとして配置
-
npm run google:get-refresh-tokenを実行- スクリプトが認可URLを生成して表示する
- ブラウザでURLを開き、GoogleログインしてClassroom読み取り権限を許可する
- 認可後に出たauthorization codeをターミナルに貼り付ける
- レスポンスからrefresh_tokenを取得
- 開発環境なら.dev.versなどにGOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKENを入力し、保存
get-refresh-token.mjs
import fs from 'node:fs/promises';
import readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
import { google } from 'googleapis';
const SCOPES = ['https://www.googleapis.com/auth/classroom.courses.readonly']; // 使用するAPIに合わせて
const CREDENTIALS_PATH = process.env.GOOGLE_CREDENTIALS_PATH ?? './credentials.json';
const REDIRECT_URI = process.env.GOOGLE_REDIRECT_URI ?? 'urn:ietf:wg:oauth:2.0:oob';
async function readCredentials() {
const raw = await fs.readFile(CREDENTIALS_PATH, 'utf8');
const json = JSON.parse(raw);
const config = json.installed ?? json.web;
if (!config?.client_id || !config?.client_secret) {
throw new Error(
`Invalid credentials format in ${CREDENTIALS_PATH}. Expected web/installed client_id and client_secret.`,
);
}
return {
clientId: config.client_id,
clientSecret: config.client_secret,
};
}
async function main() {
const { clientId, clientSecret } = await readCredentials();
const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, REDIRECT_URI);
const authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
prompt: 'consent',
});
console.log('\n1) Open this URL in your browser and allow access:\n');
console.log(authUrl);
const rl = readline.createInterface({ input, output });
const code = await rl.question('\n2) Paste the authorization code here: ');
rl.close();
const { tokens } = await oauth2Client.getToken(code.trim());
if (!tokens.refresh_token) {
throw new Error(
'No refresh_token returned. Re-run and ensure prompt=consent, then approve requested scopes.',
);
}
console.log('\nRefresh token (set as GOOGLE_REFRESH_TOKEN):\n');
console.log(tokens.refresh_token);
if (tokens.access_token) {
console.log('\nAccess token was also issued (short-lived):\n');
console.log(tokens.access_token);
}
}
main().catch((error) => {
console.error('\nFailed to get refresh token:');
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
});
所感(雑)
GoogleのAPIは結構多用することになりそうなので身につけたいと思いました
参考
Codexと↓