AADSTS50146の原因と解決:ADALからMSAL(ROPC)移行で起きるエラー完全ガイド【Azure AD/Microsoft Entra・.NET 8対応】

ASP.NET Web API を .NET 8 ベースへ更新し、認証を ADAL(ROPC)から MSAL に切り替えた直後に AADSTS50146(invalid_request : This application is required to be configured with an application‑specific signing key …)でトークン取得が失敗する――この現象は、旧 v1(ADAL)時代のレガシー設定が残存していることが主因です。本稿は「壊さずに移行する」観点で、根本原因の理解から安全な対処手順、検証コード例までを一気通貫で解説します。

目次

結論と全体像(最速の意思決定)

エラー AADSTS50146 は、アプリ登録に残った「アプリ固有の署名鍵(application‑specific signing key)を要求する」レガシー設定が、v2 エンドポイント(MSAL)では無効であるにも関わらず有効化されているときに発生します。根本対処は次の二択です。

対応策内容備考・補足
A. 新しい v2 アプリ登録を作成(推奨)旧 ADAL 用アプリ登録は残し、MSAL(v2 エンドポイント)専用に 別アプリ登録を新規作成。クライアントと API のバインドを段階的に切り替える。安全性が最も高い。既存運用への副作用がない/ロールバック容易。
B. 既存アプリ登録を修正して流用ポータルの App registrations → 対象アプリ → Manifest で、
① レガシー「アプリ固有の署名鍵」を 無効化(該当項目を削除または false 化)
② accessTokenAcceptedVersion を 2 に設定
③ 保存後、数分待って再試行
v1 時代の非推奨フラグを除去する作業。慎重さが必要(影響範囲の把握とバックアップ)。

要点: v2(MSAL)は application‑specific signing key を前提にしません。残存していると AADSTS50146 でブロックされます。安全第一なら「A」(新規 v2 アプリ登録)を選び、速攻で解消したい場合のみ「B」(既存の修正)を選びます。

AADSTS50146 が起きる仕組みを 3 分で理解する

ADAL が主に利用した v1 エンドポイントでは、一部のアプリ種別で「テナント共通の署名鍵ではなく、アプリ専用の署名鍵でトークンを検証する」動線を選べるレガシー設定が存在しました。MSAL(v2 エンドポイント)は設計が異なり、このレガシー要求は無視できません。要求が残ったまま v2 にアクセスすると、「その専用鍵を設定していないので処理できない」という趣旨で AADSTS50146 が返されます。

  • ADAL(v1):resource パラメータ。レガシー SSO 設定やアプリ専用署名鍵の影響を受ける構成が存在。
  • MSAL(v2):scopes(推奨 .default)。共通の署名鍵と OpenID Connect/OAuth 2.0 の標準に忠実。
    → レガシー設定が残ると 整合性が崩れて AADSTS50146。
観点ADAL(v1)MSAL(v2)
トークンエンドポイント/oauth2/token/oauth2/v2.0/token
リクエスト パラメータresourcescope(例:api://{API-ID-URI}/.default)
トークン バージョンv1(デフォルト)v2(API 側の accessTokenAcceptedVersion=2 が鍵)
アプリ固有の署名鍵一部シナリオで要求可前提なし(残存要求があると AADSTS50146)

推奨:新しい v2 アプリ登録を作って切り替える(対応策 A)

運用を壊さずに MSAL 化する鉄板のやり方です。旧 ADAL アプリは温存し、並行で v2 専用のアプリ登録を作って段階的にトラフィックを切り替えます。

クライアント(ROPC 呼び出し側)の設定

  1. アプリ登録を新規作成(シングルテナント推奨:Accounts in this organizational directory only)。
  2. Authentication → Advanced settings → Allow public client flows を Yes(ROPC に必須)。
  3. API permissions:アクセス対象の API を追加。Graph なら https://graph.microsoft.com/.default を利用する想定で付与・同意。

API(受け側 Web API/.NET 8)の設定

  1. Expose an API:Application ID URI を設定(例:api://{API-APP-ID-GUID})。
  2. Manifest:accessTokenAcceptedVersion を 2 に。これはAPI 側で設定する点に注意(API が v2 トークンを受け付けることを宣言)。

ROPC の最小サンプル(MSAL/.NET 8, C#)

// 実行環境: .NET 8, Microsoft.Identity.Client (MSAL) パッケージ
using Microsoft.Identity.Client;
using System.Net;
using System.Security;

var tenantId  = "{TenantId}";
var clientId  = "{PublicClient_AppId}";        // クライアント(ROPC 呼び出し側)のアプリID
var username  = "{[[email protected]](mailto:[email protected])}";
var password  = "{PlainTextPassword}";         // 実運用では安全な保管と入力経路を用意すること
var apiIdUri  = "api://{ApiAppIdOrUri}";
var scopes    = new[] { $"{apiIdUri}/.default" }; // Graph の場合は "[https://graph.microsoft.com/.default](https://graph.microsoft.com/.default)"

var app = PublicClientApplicationBuilder
.Create(clientId)
.WithAuthority($"[https://login.microsoftonline.com/{tenantId}](https://login.microsoftonline.com/{tenantId})")
.Build();

using var secure = new NetworkCredential("", password).SecurePassword;
var result = await app.AcquireTokenByUsernamePassword(scopes, username, secure).ExecuteAsync();

Console.WriteLine($"AccessToken (truncated): {result.AccessToken[..50]}...");
Console.WriteLine($"Token for audience: {result.Account?.Username} / ExpiresOn: {result.ExpiresOn}"); 

注意: ROPC は UI なし認証のため、MFA やデバイス準拠が 必須のユーザーでは失敗します。運用上は「MFA/条件付きアクセスの対象外」にしたサービス アカウントを用意する、または ROPC 自体の利用を最小化してください。

.NET 8 Web API(受け側)の最小構成

API は v2 トークンを受け付ける設定で JWT を検証します。

// Program.cs(.NET 8 Minimal API の一例)
using Microsoft.AspNetCore.Authentication.JwtBearer;

var builder = WebApplication.CreateBuilder(args);

var tenantId = "{TenantId}";
var apiAppId = "{Api_AppId}"; // 受け側 API のアプリ登録ID

builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = $"[https://login.microsoftonline.com/{tenantId}/v2.0](https://login.microsoftonline.com/{tenantId}/v2.0)";
options.Audience  = apiAppId; // または Application ID URI を受ける場合は適宜設定
// 追加の検証パラメータは要件に応じて調整
});

builder.Services.AddAuthorization();
var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/whoami", (HttpContext ctx) =>
{
var name = ctx.User.Identity?.Name ?? "(anonymous)";
return Results.Ok(new { name });
}).RequireAuthorization();

