# Cursor実装プロンプト｜会社のOutlookメールを読む小さなWebアプリ

このファイルは、第8章の実装をCursorへ頼むときに、チャットへそのまま貼る指示書です。説明書の本文は同じサイトのトップページです。

確認日: 2026年10月19日

## 貼る前に、自分で終えていること

Microsoft Entra の画面操作は、人間が説明書の第4章から第6章まで終えたあとに行います。Cursorには管理画面を操作させません。

手元の `.env.dev` には、次の値だけを自分で入れます。このチャットには貼りません。

- ディレクトリ (テナント) ID
- アプリケーション (クライアント) ID
- クライアント シークレットの「値」
- 自分で作った、長いセッション用のランダム文字列

## ここから下を、新しいCursorチャットへ貼る

```text
会社用 Microsoft 365 Outlook のメールを、サインインした本人だけが読む小さな練習用Webアプリを、新しいフォルダに作ってください。

作業場所:
- 新しく作るフォルダ名: outlook-mail-reader
- 置き場所は、読者自身のホームの中など、既存アプリとは別の場所
- 既存アプリのフォルダは読まない、変更しない、パッケージを追加しない
- 既存アプリが Swift / iOS だけの場合、Outlook接続をそちらへ足さない

公式の型:
- 認証は Microsoft Authentication Library（MSAL）の @azure/msal-node
- 構成は公式チュートリアル「Sign in users in a Node.js/Express web app」に合わせる
  https://learn.microsoft.com/ja-jp/entra/identity-platform/tutorial-v2-nodejs-webapp-msal
- プロトコルは OAuth 2.0 認可コードフロー + PKCE（code_challenge_method は S256）
- トークンの応答モードは form_post。コールバックは POST で受ける
- Microsoft Graph は https://graph.microsoft.com/v1.0
- 古い Outlook REST API（https://outlook.office.com/api/）は使わない
- Microsoft Graph Toolkit は新規採用しない

アプリの種類:
- サーバーで動く Express のWebアプリ（機密クライアント）
- Entra のプラットフォームは Web
- リダイレクト URI は http://localhost:3000/auth/redirect
- ログアウト後の戻り先は http://localhost:3000/
- ブラウザだけで動くSPAにはしない。@azure/msal-browser は入れない
- クライアント シークレットをブラウザ、HTML、ログ、エラー画面へ出さない

スコープは次の2つだけ:
- User.Read
- Mail.ReadBasic
MSAL が既定で足す OpenID Connect のスコープ（openid、profile、offline_access）はそのままでよい。
Mail.Read、Mail.ReadWrite、Mail.Send、Mail.ReadBasic.All、アプリケーションの許可、.default は追加しない。
メール本文、bodyPreview、添付ファイルは要求しない。

作る機能:
1. トップページに「Microsoft でサインイン」ボタン
2. 認証コールバック（POST /auth/redirect）
3. サーバーセッションの中だけでトークンを保持
4. GET https://graph.microsoft.com/v1.0/me で、サインインした本人の表示名とメールアドレスを確認
5. GET https://graph.microsoft.com/v1.0/me/mailFolders/inbox/messages で受信トレイを読む
   - $select=subject,receivedDateTime
   - $top=10
   - $orderby=receivedDateTime desc
   - $orderby が InefficientFilter で失敗したら、並び替えなしで取り直し、画面側で受信日時の新しい順に並べる
6. 画面には件名と、Asia/Tokyo で表記した受信日時だけを出す。件名がないメールは「（件名なし）」
7. 失敗したときは、状態コードとエラーコードを日本語の画面に出す。トークン、シークレット、認可コードは出さない
8. 「ログアウト」で、サーバーのセッションを破棄し、Microsoft のログアウトエンドポイントへ送る
9. メールの送信、削除、既読変更、下書き作成は実装しない。POST /me/sendMail を書かない

秘密情報:
- 設定は .env.dev から読む。dotenv を使う
- .env.dev.example には次のプレースホルダだけを書く。本物の値は書かない

CLOUD_INSTANCE=https://login.microsoftonline.com/
TENANT_ID=00000000-0000-0000-0000-000000000000
CLIENT_ID=00000000-0000-0000-0000-000000000000
CLIENT_SECRET=replace-with-the-secret-value
REDIRECT_URI=http://localhost:3000/auth/redirect
POST_LOGOUT_REDIRECT_URI=http://localhost:3000/
GRAPH_API_ENDPOINT=https://graph.microsoft.com/
EXPRESS_SESSION_SECRET=replace-with-a-long-random-string

- CLOUD_INSTANCE と GRAPH_API_ENDPOINT の末尾スラッシュは残す
- .gitignore に .env.dev と node_modules を入れる
- express-session のクッキーは httpOnly と sameSite=lax。localhost の練習では secure は false。本番用の secure は true にするコメントを残す
- セッションの秘密は EXPRESS_SESSION_SECRET を使う。クライアント シークレットを流用しない
- MSAL のログは piiLoggingEnabled: false
- console.log に accessToken、refreshToken、clientSecret、authorization code、req.session 全体を出さない
- 画面へ値を出すときは innerHTML を使わず、テキストとしてエスケープする

実装の置き場所:
- package.json
- .gitignore
- .env.dev.example
- authConfig.js
- auth/AuthProvider.js（公式チュートリアルの PKCE、state、トークンキャッシュの形）
- routes/auth.js
- routes/mail.js
- app.js
- views/（トップ、メール一覧、エラー。日本語）

依存パッケージは公式チュートリアルと同じ系統にする:
- express
- express-session
- dotenv
- @azure/msal-node
- axios
ビューは追加のテンプレートエンジンなしの HTML 文字列でも、公式どおりの hbs でもよい。初心者がファイルを開いて追える形を優先する。

完了条件:
- npm start で http://localhost:3000 が開く
- 未設定の環境変数があるときは、どの名前が足りないかだけを表示して起動を止める。値は表示しない
- コードの中に Mail.Send、sendMail、Mail.ReadWrite が存在しない
- README に、Entra 側で人間が済ませる手順への参照と、起動コマンドだけを日本語で書く
- この作業中に、本物のテナントへログインしない。管理者の同意を実行しない。Graph を呼び出してメールを取得しない
```

## 人間がアプリを起動するとき

1. `.env.dev.example` を `.env.dev` という名前で複製する。
2. プレースホルダを、自分の Entra の値に差し替える。シークレットの「値」を入れる。シークレット ID は入れない。
3. ターミナルで、そのフォルダだけを開き、`npm install` のあと `npm start` を実行する。
4. ブラウザで `http://localhost:3000` を開く。
5. 説明書の第9章の成功条件を、上から順に確認する。
