Box APIを使い始める。JWT認証でBOXにつないでみる。


## はじめに

Box APIの使いかたについて、簡単に導入方法を紹介します。


## Box APIを利用するための前提

前提として、BOXテナントの管理者権限を持っていることを前提としています。
この記事ではBox Node SDKを利用しますが、基本の考え方は他のSDKを利用する場合、またはSDKを使わずに、REST APIを直接実行する場合でも共通しています。


## リソース

参考にしたリソースはこちら。

Box DEV
https://developer.box.com/
API、各種SDK等、開発に関して様々な情報がまとまっています。

Box Support
https://support.box.com/hc/en-us
Boxの標準環境での使いかたなどがのっています。

どちらも、英語版の他、日本語版が用意されています。



## BOX側でのアプリの作成（開発の事前準備）

#### Box側でアプリ作成

1. Box DEVを開いて、マイアプリをクリックします。
https://ja.developer.box.com/
まだBoxにログインしていない場合は、ここで管理者としてBoxにログインしてください。この操作でBoxの画面の左ナビゲーションの中に「開発者コンソール」というリンクが表示されるようになります。

1. 開発者コンソールを開きます
1. アプリの新規作成をクリックします

1. アプリの種類を聞かれるので、「カスタムアプリ」を選択します。
このアプリの種類は、アプリ作成のウィザードを選択しているだけです。あとから設定を変更することでアプリのタイプを変えることができます。何も考えずにカスタムアプリで大丈夫です。

1. 認証方法の選択をします。ここでは「JWTを使用したOAuth 2.0（サーバー認証）」を選択します。
このガイドではBox Platformの中心的な認証方法である、JWT方式で説明していきます。
  
1. アプリケーションの名前をつけます。

1. アプリが作成されるので、アプリの表示ボタンを押します。
この画面で表示されているcurlのコマンドの使用例は単なる例なので、無視して次に進んでください。

1. 作成したアプリの構成画面が表示されます。以下ざっくりと説明です。

- Developerトークン
    - 正規の認証を経なくとも、このDeveloperトークンを利用することでAPIを試すことができます。
    - 60分しか使えないので、再発行する必要があります。
    - 今回は利用しません。
- OAuth2.0資格情報
    - クライアントIDはあとでアプリケーションを承認する時に必要になります。
    - 公開キーの追加と管理で「公開／秘密キーペアを生成」を押した時に作られるConifgファイルにこれらの情報が含まれます。
- アプリケーションアクセス
    - とりあえずデフォルトのままで。
    - 既存ユーザーとコンテンツにアクセスする必要があればEnterpriseを選びます。
- アプリケーションスコープ → 一旦デフォルトのままにします。
- 高度な機能 → 一旦デフォルトのままにします。
- 公開キーの追加と管理
    - こちらで公開／秘密キーペアを生成します。
    - **「公開／秘密キーペアの生成」をクリックすると、configファイルがダウンロードされるので後で利用します。config.jsonとリネームしておきます。**
    - git等で共有しないようにします。このファイルの中身は、プロダクション環境では環境変数等に保存するようにするのがおすすめです。

#### Box側でアプリの承認
    
1. マイアプリ > 一般 > アプリの承認で、承認用に送信ボタンを押します。
  - クライアントIDが表示されるのでコピーしておきます。
  - または、マイアプリ > 当該アプリ > 構成 > OAuth2.0資格情報 のクライアントIDをコピーします。
  - 管理コンソールにアクセスできるのであれば、この送信ボタンを押す必要はありません。
 
1. 管理コンソール > アプリ > カスタムアプリ・タブを開き、 「新しいアプリケーションを承認」ボタン押して、コピーしたクライアントIDをペーストし、承認します。
  - ここで、構成の中で指定した各種スコープ等を確認されるので、承認ボタンを押します。
  

これでBOX側の準備は最低限ととのいました



## サンプルコードの作成手順

ここからはカスタムアプリケーションの作成です。

#### 前提

* node.jsが導入済みで、nodeコマンドが実行できること

* 筆者の環境では、nodeのバージョンは、v12.16.1を利用しています。

* npmまたはyarnが導入済みであること

  

#### nodeプロジェクトの作成

```sh

# 任意の名前でフォルダを作成し、入ります。
mkdir box-quick-start 
cd box-quick-staret 
# nodeのパッケージの初期化をします。yarn / npmお好みで。
yarn init -y 
```



#### Box-node-sdkを追加

```sh
# box-quick-staretフォルダで実行。box-node-sdkを導入します
yarn add box-node-sdk 
```

参考：SDKはこれを利用しています。 https://github.com/box/box-node-sdk



生成されたpackage.json

```json:package.json
{
  "name": "box-quick-start",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "box-node-sdk": "^1.32.0"
  }
}
```

#### ダウンロードしておいたconfig.jsonをコピー
box-quick-staretフォルダにconfig.jsonをコピーします。
プロダクション環境ではこのファイルの内容が漏洩しないように気をつけてください。

#### ファイルを追加

```sh
# box-quick-staretフォルダで実行。ファイルを追加します。
touch index.js
```



#### Index.jsの編集

任意のエディタを開き、以下のコードをペースト

