0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Node.js】Box APIでOAuth認証を実装する(アクセストークン自動更新付き)

Posted at

はじめに

Box API を OAuth2 認証経由で利用するまでの手順と、TypeScript によるトークン管理・API 呼び出しの実装方法を紹介します。

1. アプリ登録(Box Developer Console)

  1. https://app.box.com/developers/consoleにアクセス
  2. アプリを作成
    • Custom App (OAuth 2.0 with JWT or OAuth 2.0)」を選択
  3. 必要なスコープ(権限)を設定(例: Read and write all files and folders
  4. 管理者の承認をリクエスト(企業アカウントで必要)

2. クライアント情報を取得

  • Client IDClient Secret をメモ
  • リダイレクト URI に以下を設定
    https://oauth.pstmn.io/v1/callback
    

企業内で Self-signed certificate を使用している場合、SDK の使用は困難なため、API による実装を選択します。

3. 認可コードの取得(ブラウザ)

以下の URL にアクセス:

https://account.box.com/api/oauth2/authorize?client_id={Client ID}&response_type=code

アクセス後、次のような URL にリダイレクトされます:

https://oauth.pstmn.io/v1/callback?state=&code=xxx

この code を控えておきます。

4. リフレッシュトークンの取得(Postman)

リクエスト設定

  • Method: POST
  • URL: https://api.box.com/oauth2/token
  • Body: x-www-form-urlencoded
Key Value
client_id {Client ID}
client_secret {Client Secret}
code 前ステップで取得した code
redirect_uri https://oauth.pstmn.io/v1/callback
grant_type authorization_code

レスポンス例

{
  "access_token": "...",
  "refresh_token": "...",
  "expires_in": 3599,
  "token_type": "Bearer"
}
  • refresh_token はファイル等に安全に保存して管理します。

5. TypeScript で Box API を使うクラスを実装

refresh_token を管理するストレージ

storage.json
{
  "BOX_REFRESH_TOKEN": "xxx"
}

トークン管理付きの Box クラス実装

以下に、あなたの Box.ts クラスを機能ごとに分割して説明します。各部分の役割や考え方も補足しています。

インポート

Box.ts
import fs from "fs";
import credentials from "@utils/constants/credentials";
import Postman from "@utils/Postman"; // 自作のHTTPユーティリティ
import Logger from "@utils/Logger";   // 自作のログユーティリティ
  • fs: refresh_token を保存しているファイル(例:storage.json)の読み書きに使用。
  • credentials: client_idclient_secret など、Box API 認証に必要な情報を保持。
  • Postman: HTTP リクエストを簡潔に記述するためのラッパー。
  • Logger: エラー発生時のログ出力用。

クラスの定義と初期状態

Box.ts
class Box {
  private expiredAt = 0;
  private accessToken = "";
  • expiredAt: アクセストークンの有効期限(Date.now() 形式のタイムスタンプ)。
  • accessToken: 現在のアクセストークン(API 呼び出し時に使用)。
Box.ts
private async checkToken(): Promise<void> {
  if (Date.now() >= this.expiredAt) {
    await this.refreshAccessToken();
  }
}
  • アクセストークンが期限切れであれば、refreshAccessToken を呼び出して更新します。
  • 毎回の API 呼び出し前にチェックすることで、トークン切れによる失敗を防止します。

refresh_token の取得/保存

Box.ts
private getRefreshToken(): string {
  const path = "storage.json";
  const storage = JSON.parse(fs.readFileSync(path, "utf-8"));
  return storage.BOX_REFRESH_TOKEN;
}

private setRefreshToken(token: string): void {
  const path = "storage.json";
  const storage = JSON.parse(fs.readFileSync(path, "utf-8"));
  storage.BOX_REFRESH_TOKEN = token;
  fs.writeFileSync(path, JSON.stringify(storage, null, 2));
}
  • getRefreshToken: ローカルファイルから refresh_token を読み込みます。
  • setRefreshToken: 取得した新しい refresh_token をファイルに保存します。

Box の refresh_token は毎回変わるため、毎回更新して保存する必要があります。

アクセストークンのリフレッシュ処理

Box.ts
private async refreshAccessToken(): Promise<void> {
  const url = `https://api.box.com/oauth2/token`;
  const body = {
    client_id: credentials.BOX_CLIENT_ID,
    client_secret: credentials.BOX_CLIENT_SECRET,
    refresh_token: this.getRefreshToken(),
    grant_type: "refresh_token",
  };

  try {
    const res = await Postman.postWithEncodedString(url, body);

    if (res.status === 200) {
      this.accessToken = res.data.access_token;
      this.expiredAt = Date.now() + res.data.expires_in * 1000;
      this.setRefreshToken(res.data.refresh_token);
    } else {
      Logger.error(`Token refresh failed: ${res.status} ${JSON.stringify(res.data)}`);
      throw new Error("Token refresh failed");
    }
  } catch (error) {
    Logger.error(`Error refreshing token: ${error}`);
    throw error;
  }
}
  • refresh_token を使って新しい access_token を取得。
  • レスポンスが 200 の場合:
    • アクセストークンを更新
    • 有効期限を再設定(現在時刻+期限秒数)
    • refresh_token を保存
  • エラー時はログを出力し、例外をスロー。

ファイルのメタデータ取得API

Box.ts
public async getFileMeta(id: string): Promise<void> {
  await this.checkToken();

  const url = `https://api.box.com/2.0/files/${id}`;
  const res = await Postman.get(url, this.accessToken);
  console.log(JSON.stringify(res.data, null, 2));
}
  • 指定された fileId をもとに Box API からメタデータを取得。
  • API を叩く前に checkToken() を必ず呼んで、アクセストークンの有効性を保証

クラスのインスタンスをエクスポート

Box.ts
export default new Box();
  • Box クラスのシングルトンインスタンスをエクスポート。
  • 複数箇所で共通のトークン管理が可能になります。

まとめ:このクラスの設計思想

  • 責務の分離
    • 認証処理、トークン管理、API 呼び出しをそれぞれ独立して実装。
  • 堅牢性
    • 有効期限切れを事前に検知し、自動更新。
  • セキュリティと可搬性の両立
    • refresh_token をローカルファイルで保持し、SDK に依存せずに軽量実装。

6. 使用例

sandbox.ts
import "module-alias/register";
import Box from "@class/manager/Box";

(async () => {
  await Box.getFileMeta("1234567890"); // 取得したいファイルID
  process.exit(0);
})();

補足:セキュリティ上の注意

  • client_secretrefresh_token.env や secure vault 等で安全に保管してください
  • ログや Git にアクセストークンを記録しないように注意してください
0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?