app.Run(); 

既存アプリ登録を修正して流用する(対応策 B)

短時間で切り替えたい場合の現実解です。作業前に Manifest のバックアップを必ず取得してください。

  1. Azure ポータル → App registrations → 対象アプリ → Manifest を開く。
  2. レガシーの application‑specific signing key に相当する項目を 削除 or false に変更(テナントによって表記が異なるため、SigningKey, SigningCertificate, UseCustomSigningKey 等のラベルを目印に「要求を外す」)。
  3. accessTokenAcceptedVersion を 2 に設定(特に受け側 API のアプリ登録)。
  4. 保存し、数分のレプリケーション待ちの後に ROPC を再試行。

Manifest のイメージ(記述名は環境差があるため 参考):

{
  "signInAudience": "AzureADMyOrg",
  "accessTokenAcceptedVersion": 2,
  "publicClient": { "redirectUris": [] },
  "isFallbackPublicClient": true,
  // ↓ もしアプリ固有の署名鍵を要求する設定/痕跡があれば削除
  // "useCustomSigningKey": false,
  // "preferredTokenSigningKeyThumbprint": null,
  // "preferredTokenSigningKeyEndDateTime": null
}

補足: accessTokenAcceptedVersion は「API(リソース)側」の宣言です。クライアント側に設定しても、API が v2 トークンを受け付けない限り効果は出ません。
また、Allow public client flows は ROPC を行う「クライアント」の設定です。両者を取り違えないことが重要です。

