Microsoft Graph Toolkitのmgt‑msal2‑providerで「unauthorized_client: The client does not exist or is not enabled for consumers」が出る原因と対処(完全ガイド)

Microsoft Graph Toolkit(mgt)で個人 Microsoft アカウントをサインインさせようとすると「unauthorized_client: The client does not exist or is not enabled for consumers」が出る――本番だけ再現したり、テナント設定やリダイレクト URI を変えた直後に発火する厄介なエラーです。本記事では再発防止まで踏み込んだ決定版の原因整理と対処手順、実装サンプル、検証チェックリストをまとめます。

目次

症状と前提(再現パターンの全体像)

以下のいずれか、あるいは複数が同時に発生します。

  • SPA(Single Page Application)で <mgt-msal2-provider> を用いたサインインを押すと、ダイアログに
    unauthorized_client: The client does not exist or is not enabled for consumers が表示され、@outlook.com/@hotmail.com 等の個人アカウントがサインイン不可。
  • Azure Portal でアプリ登録(App registration)やエンタープライズ アプリケーション(サービス プリンシパル)の割り当てを済ませても改善しない。
  • ローカル環境では成功するが、ステージング/本番だけ失敗する。あるいは設定変更(マニフェスト更新・シークレット更新・リダイレクト URI 追加)直後から失敗し始める。

このエラーが意味するもの(原因の読み解き)

メッセージは直訳すると「そのクライアント(アプリ)は存在しない、または consumers(個人 Microsoft アカウント)向けに有効化されていない」です。多くのケースで根本原因は以下に分類されます。

  1. アプリの対象アカウント種別が不一致:signInAudience が AzureADMyOrg や AzureADMultipleOrgs のままで、個人(consumers)を許容していない。
  2. 認可エンドポイント(authority)が不適切:テナント固有の https://login.microsoftonline.com/<tenantId> を指定しており、common/consumers を使っていない。
  3. リダイレクト URI の種類や値が不整合:SPA なのに「Web」として登録、もしくは www 有無/末尾スラッシュ/パスの違い/HTTP⇔HTTPS の差異。
  4. v1 と v2 の混在:v2 トークン(スコープベース)で動作すべきところに v1(リソースベース)を混ぜている、/.default の誤用など。
  5. ライブラリの取り違え:mgt-msal-provider(MSAL v1)と mgt-msal2-provider(MSAL v2)が混在。
  6. 環境差・キャッシュ・CORS:本番だけドメインやポートが違い、登録されていない/ブラウザのキャッシュや ITP の影響。

最短で直すための「先に見るべき」チェックリスト

まずは次の 6 点を上から順に確認してください。多くはこれで即解決します。

  1. アプリ マニフェストの accessTokenAcceptedVersion=2 と signInAudience="AzureADandPersonalMicrosoftAccount" を設定。
  2. リダイレクト URI を 「Single‑page application」 として 環境ごとに正確に登録。
  3. 認可先 authority を https://login.microsoftonline.com/common(または /consumers)に変更。
  4. v2 前提の構成(scopes 使用・v2.0 エンドポイント)に統一。v1 パラメータ(resource 等)を排除。
  5. Graph Toolkit のバージョンを固定し、<mgt-msal2-provider> を使用しているか再確認。
  6. 本番ドメインの CORS/リダイレクト URI/末尾スラッシュ とブラウザキャッシュを同時に確認。

対処の全体サマリー(一覧表)

対応内容詳細備考
1. マニフェスト設定の見直しaccessTokenAcceptedVersion を 2、signInAudience を “AzureADandPersonalMicrosoftAccount” に。個人アカウントを許可しない既定(AzureADMyOrg)のままだと本エラー。
2. リダイレクト URI の正確な登録SPA の場合は「Single‑page application」として登録。例:http://localhost:3000/、https://example.com/ 等。ローカル・ステージング・本番で全て登録必須。末尾スラッシュや www 有無に注意。
3. シークレット/証明書の更新SPA 単体では通常クライアント シークレットは不要。バックエンド(Web API)を伴う構成なら、更新後の値を確実に反映。失効時は 401/403 など別エラーに見えることも。構成を整理。
4. テナント共通エンドポイントの利用authority を .../common または .../consumers に設定。特定 tenantId を指定すると個人アカウントが弾かれる。
5. “ユーザーの割り当てが必要” の見直しエンタープライズ アプリケーションで User assignment required を No に。必要時のみ個別割り当て。主に組織アカウント向けのガード。混同に注意。
6. v2.0 エンドポイントに統一/.default や v1 エンドポイントの混在を排除。スコープは User.Read 等の v2 形式。v1 トークンは個人アカウントと非互換。

手順詳細:マニフェストの正解例と落とし穴

アプリ登録 > マニフェストで次の 2 つを必ず確認します。

