Microsoft Graph APIをNodejsで実装しよう

# はじめに
本記事では **Microsoft Graph API** を利用するための手順を、以下の流れで解説します。

- Azure ポータルでのアプリ登録
- 認可コードとアクセストークンの取得（Postman使用）
- `refresh_token` を使ったトークン更新
- TypeScript でのトークン管理クラス実装例

Microsoft 365（Outlook、Teams、SharePoint など）と連携したい方は、ぜひ参考にしてください。

# 1. Azureポータルでアプリ登録

1. Azure ポータルにアクセス→`https://portal.azure.com`
2. 左メニューから **「Microsoft Entra ID」** を選択  
3. 左メニューから **「App registrations」** → **「+ New registration」**

#### 登録時の入力項目
| 項目 | 値 |
|------|----|
| **Name** | 任意のアプリ名 |
| **Supported account types** | *Accounts in this organizational directory only* |
| **Redirect URI** | Web を選択し、`https://oauth.pstmn.io/v1/callback` を入力 |

# 2. アプリ情報の確認
左メニューの **「Overview」**（または「General」）から、以下をメモします。

| 項目 | 内容 |
|------|------|
| **Application (client) ID** | クライアントID |
| **Directory (tenant) ID** | テナントID |

# 3. API Permissions の設定
1. 左メニューから **「API permissions」** を開く  
2. 「**Add a permission**」をクリック  
3. 「**Microsoft Graph**」→「**Delegated permissions**」を選択  
4. 必要な権限を追加  

#### よく使う権限一覧
| 必須 | 権限                         | 用途                                               |
|------|------------------------------|----------------------------------------------------|
| ✅   | `openid`                     | ユーザー識別子（sub）を取得するため               |
| ✅   | `offline_access`             | refresh token を取得して長期間認証を維持するため  |
|      | `ChannelMessage.Send`        | Teams のチャネルにメッセージを送信する             |
|      | `ChatMessage.Send`           | ユーザーとの 1:1 チャットにメッセージを送信する    |
|      | `Mail.Send`                  | Outlook（Microsoft 365）のメールを送信する          |
|      | `Mail.Read`                  | Outlook メールを読み取る                            |
|      | `Sites.ReadWrite.All`        | SharePoint のすべてのサイトに対する読み書き権限     |
|      | `Channel.ReadBasic.All`      | Teams チャネルの基本情報（名前など）を取得する     |
|      | `Calendars.ReadWrite`        | ユーザーのカレンダー予定の取得・追加・変更・削除   |
|      | `Calendars.ReadWrite.Shared` | 共有された他ユーザーのカレンダーに対して同様の操作 |

# 4. クライアントシークレットの作成
1. 左メニューから **「Certificates & secrets」** を選択  
2. 「**+ New client secret**」をクリック  
3. **Description** と **有効期限（最大24か月）** を設定  
4. 作成直後に表示される **Secret Value** を必ずメモ  

# 5. 認可コードの取得（ブラウザ）

以下のURLにアクセスして認可コード（`code`）を取得します：

```
https://login.microsoftonline.com/{Tenant ID}/oauth2/v2.0/authorize?client_id={Client ID}&response_type=code&redirect_uri=https://oauth.pstmn.io/v1/callback&scope={Scope}&response_mode=query
```
- `{Tenant ID}`：Directory (tenant) ID
- `{Client ID}`：Application (client) ID
- `{Scope}`：`openid%20offline_access%20ChannelMessage.Send` のように `%20` 区切りで

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

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

この中の `code` をメモします。

# 6. リフレッシュトークンの取得（Postman）
#### リクエスト設定

- **Method**: POST  
- **URL**: `https://login.microsoftonline.com/{Tenant ID}/oauth2/v2.0/token`  
- **Body**: `x-www-form-urlencoded`

| Key | Value |
|-----|-------|
| `client_id` | {Client ID} |
| `scope` | `openid offline_access ChannelMessage.Send`（半角スペース区切り） |
| `code` | 前ステップで取得した code |
| `redirect_uri` | `https://oauth.pstmn.io/v1/callback` |
| `grant_type` | `authorization_code` |
| `client_secret` | 作成した Secret Value |

トークン取得のレスポンスに含まれる JSON から、`refresh_token`を安全な場所に保存しましょう。


```json
{
  "token_type": "Bearer",
  "expires_in": 3599,
  "access_token": "...",
  "refresh_token": "..."
}
```

# 7. TypescriptでGraph APIクラスを実装する
#### トークン管理を実装する
```typescript:Graph.ts
import credentials from "@utils/constants/crendentials";

const REDIRECT_URI = "https://oauth.pstmn.io/v1/callback";
class Graph {
  private expiredAt = Date.now();
  private refreshToken: string = credentials.GRAPH_REFRESH_TOKEN;
  private accessToken: string = "";

  private async checkToken(): Promise<void> {
    if (Date.now() >= this.expiredAt) {
      await this.refreshAccessToken();
    }
  }

  private async refreshAccessToken(): Promise<void> {
    const url = `https://login.microsoftonline.com/${credentials.GRAPH_TENANT_ID}/oauth2/v2.0/token`;
    const SCOPE = [
      "openid",
      "offline_access",
      "ChannelMessage.Send",
      "ChatMessage.Send",
      "Mail.Send",
      "Mail.Read",
      "Sites.ReadWrite.All",
      "Channel.ReadBasic.All",
      "Calendars.ReadWrite",
      "Calendars.ReadWrite.Shared"
    ];
    
    const body = {
      client_id: credentials.GRAPH_CLIENT_ID,
      scpe: SCOPE.join(" "),
      refresh_token: this.refreshToken,
      redirect_uri: REDIRECT_URI,
      grant_type: "refresh_token",
      client_secret: crendentials.GRAPH_CLIENT_SECRET
    };

    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;
      } else {
        Logger.error(`Failed to refresh access token: $`{res.status} $`{res.data}`)
        throw new Error();
      }
    } catch (error) {
      Logger.error(`Failed to refresh access token: $`{res.status} $`{res.data}`)
      throw new Error();
    }
  }
}

export default new Graph();
```

# 8. 使用例：`/me` エンドポイントを叩く
#### getMeを実装する
```typescript:Graph.ts
class Graph {
  public async getMe() {
    await this.checkToken();

    const url = "https://graph.microft.com/v1.0/me";
    const res = await Postman.get(url, this.accessToken);
    console.log(JSON.stringify(res.data, null, 2));
  }
}
```
#### 使ってみる
```typescript:sandbox.ts
import "module-alias/register";
import Graph from "@class/manager/Graph";

(async () => {
  await Graph.getMe();
  process.exit(0);
})();
```

## おわりに
以上で Microsoft Graph API を使うための準備と TypeScript 実装の一例まで解説しました。

Microsoft Graph API はとても強力で、**Teams の通知自動化**や**Outlook メール管理**、**SharePoint ファイル操作**など多彩な用途に活用できます。

必要に応じて、今回のクラスに `/me/messages` や `/teams/{team-id}/channels` などの追加実装も可能です。お気軽にコメントください！

もしよければ Qiita のいいね ❤️ やフォローもよろしくお願いします！