ROPC 設定チェックリスト(最終確認)

  • Allow public client flows:クライアント側で On。
  • Authority:https://login.microsoftonline.com/{TenantId}(テナント固有)。/common は避ける。
  • Scope:{ResourceAppIdUri}/.default(Graph は https://graph.microsoft.com/.default)。ROPC では動的スコープ要求を避ける。
  • API 側 Manifest:accessTokenAcceptedVersion = 2。
  • MFA / 条件付きアクセス:対象ユーザーが 要求されないポリシー範囲にあること。
  • パスワード有効 / ロックなし:AADSTS 50126/50053 などの基礎エラーも併発しやすい。

よくあるエラーと対処

エラー意味対処
AADSTS50146アプリ固有の署名鍵を要求するレガシー設定が残存新規 v2 アプリ登録へ切替(推奨)または Manifest から要求を削除+API 側を v2 受け入れに
AADSTS50076/50079MFA が必要ROPC 対象ユーザーを MFA 対象外へ/別フロー(デバイスコード/インタラクティブ)へ移行
AADSTS53003条件付きアクセスでブロック準拠デバイス/ロケーション等の要件を満たすか、ポリシーで例外を付与
AADSTS50126資格情報が無効ユーザー名/パスワードの再確認・ロック解除・再発行
AADSTS700016/7000218アプリ ID 不正/シークレット不正クライアント ID/シークレットの一致確認(ROPC パブリック クライアントでは通常シークレット不要)

cURL での再現・切り分け(手動検証)

コードやライブラリに依存せず、エンドポイントと構成の問題を切り分けるときに有効です。

# v2 エンドポイント(/oauth2/v2.0/token)で ROPC を実行
curl -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id={PublicClient_AppId}" \
  -d "grant_type=password" \
  -d "username={[email protected]}" \
  -d "password={PlainTextPassword}" \
  -d "scope=api://{ApiAppIdUri}/.default" \
  "https://login.microsoftonline.com/{TenantId}/oauth2/v2.0/token"

HTTP 200 でアクセストークンが返れば構成はおおむね正しいです。AADSTS50146 が返る場合は、前述のレガシー要求が残っています。

移行の落とし穴(現場あるある)

  • API 側の v2 化を忘れる: クライアントを MSAL 化しても、API が accessTokenAcceptedVersion=2 でないと整合しません。
  • ユーザーに MFA がかかっている: ROPC は UI なしのため、MFA/CA で失敗します。サービス アカウントの分離を。
  • Scope と Resource の混同: ADAL 時代の resource をそのまま持ち込むと失敗します。.default を使って権限セット(アプリに付与済み権限)を要求します。
  • ポータルの反映待ちを見落とす: Manifest 保存直後はレプリケーション待ちが必要です(数分)。すぐの再試行で「直っていない」と誤判定しがち。

安全な段階的切り替えプラン(推奨運用)

  1. 新規 v2 アプリ登録を用意(クライアント/API 双方)。
  2. ステージング環境で ROPC → API の往復を cURL + MSAL サンプルで検証。
  3. ユーザーまたはジョブ単位で段階リリース(失敗時は即ロールバック)。
  4. 旧 ADAL 依存が残るワークロードを最小化し、最終的に撤去。

セキュリティの実務ポイント

  • ROPC 自体は非推奨フロー(パスワード長期保管リスク)。可能なら Device Code や Auth Code + PKCE へ移行。
  • やむを得ず ROPC を使う場合は、専用アカウント・最小権限・監査ログ監視・資格情報の期限管理を徹底。
  • 条件付きアクセスの例外は 最小スコープで。「ユーザー単位+アプリ限定+ネットワーク制限」で被害半径を抑制。

トラブルシュート高速化のコツ

  • エラー本文は必ず保存: error, error_description, correlation_id, timestamp を記録。
  • 最小再現(cURL)とコード経路(MSAL)を両方試す。ライブラリのラッパーや設定ミスを分離。
  • アプリ登録オブジェクト(Application)とエンタープライズ アプリ(Service Principal)の違いを意識。修正対象を取り違えない。

現場チェックリスト(保存版)

項目見る場所判定基準
Allow public client flowsクライアント → AuthenticationYes(ROPC 有効)
Authorityクライアント コードhttps://login.microsoftonline.com/{TenantId}(テナント固有)
Scopeクライアント コード{ResourceAppIdUri}/.default または https://graph.microsoft.com/.default
トークン受け入れバージョンAPI → ManifestaccessTokenAcceptedVersion = 2
アプリ固有の署名鍵の要求対象アプリ → Manifest該当項目が存在しない/無効
MFA/CA の影響ユーザーのポリシーROPC 対象ユーザーは除外 or 要件を満たす

まとめ

  • AADSTS50146 は application‑specific signing key を要求するレガシー設定が原因。
  • 最も安全なのは「A:新しい v2 アプリ登録」で切り替え。既存を活かすなら「B:Manifest 修正」。
  • ROPC を成立させる鍵は Allow public client flows / tenant 固有 Authority / .default スコープ / API 側の v2 受け入れ。
  • 運用では ROPC を最小化し、将来的にはインタラクティブ系フローへの移行を視野に。

付録:PowerShell で Manifest を素早く確認・更新

自動化が必要な現場向けの参考スニペットです(実行前に権限と影響範囲を必ず確認してください)。

# Microsoft Graph PowerShell を想定
# 接続
Connect-MgGraph -Scopes "Application.ReadWrite.All"

# 対象アプリを取得

$app = Get-MgApplication -Filter "appId eq '{Api_AppId}'"

# accessTokenAcceptedVersion を 2 に

Update-MgApplication -ApplicationId $app.Id -AccessTokenAcceptedVersion 2

# (必要に応じて)レガシー署名鍵関連のプロパティを無効化/削除

# 実環境のプロパティ名に応じて編集

付録:移行時の変更点リマインド

  • resource → scope:MSAL では scope=.default が基本。アプリに付与済みの権限束を要求します。
  • v2 トークン:API 側が v2 を受け付けるよう accessTokenAcceptedVersion=2 を忘れずに。
  • レガシー設定の撤去:application‑specific signing key 要求は撤去。残ると AADSTS50146。

最後に

本稿の手順に沿えば、旧 ADAL 環境を壊さずに MSAL(ROPC)へ移行し、AADSTS50146 を確実に解消できます。まずは新規 v2 アプリ登録で検証し、段階的に切り替える――それが最短・最安全のルートです。

この記事を書いた人

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

コメント

コメントする

目次