Google Apps Script + OAuthライブラリで、freee APIの操作を行う

この記事は、[Web API アドベントカレンダーの6日目の記事](https://qiita.com/advent-calendar/2020/webapi)です。

Google Apps Script を利用して、freee API の操作を行いたいというニーズをよく聞きます。
いろいろなハウトゥ記事が公開されていますが、「OAuth2.0」の認可操作が一つの壁になっているようです。

今回は、Googleが配布しているOAuth2.0ライブラリ「apps-script-oauth2」を利用して、freee API の操作を行うまでをまとめてみました。

# 行うこと

* Googleスプレッドシート ＋ Google Apps Script で、freee API の認可・情報取得を行う。
* OAuth2.0 の処理は、「apps-script-oauth2」を使う
* スプレッドシートのメニューに「認可」「ログアウト」の操作メニューを追加する。
* 「認可」をクリックすると、OAuth処理が始まる。成功したあともう一度「認可」を押すと、認可ユーザーの情報が表示される
* 「ログアウト」をクリックすると、freeeアプリからログアウト処理される


# 流れ

* freee アプリの作成
* Googleスプレッドシートの準備とapps-script-oauth2のインストール
* コード作成
* 実際の動作


# freee アプリの作成

契約しているfreeeアカウントを利用して始めます。

freeeアカウントがない場合は[「freee API スタートガイド」](https://developer.freee.co.jp/getting-started) を見ながらテスト用の事業所を作ると良いでしょう。

freee API を操作するためには、最初に「freeeアプリ」を作る必要があります。
作成の手順は、公式サイトの「チュートリアル」に詳しくまとまっています。

 [freee API チュートリアル](https://developer.freee.co.jp/tutorials)

このチュートリアル見ながら進めれば、迷うことはないでしょう。順番としては

*  [freee アプリストアの開発者ページ](https://app.secure.freee.co.jp/developers)にアクセス
*  [freeeアカウントでログイン](https://accounts.secure.freee.co.jp/login/app_store/developers)
*  事業所を選ぶ
*  [アプリ管理](https://app.secure.freee.co.jp/developers/applications)のページから、アプリを新規追加する

の流れでアプリを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/ce3988a3-843a-ba16-b025-8fe06bf02df3.png)

アプリ名、説明、利用規約のチェックを行い、新規作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/f9111269-b12c-ede2-a743-c2a646958450.png)

アプリを下書き保存しておきます。

「コールバックURL」
「Client ID」
「Client Secret」

は、後の設定で利用します。


# Googleスプレッドシートの準備とapps-script-oauth2のインストール

次に、Googleスプレッドシートの準備をします。


## apps-script-oauth2 とは何か

apps-script-oauth2 は、Google Apps Script用の OAuth2.0処理ライブラリです。Googleが管理するGitHubの [「Google Workspace」](https://github.com/googleworkspace) 内で配布されています。

* [https://github.com/googleworkspace/apps-script-oauth2](https://github.com/googleworkspace/apps-script-oauth2)

Google Workspace は、Google APIのためのサンプルコード集です。オープンソースライセンスを採用しており、誰でも自由に利用ができます。

Googleが管理しているライブライということで、安心して使える印象がありますね（サンプル＋OSSなのでノーサポートではありますが）。


## apps-script-oauth2 のインストール

GSuiteから新しいGoogle スプレッドシートを作成し、「ツール」→「スクリプトエディタ」を選んで、GASのエディタを開きます。

エディタ画面が開いたら、「ライブラリ」を選び、apps-script-oauth2 のインストール準備をします。

「Add a library」欄に、apps-script-oauth2のスクリプトIDを入力して追加します。IDは一意に決まっており

```
1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
```
を入力。最新バージョンを選んで保存します。

![スクリーンショット 2021-09-01 16.49.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/26ce0fab-ab60-9f76-630d-731ceee278ba.png)


保存ができたら、インストールは完了です。

## freeeアプリの コールバックURLを設定する

次に、先ほど作成したfreee アプリのコールバックURLを修正します。
OAuth2.0認可処理後の遷移先を設定します。

apps-script-oauth2を使った場合、コールバックURLは必ず

```
https://script.google.com/macros/d/{SCRIPT ID}/usercallback
```

になります。「script ID」は、エディタ画面の「プロジェクトの概要」→「ID(スクリプトID)」で確認できます。

コールバックURLのSCRIPT IDを書き換え、freeeアプリの「コールバックURL」に入力し、下書き保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/c58e3309-57c0-6c7a-dc17-eac94f511468.png)

# Client ID と Client Secret を プロパティに保存する

freeeのOAuth認証に必要な情報を、GASの [プロパティサービス](https://developers.google.com/apps-script/reference/properties) に保存します。プロパティサービスとは、Google Apps Script で使う情報をクラウドサーバー上に保存して、環境変数のように使える領域のことです。ブラウザでいうウェブストレージに近い感覚で利用できます。

https://developers.google.com/apps-script/reference/properties

プロパティサービスにデータを登録するコードは以下のとおりです。

```javascript
function registKeys() {

    const Properties = PropertiesService.getScriptProperties();

    Properties.setProperty('ClientId', '${freeeアプリのClient ID文字列}');
    Properties.setProperty('ClientSecret', '${freeeアプリのClient ID文字列}');

    console.log(Properties.getProperty('ClientId'));
    console.log(Properties.getProperty('ClientSecret'));
}

```

上記を実行すると、freeeの OAuth アプリの Client ID、Client Secret が保存・ログに表示されます。
一度保存したClientID、ClientSecret は、以下のコードで取り出すことができます。

```javascript

const Properties = PropertiesService.getScriptProperties();

// ClientID を取得

Properties.getProperty('ClientId');

//Client Secret を取得

Properties.getProperty('ClientSecret');

```




# コード作成

Google Apps Script のコードを記述します。 [apps-script-oauth2](https://github.com/googleworkspace/apps-script-oauth2)のReadmeに書かれているコードを流用し、freeeアプリに合わせて書き換えました。

```javascript
const Properties = PropertiesService.getScriptProperties();
const driveService = getDriveService ();

function getDriveService () {
  return OAuth2.createService ('freeeAPI')
    .setAuthorizationBaseUrl (
      'https://accounts.secure.freee.co.jp/public_api/authorize'
    )
    .setTokenUrl ('https://accounts.secure.freee.co.jp/public_api/token')
    .setClientId (
      Properties.getProperty('ClientId')
    )
    .setClientSecret (
      Properties.getProperty('ClientSecret')
    )
    .setCallbackFunction ('authCallback')
    .setPropertyStore (PropertiesService.getUserProperties ());
}

function onOpen () {
  SpreadsheetApp.getUi ()
    .createMenu ('freee API連携')
    .addItem ('認可処理', 'showAuth')
    .addItem ('ログアウト', 'logout')
    .addToUi ();
}

function createModelessDialog (html, title) {
  const htmlOutput = HtmlService.createHtmlOutput (html)
    .setWidth (360)
    .setHeight (120);
  SpreadsheetApp.getUi ().showModelessDialog (htmlOutput, title);
}

function showAuth () {
  if (!driveService.hasAccess ()) {
    const authorizationUrl = driveService.getAuthorizationUrl ();
    const template = HtmlService.createTemplate (
      '<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' +
        'freee APIの認可をします。'
    );
    template.authorizationUrl = authorizationUrl;
    const page = template.evaluate ();
    const title = 'freeeアプリの認可処理';

    createModelessDialog (page, title);
  } else {
    showUser ();
  }
}

function authCallback (request) {
  const isAuthorized = driveService.handleCallback (request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput ('Success! You can close this tab.');
  } else {
    return HtmlService.createHtmlOutput ('Denied. You can close this tab');
  }
}

function showUser () {
  const response = UrlFetchApp.fetch (
    'https://api.freee.co.jp/api/1/users/me',
    {
      headers: {
        Authorization: 'Bearer ' + driveService.getAccessToken (),
      },
    }
  );

  const myJson = JSON.parse (response);

  Browser.msgBox ('OAuth認可済みです。\\n認可されたユーザー名：' + myJson.user.display_name);
}

function logout () {
  driveService.reset ();
  const mes = 'freeeアプリからログアウトしました。';
  const logoutTitle = 'ログアウト終了';

  createModelessDialog (mes, logoutTitle);
}

```

# 実際の挙動


スプレッドシートのメニューに「認可」「ログアウト」の操作メニューが追加されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/fa984de7-76a3-ff4a-a8ea-c0085c077b08.png)



* 「認可処理」をクリックすると、OAuth処理が始まリます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/da176631-5947-d13f-bed2-7df3aded7ffa.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/7d348582-4b6a-909d-059b-55eb4c7aced5.png)


成功したあともう一度「認可」を押すと、認可ユーザーの情報が表示されます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/2864bf40-be6c-7fb0-dacd-e423a4e93df6.png)



「ログアウト」をクリックすると、freeeアプリからログアウト処理されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83767/bab4a250-bae1-21ea-080f-691397273642.png)