```js:index.js
const boxSDK = require("box-node-sdk"); // SDKです
const config = require("./config"); // config.json: Boxからダウンロードしたファイルです。

const main = async () => {
  // config.json を元に、sdkを作成。
  const sdk = await boxSDK.getPreconfiguredInstance(config);

  // Service AccountのClientオブジェクトを取得。
  // 管理者ユーザーとは別。管理者ユーザーのコンテンツはこのままでは見れないので注意。
  // Service Account/App Userのどちらでもコンテンツを操作可能だが、
  // 複数ユーザーで利用する場合はコンテンツの所有者や変更履歴の明示化のためAppUserを作成することが望ましい。
  const saClient = await sdk.getAppAuthClient("enterprise");

  // App Userを作成する。本来は再利用のために、戻ってきたappUserのIDを保存する必要がある。
  const appUser = await saClient.enterprise.addAppUser("Mike Morris");

  // 作成したAppUserのスコープでClientを作成
  const auClient = await sdk.getAppAuthClient("user", appUser.id);

  // アップロードのサンプル。
  // package.jsonを、AppUserのホームフォルダにアップロードしてみる
  const fs = require("fs");
  const stream = fs.createReadStream("./package.json");

  // AppUserでホームフォルダにファイルをアップロード
  const file = await auClient.files.uploadFile("0", "package.json", stream);

  // アップロードされたファイルの情報を確認
  console.log(file);

  // AppUserのホームフォルダを一覧
  const auItems = await auClient.folders.getItems("0");

  // アップロードしたpackage.jsonがリストに入っていることを確認
  console.log(auItems);
};
main();


```



上記ファイルの実行方法

```zsh
# box-quick-staretフォルダで実行
node index.js
```



#### まとめ

以下のことが簡単に実現できました。

- box-node-sdkの導入方法と、JWT認証の利用の仕方
- Service Accountの利用の仕方
- App Userの作成の仕方
- ホームフォルダ以下のコンテンツの一覧の取得方法



この他様々な操作は、以下のbox-node-sdkと、API Referenceを調べることで使いかたはすぐにわかると思います。

box-node-sdk: https://github.com/box/box-node-sdk

API Reference: https://developer.box.com/reference/





以下、認証方法に関してのおまけ情報です。



## BOXの認証方法について

カスタムアプリケーションからBOXにAPIで接続する場合、基本的に以下の２通りの方法があります。（厳密にいうとWeb統合やBox Skillsの方式の認証などもありますが、ここでは割愛します。）

* 標準OAuth 2.0 （ユーザー認証）
* JWTを使用したOAuth 2.0（サーバー認証）



２つの認証方式には、以下のような特徴があります。

#### 標準OAuth 2.0 （ユーザー認証）

* https://ja.developer.box.com/guides/authentication/oauth2/

* Box Devガイドにかかれている、OAuth 2.0を使用する最適なパターン

  * 既存のBoxアカウントを持っているユーザーを使用する
  * ユーザーにBoxを使用していることを知らせる必要がある
  * ユーザーのBoxアカウントにデータを保存し、アプリケーションのBoxアカウントには保存しない

* 注意：BoxにはFair Use Policyというものがあり、実際に人間が使っていないアカウント（ロボットやプログラムのみが利用）が、この認証方式を利用してはいけないとされている。 https://cloud.box.com/s/cotkk9k3op6zw07rmr6jb3mhgv2dix4a

    

    

#### JWTを使用したOAuth 2.0（サーバー認証）

* https://ja.developer.box.com/guides/authentication/jwt/
* Service Accountと呼ばれるAPI専用の管理者のようなユーザーに対して認証を行う。バックエンドサーバー内で認証しておく。
* 一度Service Accountが認証されると、以降はService Accountを利用して、App Userと呼ばれる、利用者用アカウントをBoxの管理対象ユーザーとは無関係に自由に好きなだけ払い出すことができる。BoxはService Accountだけを認証し、App Userの認証を行わない。
* この方式を利用し、個々のユーザーに利用させるパターンにする場合は、App Userとカスタムアプリのユーザーのマッピングを独自に管理する必要がある
* バックエンドサーバー内から認証されることを想定（技術的には可能だが、強力な権限を持つService Accountのパスワードやアクセストークンが漏洩してしまうので、ブラウザ上だけでこの方式の認証を行うべきではない）
* Box Platformというプラン体系の中心的な位置づけの認証モデル。上記のFair Use Policyは適用外。
* Box Devガイドにかかれている、JWT認証を使用する最適なパターン
  * Boxアカウントを持たないユーザーを使用する
  * 独自のIDシステムを使用する
  * ユーザーにBoxを使用していることを認識させたくない
  * アプリケーションのBoxアカウントにデータを保存し、ユーザーのBoxアカウントには保存しない
* ガイドの説明以外で、JWT認証が活用できそうなパターン
  * 業務に特化した専用UIを持つカスタムアプリケーションを作成し、コンテンツをBOXに預ける。
  * メールアドレスを持っていない人たち向けのカスタムアプリケーションを作成
  * バッチプログラム等の専用のアカウントとして利用。
  * 既存サービスから利用することで、Boxを内部で利用しつつ、既存Saasの販売を行う。



APIの実行、SDKの利用ともに、認証方法はどちらでも構いません。

この記事では主にJWT認証を利用した方法を説明しています。

参考： https://ja.developer.box.com/guides/authentication/jwt/with-sdk/