Boxウェブアプリ統合を作ってみた

## 今回作るもの

Boxのウェブアプリ統合をnode.jsで作ってみます。

ウェブアプリ統合とは、標準のBoxの画面にボタンを表示し、そのボタンをクリックしたら外部で連携されたアプリケーションが何らかの処理を行うという仕掛けのことです。

Boxのテナントの設定にもよりますが、DocuSignなど、予めおすすめのボタンがいくつかついていたりします。あのボタンは自前で追加可能です。

今回は、任意のファイルを、3分間ロックしてダウンロードも禁止にするという使いみちのないウェブアプリ統合を作ってみます。Boxにはそもそも編集中に他人からファイルを変更されないように悲観ロックを行う機能がついているのですが、単純にこの機能を外部から利用してロックをかけるというものを作ってみます。


## 仕組み

基本的なしくみとしては以下のような流れです。

1. 外部に自前のカスタムアプリケーション・サーバーを用意する。
2. Boxの開発者コンソールからアプリの登録をし、ウェブアプリ統合を有効にする。
3. Boxの画面からウェブアプリ統合のボタンを押すと、カスタムアプリケーション・サーバーにHTTPでリクエストが飛ぶ。多くの場合カスタムアプリケーション側からBoxにアクセスすると思うが、AuthCodeを飛ばせるので、これをaccessTokenに変換することで、Box側にアクセスが可能になる
4. カスタムアプリケーションで好きな処理を行う



上記の流れは、基本的にWebhookや、Skillsでも細かい差異はありますが、基本の流れはほぼ同じようです。
ウェブアプリ統合のトリガーはボタンのクリックなのに対して、WebhookやSkillsのトリガーはコンテンツのライフサイクルフックなところも異なりそうです。
今回のサンプルでは、カスタムアプリケーションをHerokuに置き、node.js、box-node-sdkを利用して作ってみます。



## 事前に必要なもの

今回のウェブアプリ統合を試すためにひつようなものは、以下の通りです。

* Node.js
* Herokuのアカウント登録（無料）と、herokuコマンド
* git


## 手順


### Boxでアプリケーションの作成



開発者コンソールを開き、アプリケーションを作成します。

「カスタムアプリ」、または「企業統合」のどちらかを選びます。
どちらを選ぼうが、認証方式を、「標準OAuth 2.0（ユーザー認証）」を選べば大丈夫です。
OAuth 2.0資格情報を後で使うのでひかえておきます。
統合に関しての設定も後で必要になりますが、外部アプリケーションを作成してから設定します。



### 外部アプリケーションの準備



外部アプリケーション用のプロジェクトを作ります。

```sh
# 任意のフォルダを作ります
mkdir web-integ-file-locker
cd web-integ-file-locker

# yarnでもnpmでもおすきなものを・・
yarn init -y

# box-node-sdk、express、momentを追加しておきます 
yarn add box-node-sdk express moment

```



生成されたpackage.jsonを改造する scripts/startを入れる。（Herokuがstartを実行する）

```json:package.json
{
  "name": "web-integ-file-locker",
  "version": "1.0.0",
  "main": "app.js",
  "license": "MIT",
  "scripts": {
    "start": "node app"
  },
  "dependencies": {
    "box-node-sdk": "^1.32.0",
    "express": "^4.17.1",
    "moment": "^2.24.0"
  }
}

```

任意のエディタで、`web-integ-file-locker`フォルダ直下に`app.js` を作ります。
認証に関しては、標準OAuth2.0 （3-legged）のやり方になります。
https://github.com/box/box-node-sdk/blob/master/docs/authentication.md#traditional-3-legged-oauth2


