SAP Cloud Integration(SAP Integration Suite / Cloud Integration)から Microsoft 365(Microsoft Graph)へ OAuth 2.0(Authorization Code+refresh token)で接続しメール送信しようとすると、トークン取得は成功するのに invalid_grant(AADSTS65001) で失敗する――この症状は「同意したテナント」と「送信元メールボックスが存在するテナント」がズレている時に起きがちです。本記事では、原因の見極め方と、現場で通る解決策を具体的に整理します。
前提:SAP Cloud Integration から Microsoft 365 へメール送信する代表的な方法
まず前提として、SAP Cloud Integration(以降 CPI と表記)から Microsoft 365 にメールを送る方法はいくつかあります。今回のテーマは Microsoft Graph を OAuth 2.0(委任権限)で叩いて送信する構成です。
| 方式 | 認証/権限 | 特徴 | 向いている用途 | 注意点 |
|---|---|---|---|---|
| Microsoft Graph(委任: Delegated) | Authorization Code(ユーザーサインイン)+refresh token | ユーザーとして送信(/me/sendMail が分かりやすい) | 人が関与する運用、ユーザー単位で送信元が変わる | テナント/ユーザー種別のズレ、MFA/CA、更新トークンの失効に左右されやすい |
| Microsoft Graph(アプリ: Application) | Client Credentials(アプリのみ)+Application権限 | サーバー間連携に強い。インタラクティブログイン不要 | システム連携の定期送信、障害通知、帳票送信 | 権限が強力になりがち。Exchange Online 側のアクセス制限設計が重要 |
| SMTP AUTH(レガシー寄り) | Basic / OAuth(環境次第) | 既存資産と相性が良い場合がある | 既存SMTP基盤の置き換え | テナントのセキュリティ方針で無効化されやすい。推奨は Graph 寄り |
今回の AADSTS65001 は、Graph 自体というより Entra ID(旧 Azure AD)の「同意(Consent)」とテナント境界に起因しているケースが多いです。
今回の症状:トークン取得は成功するのに AADSTS65001(invalid_grant)で失敗する
相談で多い流れは次のとおりです。
- Entra ID でアプリ登録を作成
- Microsoft Graph の 委任(Delegated)権限(例:Mail.Send など)を追加
- 管理者同意(Admin consent)も実施
- Authorization Code でアクセストークン/更新トークンの取得は成功
- しかし CPI 側でそのトークン(refresh token での再取得を含む)を使って送信すると失敗
典型的なエラー例(メッセージの骨格)は次のようなものです。
AADSTS65001: The user or administrator has not consented to use the application with ID '...' named '...'.
Send an interactive authorization request for this user and resource.
ここで重要なのは、「トークンが取れている」=「正しいテナント・正しいユーザー・正しい同意状態で運用できる」とは限らないことです。とくに複数テナントが絡むと、見た目は同意済みに見えても「別テナントとして同意していた」「ゲストとして同意していた」などが混ざり、結果として更新トークンの再取得や Graph 呼び出しの段階で AADSTS65001 が噴き出します。
結論:同意したテナントと、送信元メールボックスが存在するテナントが一致していない可能性が高い
今回の現象は、次のどちらか(または複合)で説明できることが多いです。
| パターン | 何が起きているか | 見抜き方(最短) | 効く対処 |
|---|---|---|---|
| ゲストユーザーで認可していた | ログインはできるが、認可されたユーザーが “Guest” 扱いで、そのテナントに Exchange Online メールボックスが無いことがある | トークンを解析すると、UPN に #EXT# が入る等で気づける | アプリを マルチテナント化し、メールボックスを持つホームテナントのユーザーとして認可し直す |
| 同意したテナントが違う | アプリ登録・Admin consent はテナントAだが、実際の送信元メールボックスはテナントB。同意がAにしか存在しない | メールアドレスのドメインや Microsoft 365 管理センターで、メールボックスの所属テナントを確認 | テナントBでも Admin consent(=B側に Enterprise Application / Service Principal を作って同意) |
つまり「同意はしたはずなのに…」という感覚の裏で、“どこに対して同意したのか” がズレているのが本質です。ここを確定させるために、次の切り分けを必ず行います。
切り分け手順:トークンを解析して「誰として」「どのテナントで」認可しているか確定する
CPI の設定画面だけ見ていると、どのテナントのどのユーザーで認可が完了しているかが曖昧になりがちです。最短の切り分けは アクセストークン(JWT)を解析することです。
解析の進め方
- CPI 側で取得できるアクセストークン(必要なら ID トークンも)をコピー
- JWT を解析できるツールに貼り付けて中身(claims)を見る
- tenant(tid/iss)とユーザー(preferred_username / upn)を確認
チェックすべき代表的な項目を表にまとめます(実際の claim 名はエンドポイントや構成で多少変わることがあります)。
| 見る項目 | 何を意味するか | 判断のポイント | よくある落とし穴 |
|---|---|---|---|
| iss(Issuer) | トークンを発行した Entra ID の発行者URL | URL に含まれるテナントIDが「今このトークンが属しているテナント」 | アプリ登録したテナントと “同じ” とは限らない |
| tid(Tenant ID) | トークンのテナントID | メールボックスが存在するテナントIDと一致しているか | ゲストでログインすると “招待先テナント” になりやすい |
| preferred_username / upn | ユーザー識別子(表示名/UPN) | #EXT# を含むならゲストの可能性が高い | 同じ表示名でも別テナントの別ユーザーが存在しうる |
| scp(Scope) | 委任権限として付与されたスコープ | Mail.Send 等が含まれているか | 権限はあってもメールボックスが無いと送れない |
この時点で「発行テナントが想定と違う」「ユーザーがゲスト扱い」が見えたら、ほぼ勝ち筋です。次はパターン別に対処します。
パターンA:ログインユーザーがゲストだった(そのテナントに EXO メールボックスが無い)
複数テナント環境でよくあるのが、“招待された側のテナント” のゲストユーザーで認可してしまうケースです。ゲストユーザーは便利ですが、Exchange Online のメールボックスは通常 ホームテナント側にあります。つまり、ゲストとしてトークンは取れても、送信元として扱うメールボックスが存在しない(または送信として成立しない)状態に陥ります。
このパターンの典型的な状況
- ユーザーは複数の Microsoft 365 テナントに所属している(B2B 招待など)
- ログイン自体はできるが、実際のメールボックスは別テナントにある
- トークン内のユーザー識別子に #EXT# が含まれている、または tid が想定と違う
対処の考え方
狙うべき状態は「メールボックスを持つホームテナントのユーザーとして Graph の委任権限を同意し、そのユーザーで送信する」ことです。そのために有効な選択肢が、アプリを マルチテナントにして認可エンドポイントを /common(または /organizations)にする方法です。
対処手順(実務で通る形)
- アプリをマルチテナントに変更 Entra ID のアプリ登録で「サポートされているアカウントの種類」を、単一テナントではなく 複数テナント(組織アカウント)に変更します。これにより、ホームテナント側で同意を取り直す道が開きます。
- 認可エンドポイントのテナントを /common(または /organizations)に Authorization Code フローの認可 URL のベースを tenant 固定から変更します。
https://login.microsoftonline.com/common/oauth2/v2.0/authorize https://login.microsoftonline.com/common/oauth2/v2.0/token※可能であれば、最終的には「実際に運用するホームテナントID」に固定した方が、意図しないテナントでのサインインを防げます。まずは切り分け・復旧として /common を使い、原因が確定したら tenant 固定へ戻す、という進め方が現場では安全です。 - メールボックスを持つユーザーで“改めて”同意 過去にゲストとして同意した痕跡が残っていると混乱します。ホームテナント側のユーザー(実際の送信元メールボックスを持つユーザー)で、再度 Authorization Code の同意フローをやり直します。
- トークンを再解析し、tid とユーザーがホームテナントになっていることを確認 ここで tid が正しいテナントIDになっていれば、AADSTS65001 から脱出できる可能性が高いです。
“ゲストのまま送れないのか?” という疑問への整理
理屈としては「招待先テナントにメールボックスが存在し、かつ送信を許可する構成」なら成立し得ますが、一般的な B2B の運用では、ゲストは そのテナントに Exchange Online メールボックスを持たないことが多く、期待した “/me/sendMail” の動作にはなりません。委任権限でのメール送信は「そのユーザーがそのテナントでメールボックスを持つ」という前提条件が非常に強い、という理解が安全です。
パターンB:アプリ登録はテナントA/メールボックスはテナントB(同意がテナントAにしか無い)
次に多いのが、アプリ登録・Admin consent をテナントAで完了させたのに、実運用で送信したいメールボックスは テナントBにあるケースです。この場合、A で同意しても B で同意が存在しないため、B 側のユーザーとしてトークンを更新しようとした瞬間に AADSTS65001 が出ます。
このパターンが起きる背景
- グループ会社や委託先など、複数の Microsoft 365 テナントが関与している
- アプリ登録を “管理しやすいテナント” で作ったが、送信元は別テナントのメールボックス
- 「Admin consent を押した」事実はあるが、それが どのテナントに対してかの認識がズレている
対処の要点
委任権限であれアプリ権限であれ、実際にリソース(メールボックス)が存在するテナント側でも同意が必要になる場面が出ます。ここでの現実的な対処は、テナントB側で Admin consent を付与することです。
対処手順(テナントBで行う)
- テナントBの管理者アカウントで Entra 管理センターにログイン 「アプリ登録」か「エンタープライズ アプリケーション(Enterprise applications)」のどちらで管理するかは運用次第ですが、他テナントのアプリを使う場合、B 側には実体として サービス プリンシパル(Enterprise application)が生成され、そこに同意が紐づきます。
- Graph の必要な委任権限を B 側で同意 管理者同意(Grant admin consent)を、メールボックスが存在するテナントBに対して実施します。これで「B で同意が無い」状態が解消され、refresh token からの再取得や、B のユーザーとしての利用が通るようになります。
- トークン発行先(tenant)と、運用ユーザーを B 側に揃える 「認可 URL が A 固定のまま」だと、B で同意しても A のコンテキストに戻ってしまいます。運用上のテナント設計(Aで運用するのかBで運用するのか)を決め、認可エンドポイントを揃えます。
整理:どこで Admin consent すべきか
| あなたがやりたいこと | メールボックスが存在するテナント | 同意(Admin consent)を付与すべき場所 | 運用上のポイント |
|---|---|---|---|
| /me/sendMail で特定ユーザーとして送る(委任) | そのユーザーのホームテナント | 基本的にホームテナント側 | ゲスト経由で同意するとズレやすい |
| 複数テナントのユーザーで送る(委任) | ユーザーごとに異なる | 各テナントで同意が必要になる可能性 | マルチテナント+テナント別運用設計が必要 |
| /users/{id}/sendMail をシステムから送る(アプリ権限) | 送信対象ユーザーが存在するテナント | そのテナント側で Admin consent | 権限が強いのでアクセス制限を併用したい |
補足:refresh token を使うなら offline_access は基本的に押さえる
今回の本筋はテナント/ユーザー種別の不一致ですが、委任権限で Authorization Code+refresh token を運用する場合、offline_access が必要になることがあります(スコープ要求に含めて初めて refresh token が返る/安定する構成があるためです)。
ただし、すでに refresh token を取得できているなら、offline_access 追加が “決定打” にならないことも多いです。実務的には次の順で確認すると迷いにくいです。
- refresh token が取得できているか(取得できていないならまず offline_access や同意の取り直し)
- 取得できているのに AADSTS65001 が出るなら、テナント/ユーザーのズレを疑う
- ズレが無いのに更新に失敗するなら、MFA/条件付きアクセス/トークン失効(パスワード変更等)を疑う
SAP Cloud Integration 側でハマりやすい設定ポイント
CPI の OAuth 設定は、値が正しくても “運用テナント” がズレると今回のようなエラーになります。逆に言えば、テナントが揃えば設定は一気に安定します。見直し用の観点を表にまとめます。
| 項目 | 確認すること | よくあるミス | 対処の方向性 |
|---|---|---|---|
| Authorization / Token エンドポイント | tenant 固定か /common か | アプリ登録テナントに固定したまま、別テナントのメールボックスを使おうとする | 運用テナントに合わせる(必要ならマルチテナント化) |
| スコープ | Mail.Send、必要なら offline_access | Mail.Send を付けたつもりが別の権限だった、または同意が別テナント | トークンの scp を解析して確認する |
| 送信エンドポイント | /me/sendMail か /users/{id}/sendMail か | 委任なのに /users を叩く、またはアプリ権限なのに /me を叩く | 権限モデルに合わせて API を統一 |
| 送信元(From) | 実在するメールボックスか | ゲストや共有メールボックスの扱いを誤る | まずは実ユーザーのメールボックスで検証 |
特に「トークンの scp に Mail.Send があるか」だけで安心せず、tid(テナント)とユーザー(#EXT# など)を必ず見るのが、最短での復旧につながります。
運用の現実解:システム連携なら “アプリ権限(Client Credentials)” も検討する
委任権限(Authorization Code+refresh token)は、「ユーザーが関与する」前提があるぶん、テナント境界・ゲスト・MFA・条件付きアクセス・人事異動などの影響を受けやすい側面があります。CPI のような バックエンド連携が目的なら、最終的に次の選択を検討すると運用品質が上がります。
| 観点 | 委任(Delegated) | アプリ(Application) |
|---|---|---|
| ログイン | 必要(ユーザーサインイン) | 不要(アプリのみ) |
| トークン維持 | refresh token の失効に左右される | クライアントシークレット/証明書の管理が中心 |
| 送信主体 | ログインユーザー | アプリ(ただし送信対象ユーザーを指定可能) |
| 権限の強さ | 比較的ユーザー権限に準ずる | 強力になりやすい(設計と制限が重要) |
アプリ権限を採る場合は、権限が強くなるため、送信可能なメールボックスを制限する設計(例:特定の共有メールボックスのみ送れるようにするなど)をセットで考えるのが実務の定石です。これにより「何でも送れてしまう」状態を避け、監査やセキュリティレビューも通りやすくなります。
チェックリスト:AADSTS65001 で詰まった時に確認する順番
最後に、現場での切り分け順をチェックリスト化しておきます。迷ったらこの順で潰していくと、回り道が減ります。
| 順番 | 確認項目 | 確認方法 | NG の典型 | 打ち手 |
|---|---|---|---|---|
| 最優先 | トークンの tid / iss が想定テナントか | JWT を解析 | 同意したつもりが別テナントの tid | エンドポイントを揃える(/common→tenant固定、または逆) |
| 最優先 | ユーザーが Guest になっていないか | UPN に #EXT#、ユーザー種別の確認 | ゲストで同意・運用していた | ホームテナントのユーザーで同意し直す |
| 次点 | 同意(Admin consent)がメールボックスのテナントにあるか | 該当テナントの Entra で Enterprise application を確認 | テナントAでのみ同意 | テナントBでも Admin consent |
| 補助 | scp に Mail.Send が入っているか | JWT の scp | 権限不足 | 権限追加→再同意 |
| 補助 | refresh token 取得に offline_access が必要な構成か | スコープ要求と発行結果 | 更新トークンが出ない/更新で落ちる | offline_access を含めて再同意 |
よくある質問
Admin consent を押したのに、なぜ「同意していない」と言われるのですか?
多くは「押したテナントが違う」か「ゲストとして同意していた」ためです。同意はテナント単位で記録されます。トークンの tid を見て、いま誰がどのテナントで同意している状態なのかを確定するのが先決です。
認可エンドポイントを /common にすると安全面で問題はありますか?
/common は “どのテナントでもサインインできる入口” になるため、運用が落ち着いたら 特定テナントIDに固定するのが無難です。復旧・切り分け段階では /common が強力ですが、安定運用では「意図したテナントでのみ認可する」方向に寄せると、事故を減らせます。
委任権限(Authorization Code)での運用は避けた方が良いですか?
用途次第です。人が関与する業務で「その人として送る」必要があるなら委任が自然です。一方、CPI のようなシステム連携で “定型送信” が目的なら、アプリ権限(Client Credentials)を検討すると運用負荷が下がります。どちらでも、今回の AADSTS65001 のような症状は テナント境界の整理ができると解消しやすくなります。
OAuth 2.0 と Microsoft Graph は「設定が合っていれば動く」反面、「テナントのズレ」だけで簡単に破綻します。トークン解析で tid とユーザー種別を確定し、同意したテナント=送信元メールボックスのテナントという当たり前を揃えるところから、最短で解決へ進めてください。

コメント