Microsoft Entra External IDとGoogleフェデレーションで発生する「parameter not allowed for this message type: username」エラーとAADSTS50133の対処ガイド

Microsoft Entra External ID(旧称 CIAM)で Google アカウントとフェデレーションしたところ、数日後の再訪問で「parameter not allowed for this message type: username」という Google 側エラーに悩まされていませんか。本記事では、再現条件・原因となっているプロトコルレベルの挙動・実務的なワークアラウンド(MSAL.js / ASP.NET Core の具体的コード)に加え、その後に現れることがある「AADSTS50133」への対処までを、実装者目線で詳しく整理します。

目次

Microsoft Entra External ID × Google フェデレーションで発生するエラーの全体像

構成と前提

ここで扱うシナリオは、次のような典型的な構成です。

  • テナント: Microsoft Entra External ID(旧 CIAM、*.ciamlogin.com)
  • 外部 IdP: Google(Google アカウント)
  • クライアント:
    • SPA(MSAL.js / @azure/msal-browser)
    • もしくは ASP.NET Core(Razor Pages / MVC)で OpenID Connect ミドルウェアを利用

リクエストの流れをシンプルに描くと、次のようになります。

ブラウザ(ユーザー)
   │
   │ ① /authorize で Entra External ID にサインイン
   ▼
Microsoft Entra External ID(ciamlogin.com)
   │
   │ ② フェデレーション IdP として Google にリダイレクト
   ▼
Google(accounts.google.com)

初回サインインや、明示的にログイン操作を行ったときは問題なく成功するにもかかわらず、「数日後に再訪問したとき」や「トークン失効タイミング以降にタブを開き直したとき」にだけ、Google 側の画面で次のようなエラーが表示される、というのが今回の論点です。

parameter not allowed for this message type: username

このエラーが一度出たあと、ブラウザでリロード(F5)すると、そのまま通ってしまうことも多く、「たまにしか起きない・再現しづらい」ため原因究明が難しくなりがちです。

発生タイミング:サイレント更新(prompt=none 相当)

多くの実装では、次のようなフローで「サイレント更新」を行います。

  • SPA: acquireTokenSilent を呼び出して、バックグラウンドでトークン更新(iframe / hidden window)を試みる
  • サーバー側: 既存セッションを利用して再ログインを試みる(ミドルウェアが内部で prompt=none 相当のリクエストを送る)

問題のエラーは、まさにこの「サイレント更新経路」で Google にリダイレクトされた際に発生します。インタラクティブなログイン(アカウント選択 UI が出るフロー)では発生しません。

原因:Entra External ID が Google に送る username パラメータ

ネットワークトレースで見る実際のリクエスト

ブラウザの開発者ツール(F12)でネットワークログを確認すると、以下の 2 段階のリクエストが確認できます。

  1. SPA → Entra External ID(ciamlogin.com)の /oauth2/v2.0/authorize
  2. Entra External ID → Google の /o/oauth2/v2/auth

1 のリクエストには username などのパラメータは含まれていないのに、2 のリクエストで突然 username=... が付与されている、というパターンが確認されています。

https://accounts.google.com/o/oauth2/v2/auth
  ?scope=openid+email+profile+...
  &response_type=code
  &client_id=...
  &redirect_uri=...
  &state=...
  &[email protected]  ← これが問題

このあと、Google は次のようなエラー画面にリダイレクトします。

https://accounts.google.com/signin/oauth/error?authError=invalid_request...
(本文)Parameter not allowed for this message type: username

つまり、Google OAuth 2.0 の認可エンドポイントに対して、Entra External ID 側が勝手に username というクエリパラメータを付けてしまっており、Google が「そのメッセージタイプでは許容されないパラメータ」として弾いている、という構図です。

Google が許容するのは login_hint であって username ではない

Google の OAuth 2.0 / OpenID Connect のドキュメントを見ると、ユーザー候補を事前に示すパラメータとしては login_hint が定義されていますが、username というパラメータは仕様に含まれていません。

  • login_hint: メールアドレスなどを渡して「このユーザーを候補として表示してほしい」と伝えるためのヒント
  • username: Google 側の仕様には存在しない(=Google は認識しない)

Entra External ID のフェデレーション実装では、サイレント更新経路で内部的に利用している「ユーザー識別子」を、誤って username として Google に渡してしまっていると考えられます。

なぜサイレント更新のときだけ起きるのか

同じユーザーであっても、以下のような挙動の違いが観測されます。