```javascript
const express = require("express");
const moment = require("moment");
const boxSDK = require("box-node-sdk");

const app = express();

app.use(express.urlencoded({ extend: false }));

app.post("/lockfile", async (req, res) => {
  try {
    // clientIDと、clientSecretは、構成のクライアントIDとクライアント機密コード
    const sdk = new boxSDK({
      clientID: process.env.clientID,
      clientSecret: process.env.clientSecret,
    });

    // 統合のメニューで、コールバックパラメーターにPOSTで指定したものが、bodyの下に入る
    // authCodeというのは、本来、BOXのログイン画面にリダイレクトして認証が通った後に戻されるAuthCode（だと思う）。
    // ここでは認証画面はスキップしてる。
    const authCode = req.body.authCode;
    const fileId = req.body.fileId;

    // authCodeをBoxAPIに投げて、tokenInfoを取得する。
    // authCodeはaccessTokenではないので、accessTokenをつかってClientを作っても動かない。
    // tokenInfoは、accessToken, refreshToken等が含まれているオブジェクト。
    const tokenInfo = await sdk.getTokensAuthorizationCodeGrant(authCode);

    // box-node-sdkの場合、sdk.getPersistentClient(tokenInfo, tokenStore)
    // を利用して、accessTokenが期限切れになると自動的にrefreshTokenをつかって
    // リフレッシュする仕組みも使えるが、単発の処理で終わるWebApp統合では不要なため、
    // シンプルに今とれたaccessTokenの生存期間内に処理が終わる前提で、
    // basicClientにaccessTokenのみを渡して利用する。
    const client = sdk.getBasicClient(tokenInfo.accessToken);

    // lockに渡すオプション。今から3分後までロックし、ダウンロードも禁止する
    const options = {
      expires_at: moment().add(3, "minutes").format(),
      is_download_prevented: true,
    };

    // 受け取った対象のファイルに対してロックをかける
    await client.files.lock(fileId, options);

    res.status(200).json({
      message: "file locked",
    });
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

const PORT = process.env.PORT || "4000";
app.listen(PORT, (result) => {
  console.log("Express Server started on port: " + PORT);
});

```



Herokuにアプリケーションを作り、デプロイします。

`web-integ-file-locker`フォルダ直下で実行。

```sh
# Heroku上にアプリケーションを作る
heroku create
# （オプション）わかりやすいようにリネームしておく
heroku rename web-integ-file-locker
# gitを有効にする
git init
# gitにここまでの変更をCommitする
git add .
git commit -m "init"
# Herokuにデプロイする
git push heroku master
# Herokuに環境変数を設定する。構成のOAuth 2.0資格情報です。（スペース区切りで複数セット可能）
heroku config:set clientID=0gde912gixav1244tjm5gbxxxxxxxxxx clientSecret=kIJC8i9avpsVXlB5SolnNxxxxxxxxxx

# HerokuのURLを確認（後で統合の設定で使う）
heroku info
```





これで外部アプリケーションは出来上がり。

次に、Boxの統合の設定



### Boxで統合の設定

最初に作成したアプリの画面に戻ります。

左のタブから統合を選択します。

「ウェブアプリ統合を作成」ボタンを押します。

以下の値を入れます

* 統合名：FileLocker （任意のもの）
* 説明：適当な内容
* コールバック設定 > クライアントコールバックのURL
    * ここに、上記のHeorkuでつくったURL + "/lockfile"を入れます。
    * 例：`https://web-integ-file-locker.herokuapp.com/lockfile`

* コールバック設定 > プロンプトメッセージ：（適当なもの）

* コールバック設定 > ユーザーエクスペリエンス: 統合をサーバー側でのみ実行する に変更

* コールバックパラメータに以下の項目を追加します


| 方法 | パラメータ名 | パラメータ値  |
| ---- | ------------ | ------------- |
| Post | AuthCode     | ##auth_code## |
| Post | fileId       | ##file_id##   |

    

上記以外は全てデフォルトのままでOK。

![スクリーンショット 2020-04-29 22.22.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45767/5d81e703-9365-bc52-03f5-a701376ae829.png)

画面右上のボタンで保存します。


## 検証


#### Boxから使ってみる



任意のファイルのコンテキストメニューから統合を選ぶと、FileLockerが表示されています。
![スクリーンショット 2020-04-29 22.31.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45767/8e04cf6a-5a32-2bf5-fe4a-914e52374120.png)



このボタンを押してみると、確認画面が表示され、、

![スクリーンショット 2020-04-29 22.32.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45767/0150f31d-ae0d-7758-ad1e-b9d6b9cb213e.png)


確認画面でOKを押すと、ファイルにロックがかかりました。

![スクリーンショット 2020-04-29 22.33.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45767/6c670568-f6ed-4486-b34f-d377f7724d3f.png)


こんな感じでサクッと統合が試せました。
今回は試していませんが、アップギャラリーに登録して、パブリックに公開することも可能です。
今度、なにか作ってアップギャラリーに登録してみようと思います。