次の方法で共有


Serilog を使用して Microsoft Entra で保護された Web API の認証または承認エラーのトラブルシューティングを行う

Web API アプリケーションが Microsoft Entra ID で保護されている Web API を呼び出すと、JwtBearer イベント検証エラーが原因で認証または承認エラーが発生する可能性があります。 この問題をトラブルシューティングするために、この記事では、JwtBearer イベントのログを設定および収集するための Net6WebAPILogging という名前のサンプル Web API アプリケーションを紹介します。

Net6WebAPILogging サンプル アプリケーション

このサンプル Web API アプリケーションでは、Microsoft Entra ID に Web API が既に登録されていることを前提としています。 Microsoft .NET 6 Framework と Microsoft Identity Web NuGet パッケージを使用します。

次のメソッドを使用して、JwtBearer イベントのログを設定および収集します。

  • JwtBearerEvents クラスを使用してミドルウェア イベントを構成します。 JWT ベアラー トークンは、 OnTokenValidatedOnMessageReceivedOnAuthenticationFailed、および OnChalleenge イベントの検証に失敗する場合があります。
  • JwtBearer イベントのログ記録を設定します。
  • Serilog フレームワークを使用して、Debug出力をコンソール ウィンドウに記録し、パスがappsettings.json ファイルに指定されているローカル ファイルをログに記録します。

サンプル アプリケーションの実行

サンプル アプリケーションを実行するには、次の手順を実行する必要があります。

手順 1: 保護された Web API のアプリケーション ID URI を構成する

Web API のアプリケーション ID URI を追加するには、次の手順に従います。

  1. Azure portal で、Web API のアプリ登録に移動します。

  2. [管理]の下の[API の公開]を選択します。

  3. ページの上部で、アプリケーション ID URI の横にある [追加] を選択します。 既定値は api://<application-client-id>です。

    アプリ登録でアプリケーション ID URI を設定する方法を示すスクリーンショット。

  4. 保存 を選択します。

手順 2: サンプル アプリケーション構成を変更する

appsettings.json ファイルの AzureAd セクションで、独自のアプリ登録情報を使用して、次の情報を変更します。

"AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<tenant name>.onmicrosoft.com", // for example contoso.onmicrosoft.com
    "TenantId": "<tenant ID>",
    "ClientId": "<application-client-id>"
  },

手順 3: サンプル アプリケーション コードを変更する

Program.cs ファイル内の TokenValidationParameters クラスのValidAudiencesプロパティとValidIssuersプロパティを変更します。

手順 4: Serilog を構成する

次のように、appsettings.jsonファイルの [Serilog] セクションで Serilog を構成します。

  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Debug",
        "Microsoft.Hosting.Lifetime": "Information"
      }
    },

JwtBearer イベントのログ記録を設定する

上記のイベントのログ記録を設定する方法を示すサンプル Program.cs ファイルを次に示します。

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
using Microsoft.IdentityModel.Logging;
using System.Diagnostics;
using Serilog;


// https://github.com/datalust/dotnet6-serilog-example

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateBootstrapLogger();

Log.Information("starting up");
try
{
    var builder = WebApplication.CreateBuilder(args);

    builder.Host.UseSerilog((ctx, lc) => lc
        .WriteTo.Console()
        .ReadFrom.Configuration(ctx.Configuration));

    // Add services to the container.
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

    // Enable PII for logging
    IdentityModelEventSource.ShowPII = true;
    // Configure middleware events
    builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            ValidAudiences = new List<string> { "api://<application-client-id>", "<application-client-id>" },
            ValidIssuers = new List<string> { "https://sts.windows.net/<tenant ID>/", "https://login.microsoftonline.com/<tenant ID>/v2.0" }
        };
        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = ctx =>
            {
                string message = "[OnTokenValidated]: ";
                message += $"token: {ctx.SecurityToken.ToString()}";
                Log.Information(message); 
                return Task.CompletedTask;
            },
            OnMessageReceived = ctx =>
            {
                string message = "[OnMessageReceived]: ";
                ctx.Request.Headers.TryGetValue("Authorization", out var BearerToken);
                if (BearerToken.Count == 0)
                    BearerToken = "no Bearer token sent\n";
                message += "Authorization Header sent: " + BearerToken + "\n";
                Log.Information(message);
                return Task.CompletedTask;
            },
            OnAuthenticationFailed = ctx =>
            {
                ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
                string message = $"[OnAuthenticationFailed]: {ctx.Exception.ToString()}";
                Log.Error(message);
                // Debug.WriteLine("[OnAuthenticationFailed]: Authentication failed with the following error: ");
                // Debug.WriteLine(ctx.Exception);
                return Task.CompletedTask;
            },
            OnChallenge = ctx =>
            {
                // Debug.WriteLine("[OnChallenge]: I can do stuff here! ");
                Log.Information("[OnChallenge]");
                return Task.CompletedTask;
            },
            OnForbidden = ctx =>
            {
                Log.Information("[OnForbidden]");
                return Task.CompletedTask;
            }
        };
    });

    builder.Services.AddControllers();
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    // builder.Services.AddEndpointsApiExplorer();
    // builder.Services.AddSwaggerGen();

    var app = builder.Build();

    app.UseSerilogRequestLogging();
    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        // app.UseSwagger();
        // app.UseSwaggerUI();
        // do something
    }

    app.UseHttpsRedirection();

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

    app.MapControllers();

    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Unhandled exception");
}
finally
{
    Log.Information("Shut down complete");
    Log.CloseAndFlush();
}

リファレンス

チュートリアル: Microsoft ID プラットフォームを使用して ASP.NET Core Web API を構築してセキュリティで保護する

サードパーティの情報に関する免責事項

この記事で説明するサード パーティ製品は、Microsoft に依存しない企業によって製造されています。 明示的か黙示的かにかかわらず、これらの製品のパフォーマンスや信頼性についてマイクロソフトはいかなる責任も負わないものとします。

お問い合わせはこちらから

質問がある場合やヘルプが必要な場合は、 サポートリクエストを作成するか、 Azure コミュニティ サポートに問い合わせてください。 Azure フィードバック コミュニティに製品フィードバックを送信することもできます。