シナリオEntra → Google リクエスト結果
初回ログイン(インタラクティブ)通常の prompt=consent / select_account 相当。
多くの場合 username なし
正常
有効なセッションがある状態での再訪問(サイレント)prompt=none 相当で Google に遷移。
ここで username が付く場合がある
「parameter not allowed for this message type: username」
エラー後の F5 リロードフォールバックでインタラクティブ フローに切り替わる通ることが多い

つまり「サイレント更新経路(prompt=none)のときだけ username 付きで Google に飛ぶ」ことがあり、そのときにだけエラーが表面化していると整理できます。

Entra External ID 側にオフにする UI 設定は存在しない

Microsoft Q&A では、同様の事象に対して「Entra External ID 側で username / login_hint を無効化できないか」という議論がなされましたが、現時点で UI レベルの設定は用意されていない、という回答がなされています。

そのため、現実解としては「クライアント側でリクエストパラメータ(特に prompt)を制御し、問題の経路自体を踏まないようにする」アプローチが必要になります。

ワークアラウンド:常に prompt=select_account を付けてサイレント更新を避ける

方針

サイレント更新のときに Entra → Google のリクエストに username が付与されるのであれば、「そもそもサイレント更新を行わない(=常にインタラクティブ)」ようにしておけば、この経路を通らないようにできます。

具体的には、アプリケーションから Entra External ID へ認可リクエストを送る際、常に prompt=select_account を付ける、というワークアラウンドです。

  • メリット: Google 側エラー「parameter not allowed for this message type: username」が発生しなくなる
  • デメリット: 毎回アカウント選択 UI が表示されるため、完全な「無人サインイン」体験は失われる

Microsoft Q&A でも、この方法で事象が解消したという報告が上がっています。

MSAL.js(SPA)の実装例

SPA の場合は、ログイン時に指定する loginRequest に prompt: "select_account" を付けます。

import { PublicClientApplication } from "@azure/msal-browser";

const msalConfig = {
  auth: {
    clientId: "YOUR_CLIENT_ID",
    authority: "https://your-tenant.ciamlogin.com/...",
    redirectUri: "https://your-app.example.com",
  },
  cache: {
    cacheLocation: "localStorage",
    storeAuthStateInCookie: false,
  },
};

const msalInstance = new PublicClientApplication(msalConfig);

const loginRequest = {
  scopes: ["openid", "profile", "offline_access"], // 必要に応じて API scope を追加
  prompt: "select_account",
};

export async function ensureToken() {
  try {
    // 通常はサイレント更新を試みる
    return await msalInstance.acquireTokenSilent({
      account: msalInstance.getAllAccounts()[0],
      scopes: loginRequest.scopes,
    });
  } catch (e) {
    // サイレント更新に失敗したら、常に select_account でリダイレクト
    await msalInstance.loginRedirect(loginRequest);
    return null;
  }
}

ポイントは次のとおりです。

  • acquireTokenSilent は(MSAL 内部実装として)prompt=none で再認証を試みますが、そこで失敗したら loginRedirect にフォールバックする
  • loginRedirect には常に prompt=select_account を指定することで、「インタラクティブ フローのみ」を踏ませる

ASP.NET Core(Razor Pages / MVC)の実装例

サーバーサイドの ASP.NET Core アプリでは、OpenID Connect ミドルウェアのイベントで Prompt を書き換えるのがシンプルです。

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;

builder.Services
    .AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(options =>
    {
        builder.Configuration.Bind("AzureAd", options);
        options.SaveTokens = true;

        options.Events.OnRedirectToIdentityProvider = context =>
        {
            // 既定のサイレント経路を避け、必ずアカウント選択 UI を出す
            context.ProtocolMessage.Prompt = "select_account";
            return Task.CompletedTask;
        };
    });

この設定を行うと、認証が必要になったときに常に prompt=select_account 付きのリクエストが Entra External ID に送られ、その後の Google への転送もインタラクティブ フローに統一されます。

UX 低下との付き合い方

毎回アカウント選択 UI が出るようになるため、「できれば自動ログインにしたい」という要望とはトレードオフになります。

運用面でよく採られる妥協策の例を挙げます。

  • 特に安定性が重要な業務アプリでは、まずは全ユーザーに対して select_account を強制し、エラーが収まるか確認する
  • その後、どうしても UX が問題になる利用者(社内管理者など)が多ければ、そのユーザーに限って別の IdP(Azure AD など)を使うよう設計する
  • ヘルプページや利用ガイドで「アカウント選択画面が表示されるのは仕様」であることを事前に案内しておき、不安感を減らす