{
  "accessTokenAcceptedVersion": 2,
  "signInAudience": "AzureADandPersonalMicrosoftAccount"
}
  • accessTokenAcceptedVersion:2 にすることで、v2(スコープベース)のトークンを受け入れます。
  • signInAudience:AzureADandPersonalMicrosoftAccount を選ぶと、「任意の組織ディレクトリ + 個人 Microsoft アカウント」が対象になります。

ここが AzureADMyOrg(自テナント限定)や AzureADMultipleOrgs(個人を含まない)だと、consumers が拒否され上記エラーになります。

手順詳細:リダイレクト URI は「Single‑page application」で登録

MSAL v2(PKCE)で動作する SPA は、プラットフォームの種類として「Single‑page application」を選ぶ必要があります。「Web」で登録すると コード+シークレット前提になり、リダイレクトの検証が合わず認可が失敗しがちです。

環境推奨リダイレクト URI の例注意点
ローカルhttp://localhost:3000/ もしくは http://localhost:5173/ 等、実際の開発ポートWordPress 等で /index.html を付けるなら、登録値と完全一致させる
ステージングhttps://stg.example.com/www の有無・サブドメインを間違えやすい
本番https://example.com/ または https://www.example.com/末尾スラッシュの有無差で失敗する例が多い

MSAL(および mgt)は 完全一致で照合します。トレーリングスラッシュ・大文字小文字・パスは別 URL とみなされます。

手順詳細:authority は common/consumers を使う

個人アカウントを受け入れるなら、テナントを固定しない authority を使います。

https://login.microsoftonline.com/common

または個人のみを対象にする場合は次を使います。

https://login.microsoftonline.com/consumers

.../<tenantId> や .../organizations を使うと、consumers が拒否され unauthorized_client が発生します。

v2 前提に統一(パラメータ・エンドポイント・スコープ)

  • v2 では scope(例:User.Read)を要求します。v1 の resource パラメータを混ぜない。
  • 認可/トークン エンドポイントは /oauth2/v2.0/authorize と /oauth2/v2.0/token。
  • /.default は機密クライアント(サーバー側)の用途が中心。SPA では明示スコープ(User.Read 等)を指定するのが安全。

Graph Toolkit(mgt)の正しい使い方

Web Components(マークアップ)での最小実装

<!-- mgt ローダー(例) -->
<script type="module" src="https://unpkg.com/@microsoft/mgt@3/dist/bundle/mgt-loader.js"></script>








 
  • login-type は Popup 推奨(Redirect でも可)。Redirect を使う場合は その URL を SPA のリダイレクト URI に登録します。
  • scopes は v2 形式で列挙します(カンマ区切りでもスペース区切りでも可)。

プログラムから初期化する場合(TypeScript/React 等)

import { Providers } from '@microsoft/mgt-element';
import { Msal2Provider } from '@microsoft/mgt-msal2-provider';

Providers.globalProvider = new Msal2Provider({
clientId: '',
authority: '[https://login.microsoftonline.com/common](https://login.microsoftonline.com/common)',
loginType: 'Popup',
scopes: ['User.Read', 'Mail.Read'],
// redirectUri: '[https://example.com/auth/callback](https://example.com/auth/callback)', // Redirect を使うときは登録必須
}); 

バージョン整合性(mgt と MSAL)

  • mgt v2.7 以前から移行したプロジェクトでは、古い mgt-msal-provider を読み込んでいる可能性があります。必ず mgt-msal2-provider に統一し、package.json でバージョンを固定してください。
  • 依存の二重取り込み(CDN と npm の併用)で Umd/ESM の衝突が起きると、設定が反映されないことがあります。

ローカルは通るのに本番で落ちる ― よくある差分

症状原因の典型対処
本番のみ unauthorized_client本番ドメインのリダイレクト URI 未登録/www 有無・スラッシュ違いSPA(Single‑page application)として本番 URL を完全一致で登録
ステージングのみ失敗authority を /<tenantId> 固定にしている/common(または /consumers)へ変更
設定変更直後だけ失敗ブラウザの localStorage/sessionStorage に古いキャッシュキャッシュ削除・シークレットタブ・sessionStorage を使う構成で暫定回避
Safari/一部ブラウザのみ失敗ITP/サードパーティ Cookie 制限の影響Popup ログインへ切替、またはトラッキング防止例外の案内
突然エラー化リダイレクト URI を「Web」で追加してしまい構成が不整合誤登録を削除し、「Single‑page application」で再登録

デバッグの観点(Network/Console/ログ)

Network タブで見るべきパラメータ

/oauth2/v2.0/authorize リクエストのクエリを確認します。

  • authority のホスト・パス:login.microsoftonline.com/common になっているか。
  • scope:User.Read など v2 形式か。resource は混ざっていないか。
  • redirect_uri:登録済みの URI と完全一致か(末尾 / を含むか)。
  • client_id:誤った App を参照していないか。

