LoginSignup
0
1

More than 3 years have passed since last update.

GoogleAPI for Node.js で Authorize までを共通化する

Posted at

はじめに

Node.js を使用して GoogleAPI にアクセスする場合、SheetAPI であろうと DriveAPI であろうと認可の部分までは実は大体一緒です。

https://developers.google.com/docs/api/quickstart/nodejs
https://developers.google.com/sheets/api/quickstart/nodejs
https://developers.google.com/drive/api/v3/quickstart/nodejs

あまりプロジェクト内で各サービスを行き来する事は無いのですが、どうせ共通なら一つのファイルにまとめて使いまわしたい、と思ってクラスを作りました。サンプルの内容をts化、クラス化して、Promise 対応しています。
誰でも思いつく事だと思いますが、今後の自分のため + 同じような結論に行きついた方のためにまとめておきます。

認可までを行うクラス

GoogleAuthorizer という名称のクラスにしました。
適当な名前で、以下のファイルを保存します。ここでは authorize.ts にしました。

import fs from "fs";
import readline from "readline";
import { google } from "googleapis";
import { OAuth2Client } from "googleapis-common";

export class GoogleAuthorizer {

  private credentialsBuffer: string | null = null;
  private tokenBuffer: string | null = null;

  constructor(
    private scopes: string[],
    private tokenPath: string,
    private credentialsPath: string
  ) { }

  public async getOAuth2Client(): Promise<OAuth2Client> {
    if (this.credentialsBuffer) {
      return await this.authorize(JSON.parse(this.credentialsBuffer));
    } else {
      return new Promise<OAuth2Client>((res, rej) => {
        fs.readFile(this.credentialsPath, async (err, content) => {
          if (err) {
            rej('Error loading client secret file:' + err);
            return;
          }
          // Authorize a client with credentials, then call the Google Drive API.
          this.credentialsBuffer = content.toString();
          try {
            const o = await this.authorize(JSON.parse(content.toString()));
            res(o);
          } catch (e) {
            rej(e);
          }
        });
      });
    }
  }

  private async authorize(credentials: any): Promise<OAuth2Client> {
    return new Promise<OAuth2Client>((res, rej) => {
      const { client_secret, client_id, redirect_uris } = credentials.installed;
      const oAuth2Client = new google.auth.OAuth2(
        client_id, client_secret, redirect_uris[0]);
      if (this.tokenBuffer) {
        oAuth2Client.setCredentials(JSON.parse(this.tokenBuffer));
        res(oAuth2Client);
        return;
      }
      // Check if we have previously stored a token.
      fs.readFile(this.tokenPath, async (err, token) => {
        if (err) {
          try {
            await this.getAccessToken(oAuth2Client);
            res(oAuth2Client);
          } catch (e) {
            rej(e)
          }
          return;
        }
        this.tokenBuffer = token.toString();
        oAuth2Client.setCredentials(JSON.parse(token.toString()));
        res(oAuth2Client);
      });
    });
  }

  private getAccessToken(oAuth2Client: OAuth2Client): Promise<OAuth2Client> {
    return new Promise<OAuth2Client>((res, rej) => {
      const authUrl = oAuth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: this.scopes,
      });
      console.log('Authorize this app by visiting this url:', authUrl);
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
      });
      rl.question('Enter the code from that page here: ', (code) => {
        rl.close();
        oAuth2Client.getToken(code, (err, token) => {
          if (err || !token) {
            rej('Error retrieving access token' + err);
            return;
          }
          oAuth2Client.setCredentials(token);
          // Store the token to disk for later program executions
          fs.writeFile(this.tokenPath, JSON.stringify(token), (err) => {
            if (err) {
              rej(err);
              return;
            }
            console.log('Token stored to', this.tokenPath);
          });
          res(oAuth2Client);
        });
      });
    });
  }
}

あまり気にしなくても良いかもしれませんが、ファイルIOがあまり発生するのもアレかなと思って、一度ファイルから読んだ情報はバッファに入れて再利用しています。

使用方法

先程保存した authorize.ts を読み込み、コンストラクタに以下の引数を指定して、まずはインスタンスを作成します。
その後、getOAuth2Clientawait 付きで呼び出せば、必要な OAuth2Client が取得できます。

引数 内容
1 使用するスコープ(配列)
2 トークンファイルのパス
2 認証ファイルのパス
import { GoogleAuthorizer } from "./authorize";

class Main {

  constructor() {
    this.init();
  }

  async init() {
    const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];
    const TOKEN_PATH = "token.json";
    const CREDENTIALS_PATH = "credentials.json";

    const a = new GoogleAuthorizer(SCOPES, TOKEN_PATH, CREDENTIALS_PATH);
    try {
      const auth = await a.getOAuth2Client();
      console.log(auth);
    } catch(e) {
      console.error(e);
    }
  }

}
new Main();

おまけ

冒頭で紹介した Google の Node.js サンプルページでは、とりあえず認証情報作るボタンがありますが、これをクリックすると QuickStart というプロジェクトが作成され、その中に適切な権限が与えられた認証情報が作成されて、認証ファイルがDLされます。
既存のプロジェクトに手動で追加する場合に困ったのですが、以下に方法が書いてありました。

manual.png

BigQueryのページですが、ここはサービス間で共通です。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1