テスト・切り分けのポイント:Cookie とトークンキャッシュを整理する

この事象は「あるブラウザでは頻発するが、別のブラウザでは再現しない」といった、再現性のばらつきが非常に大きいのが特徴です。その主な理由は、ブラウザ側に残っている Cookie やローカルキャッシュの状態が影響するからです。

テスト前にクリアしておきたいもの

対象ドメイン / ストア備考
Google の Cookieaccounts.google.comGoogle アカウントのセッション情報を削除
Entra External ID の Cookie*.ciamlogin.comExternal ID のセッション / 永続 Cookie を削除
MSAL のトークンキャッシュブラウザの localStorage / sessionStorageキーに msal. が含まれるものを削除
アプリ側セッション CookieアプリのドメインASP.NET Core などの認証 Cookie を削除

上記をすべて削除したうえで、

  1. 初回サインイン(問題ないことを確認)
  2. ブラウザを閉じる / 時間をおく
  3. 再訪問(サイレント更新のタイミング)

という手順を繰り返すことで、かなり安定して事象を再現できるようになります。

別症状:AADSTS50133「The session is not valid due to password expiration or recent password change」

どんなときに出るか

prompt=select_account ワークアラウンドを適用したあと、一部の環境では次のような別のエラーが発生することがあります。

AADSTS50133: The session is not valid due to password expiration or recent password change.

これは本来、Entra ID(旧 Azure AD)の「ユーザーがパスワードを変更した」「パスワードの有効期限が切れた」場合に出るエラーですが、Google フェデレーション ユーザー(=パスワードを Entra 側で持っていないユーザー)に対しても、まれに返ってくることが報告されています。

Microsoft Q&A の同スレッドでも、上記のワークアラウンド適用後に AADSTS50133 が断続的に出るというコメントがあり、Microsoft 側で修正対応中である旨が共有されています。

あり得る原因(推測を含む)

正確な内部実装は公開されていませんが、挙動から推測される要因を列挙すると次のようになります。

  • 外部 IdP(Google)のセッションと、Entra External ID 内部のセッション状態がずれる
  • 古いリフレッシュトークンやセッション Cookie が残っている状態で、再同意・再認証フローに入ったときにタイミング競合が起きる
  • 本来は「パスワード変更検知」のためのバリデーションが、フェデレーション ユーザーにも誤って適用されてしまっている

いずれにせよ、実際には「パスワード期限切れ」でも「本当にパスワードを変更した」わけでもないのに、セッションを無効扱いしてしまっている、という性質のエラーであると考えられます。

実務的な対処方針

アプリ側から見たときに重要なのは、「AADSTS50133 が出たらどう扱うか」です。実務的には、次のような方針が現実的です。

  1. アプリ側で AADSTS50133 を検知したら、「完全インタラクティブなサインイン(prompt=login)」にフォールバックする
  2. それでも解消しないユーザーについては、「Cookie 削除 → ブラウザ再起動」をお願いする
  3. 頻発するようであれば、テナント管理者経由で Microsoft サポートにチケットを上げ、サインインログを共有する

MSAL.js でのハンドリング例

async function acquireTokenWithFallback() {
  const silentRequest = {
    account: msalInstance.getAllAccounts()[0],
    scopes: ["openid", "profile", "offline_access"],
  };

  try {
    return await msalInstance.acquireTokenSilent(silentRequest);
  } catch (error: any) {
    const message = String(error.errorMessage || error.message || "");

    // AADSTS50133 を含む場合は完全インタラクティブに落とす
    if (message.includes("AADSTS50133")) {
      await msalInstance.loginRedirect({
        scopes: silentRequest.scopes,
        prompt: "login", // 毎回パスワード入力を要求するような最も強いインタラクティブ
      });
      return null;
    }

    // それ以外のエラーは通常のワークアラウンド(select_account)へ
    await msalInstance.loginRedirect({
      scopes: silentRequest.scopes,
      prompt: "select_account",
    });
    return null;
  }
}

ポイントは、「select_account での再サインインでも解消しないケースだけ、prompt=login で完全にセッションを張り直す」という二段構えにしておくことです。

ASP.NET Core(サーバー側)のハンドリング例

サーバー側では、OnRemoteFailure イベントで Entra から返ってきたエラーを検知し、500 エラーにせず UX を制御するのがよく使われるパターンです。