MSAL ログの出力(詳細診断)

import { LogLevel } from '@azure/msal-browser';

const msalConfig = {
auth: {
clientId: '',
authority: '[https://login.microsoftonline.com/common](https://login.microsoftonline.com/common)',
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) return;
switch (level) {
case LogLevel.Error: console.error(message); break;
case LogLevel.Warning: console.warn(message); break;
case LogLevel.Info: console.info(message); break;
default: console.debug(message); break;
}
}
}
},
cache: {
cacheLocation: 'sessionStorage'
}
}; 

ここで containsPii(個人情報を含むログ)は捨て、Error/Warning を重点的に確認します。

「ユーザーの割り当てが必要」設定の扱い

エンタープライズ アプリケーション側で User assignment required を No にすると、組織アカウントに対してアクセスが柔軟になります。個人アカウント(consumers)には本来適用外ですが、同一アプリで組織ユーザーも扱う場合の副作用として、割り当て必須だとログイン後のトークン取得や同意フローで詰まることがあります。

シークレット/証明書の注意点(SPA と Web API の境界)

  • 純粋な SPA(フロントのみ)ではクライアント シークレットは使用しません。コードを公開できないためです。
  • 独自のバックエンド API を持ち OBO(On-Behalf-Of)等を行う場合は、サーバー側にシークレットまたは証明書が必要です。更新したら API 側に必ず反映します。
  • 「シークレット更新後に SPA が突然失敗」は、実は App を作り直した/Client ID を入れ替えた/リダイレクト URI を消したなどの構成変更が原因であることが多いです。

CI/CD での再発防止(IaC と自動検証)

環境差が原因のトラブルは、Infrastructure as Code で 0/1 の差異として管理すると劇的に減ります。例として Bicep/Terraform の断片を示します。

Bicep(抜粋)

resource app 'Microsoft.Graph/[email protected]' = {
  name: 'my-mgt-app'
  properties: {
    signInAudience: 'AzureADandPersonalMicrosoftAccount'
    web: { implicitGrantSettings: { enableIdTokenIssuance: true } }
    spa: {
      redirectUris: [
        'http://localhost:3000/'
        'https://stg.example.com/'
        'https://example.com/'
      ]
    }
    api: { requestedAccessTokenVersion: 2 }
  }
}

Terraform(抜粋)

resource "azuread_application" "mgt" {
  display_name = "my-mgt-app"
  sign_in_audience = "AzureADandPersonalMicrosoftAccount"

web {
implicit_grant {
id_token_issuance_enabled = true
}
}

spa {
redirect_uris = [
"http://localhost:3000/",
"[https://stg.example.com/](https://stg.example.com/)",
"[https://example.com/](https://example.com/)"
]
}
api {
requested_access_token_version = 2
}
} 

CI で manifest.json をエクスポートし、次の項目を自動テストするのも有効です。

  • signInAudience === AzureADandPersonalMicrosoftAccount
  • accessTokenAcceptedVersion === 2(または API セクションの v2)
  • 各環境のリダイレクト URI がすべて含まれている

実装誤りを発見するクイックテスト

  1. 本番 URL をそのままブラウザで開き、開発者ツールの Network を開いた状態で「サインイン」をクリック。
  2. /authorize リクエストの authority が /common かを確認。
  3. redirect_uri が登録済みの値(末尾スラッシュ含む)と一致しているか比較。
  4. scope に User.Read 等の v2 スコープが入っているか。
  5. ここで相違があればその場で修正し、ブラウザの localStorage/sessionStorage をクリアして再試験。

エラーメッセージ別の対策マップ

メッセージ原因の目星最初にすること
unauthorized_client ... not enabled for consumers個人アカウントが許可されていない/authority が tenant 固定signInAudience を変更、/common に切替
invalid_clientClient ID の誤り、アプリ削除・再作成の影響Client ID を見直し、キャッシュ削除
invalid_request: The reply url is invalidリダイレクト URI 不一致(末尾 /、www など)SPA として正確に再登録
interaction_required同意不足・Conditional Access・割り当て要件権限の管理者同意、割り当て設定を見直し

Next.js / Vite / 静的ホスティングでの注意

  • ベースパス(/app/ など)を使うときは、リダイレクト URI も同じパスで登録。
  • SPA で ルーティングにハッシュ(#)を使う場合、MSAL の redirectUri と実際の URL 表現が一致しているかを確認。
  • CDN キャッシュで古い JS を配信していると、authority や scopes の修正が反映されません。キャッシュパージを忘れずに。

セキュリティ観点のミニチェック

  • SPA にクライアント シークレットを埋め込まない(漏えい前提)。
  • 必要最小限のスコープだけを要求(例:まずは User.Read)。
  • https を強制(ローカル以外)。

よくある質問(FAQ)

Q. authority は /common と /consumers のどちらが良い?

組織アカウントも個人も受け入れるなら /common。個人のみに限定したい要件があるなら /consumers を選びます。

Q. /.default を SPA で使っても良い?

推奨は明示スコープ(例:User.Read)。/.default は機密クライアントの同意モデルで使われることが多く、誤用すると権限不足や同意の不整合を招きます。

Q. エンタープライズ アプリケーションの「ユーザー割り当てが必要」は個人アカウントに影響する?

原則は組織アカウント向けの制御です。ただし混在シナリオでは、同意やトークン発行の前段で影響を受けることがあるため、不要であれば No を推奨します。

完全チェックリスト(貼って使える)

  • マニフェスト:accessTokenAcceptedVersion=2 / signInAudience="AzureADandPersonalMicrosoftAccount"
  • authority:https://login.microsoftonline.com/common(または /consumers)
  • リダイレクト URI(SPA):ローカル/ステージング/本番を 完全一致で登録
  • スコープ:User.Read など v2 形式に統一(resource を使わない)
  • mgt:<mgt-msal2-provider> を使用、依存のバージョン固定
  • ブラウザ:localStorage/sessionStorage をクリアして再試験
  • CDN:キャッシュパージ済み
  • (必要に応じて)エンタープライズ アプリの割り当て設定を確認

まとめ(再発させない運用の勘所)

unauthorized_client: The client does not exist or is not enabled for consumers は、要するに「アプリが個人アカウントを受け入れる構成になっていない」か「その前提と矛盾する URL・エンドポイントを叩いている」ときに起きます。最重要は マニフェスト(signInAudience) と リダイレクト URI(SPA として完全一致)、そして authority を /common にすること。ここを押さえたうえで、v2 への統一・依存バージョン固定・IaC による自動検証を行えば、ローカルと本番の差分やメンテ作業後の謎の失敗はほぼ撲滅できます。


実践テンプレート:最小構成の HTML(そのまま検証可)

以下は最小限の構成で、個人アカウント+組織アカウントの両方に対応します。client-id と authority を調整し、事前に同じ URL を SPA のリダイレクト URI へ登録してください。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>mgt-msal2-provider quickstart</title>
    <script type="module" src="https://unpkg.com/@microsoft/mgt@3/dist/bundle/mgt-loader.js"></script>
    <style>
      body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; margin: 2rem; }
      .row { display: flex; gap: 1rem; align-items: center; }
    </style>
  </head>
  <body>


<mgt-msal2-provider
  client-id="<YOUR-CLIENT-ID>"
  authority="https://login.microsoftonline.com/common"
  login-type="Popup"
  scopes="User.Read"
></mgt-msal2-provider>

<div class="row">
  <mgt-login></mgt-login>
  <mgt-person person-query="me" view="twoLines"></mgt-person>
</div>

<!-- 認証後に API を叩く例 -->
<script type="module">
  import { Providers } from "https://unpkg.com/@microsoft/mgt@3/dist/es6/index.js";
  async function whoAmI() {
    const provider = Providers.globalProvider;
    if (provider && provider.state === 2) { // SignedIn
      const graph = provider.graph.client;
      const me = await graph.api('/me').get();
      console.log(me);
    }
  }
  document.addEventListener('mgt:stateChange', whoAmI);
</script>



 

現場メモ:落ちやすい 12 のポイント

  1. マニフェストの 保存忘れ(Portal で変更後は Save を必ず押す)。
  2. Client ID の取り違え(本番と開発の App を別に作っている)。
  3. Redirect URI の 末尾スラッシュ違い。
  4. www 有無の違い(https://example.com/ と https://www.example.com/ は別物)。
  5. authority を /organizations にしてしまう。
  6. スコープと /.default の混在。
  7. v1 の resource パラメータがどこかに残っている。
  8. mgt の旧プロバイダー(mgt-msal-provider)を読み込んでいる。
  9. CDN のキャッシュが古い。
  10. Popup をブロックするブラウザ設定。
  11. Safari の ITP でサインイン後に状態が飛ぶ。
  12. リダイレクト URI を「Web」で登録し直してしまった。

最終チェック:これで “unauthorized_client” は消える

最後に、今回の要点を 3 行で。

  • マニフェスト: signInAudience="AzureADandPersonalMicrosoftAccount" と accessTokenAcceptedVersion=2
  • エンドポイント: authority=/common(または /consumers)+ v2(/oauth2/v2.0)
  • リダイレクト URI: 「Single‑page application」で 環境ごとに完全一致で登録

この 3 点を順に見直すだけで、ほとんどのケースは即座に復旧します。加えて、依存のバージョン固定と IaC による自動検証を取り入れれば、ステージング/本番の差分起因の再発を長期的に防止できます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次