options.Events.OnRemoteFailure = context =>
{
    var error = context.Failure?.Message ?? string.Empty;

    if (error.Contains("AADSTS50133"))
    {
        // 一度サインアウトさせてから、再ログインに誘導する
        context.Response.Redirect("/Account/ForceReLogin");
        context.HandleResponse();
    }

    return Task.CompletedTask;
};

/Account/ForceReLogin 側では、ユーザーに「サインイン情報の整合性が取れなくなったため、もう一度サインインをお願いしたい」旨を説明し、サインアウト → サインインを明示的に実行させるとよいでしょう。

実装者向けチェックリスト

ここまでの内容を、実装者が確認すべき観点として整理すると次のようになります。

項目内容優先度
prompt の固定ログイン時に prompt=select_account を常時付与し、サイレント更新経路を避ける高
エラー時フォールバックAADSTS50133 等の特定エラーを検知したら prompt=login で再試行する高
Cookie / トークンキャッシュテスト手順に「Google / ciamlogin.com の Cookie 削除」「MSAL キャッシュ削除」を組み込む中
サインインログの監視Entra External ID のサインインログで、要求パラメータ・リダイレクト先・エラーコードを定期的に確認する中
利用者への事前案内アカウント選択 UI が表示されること、まれに再サインインが必要になることをヘルプ・ガイドに明記する中

よくあるつまずきとその回避策

「クライアントから username は送っていないのに?」

ブラウザ側のネットワークログを見ると、SPA が Entra External ID に送っているリクエストには username が含まれていないにもかかわらず、Entra External ID から Google へのリクエストには username が付いている、というケースが確認されています。

これは、あくまで「Entra External ID 内部で username を組み立てて Google に渡している」ことを意味しており、クライアント アプリ側のバグではありません。クライアント側でできることは、その経路を踏まないように prompt を制御することだけです。

「select_account による UX 低下が気になる」

業務アプリであれば、セキュリティや安定性を優先して「毎回アカウントを確認させる」設計に振り切るのも 1 つの選択です。一方で、コンシューマ向けサービスなどで UX の悪化が問題になる場合、次のような工夫も考えられます。

  • 初回ログインのみ select_account で確実にフェデレーションを確立し、その後一定期間はセッション維持時間を短めに設定する
  • あくまで一時的なワークアラウンドとして位置づけ、Microsoft の修正が入った段階で元に戻す前提で運用を設計する

「断続的な AADSTS50133 が止まらない」

アプリ側でフォールバックを実装してもなお、あるユーザーだけ AADSTS50133 が頻発する場合は、次のようなステップを踏むと原因の切り分けがしやすくなります。

  1. そのユーザーに対し、「ブラウザの Cookie 削除」「別ブラウザでの再試行」を依頼する
  2. 問題が再現するタイミングで、Entra External ID のサインインログをテナント管理者に取得してもらう
  3. ログを添えて Microsoft サポートにチケットを起票し、既知の不具合かどうかを確認する

Microsoft Q&A でも、Cognito など別の CIAM に置き換えることまで検討しているというコメントもあり、影響度は小さくないことがうかがえます。 とはいえ、短期的にはワークアラウンドと運用で凌ぎつつ、修正のリリースを待つ選択が現実的です。

まとめ:現時点での「安全運用テンプレ」

最後に、本記事のポイントを整理します。

  • Entra External ID と Google フェデレーションの組み合わせでは、サイレント更新経路で Entra → Google のリクエストに username が付与されることがあり、Google が「parameter not allowed for this message type: username」として拒否する
  • 現状、Entra External ID 側に username / login_hint を無効化する UI 設定はなく、クライアント側の prompt 制御で経路を避けるのが現実的な対処である
  • 具体的には、MSAL.js / ASP.NET Core で prompt=select_account を常時付与し、サイレント更新を避けることで、Google 側エラーを回避できる
  • その副作用として UX は低下するが、業務アプリなど安定性重視のシナリオでは許容できるケースが多い
  • ワークアラウンド適用後に発生し得る AADSTS50133 については、prompt=login で完全インタラクティブにフォールバックし、Cookie / セッションの整理とサインインログ監視を組み合わせて対処する

Microsoft Entra External ID と Google フェデレーションは非常に便利な組み合わせですが、現時点ではサイレント更新周りにいくつかの「クセ」があるのも事実です。本記事をベースに、実装と運用の双方から安全なサインイン体験を設計してみてください。

この記事を書いた人

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

コメント

コメントする

目次