ゲートウェイの背後に保護された API をデプロイする

Microsoft.Identity.Web で保護された ASP.NET Core Web API を、Azure API Management (APIM)、Azure Front Door、Azure Application Gateway を含む、Azure API ゲートウェイおよびリバースプロキシの背後にデプロイします。

ゲートウェイの要件を理解する

ゲートウェイの背後に保護された API をデプロイする場合は、いくつかの問題に対処する必要があります。

  • 転送されたヘッダー - 元の要求コンテキスト (スキーム、ホスト、IP) を保持する
  • トークンの検証 - 対象ユーザーの要求がゲートウェイ URL と一致していることを確認する
  • CORS 構成 - クロスオリジン要求を正しく処理する
  • ヘルスエンドポイント - 認証不要の正常性チェックを提供します
  • パスベースのルーティング - ゲートウェイ レベルのパス プレフィックスをサポートする
  • SSL/TLS 終了 - ゲートウェイが SSL を終了するときに HTTPS を適切に処理する

一般的なゲートウェイ シナリオを確認する

要件に基づいてゲートウェイを選択します。 次のセクションでは、保護された API の最も一般的なAzure ゲートウェイ サービスについて説明します。

Azure API Management (APIM)

ユース ケース: ポリシー、レート制限、変換を使用したエンタープライズ API ゲートウェイ

アーキテクチャ:

Client → Microsoft Entra ID → Token
Client → APIM (apim.azure-api.net) → Backend API (app.azurewebsites.net)

重要な考慮事項:

  • APIM ポリシーは、バックエンドに転送する前に JWT トークンを検証できます
  • バックエンド API は引き続きトークンを検証します
  • 対象ユーザー要求は、APIM URL またはバックエンド URL と一致する必要があります (適宜構成する)

Azure Front Door

ユース ケース: グローバル負荷分散、CDN、DDoS 保護

アーキテクチャ:

Client → Microsoft Entra ID → Token
Client → Front Door (azurefd.net) → Backend API (regional endpoints)

重要な考慮事項:

  • Front Door は、 X-Forwarded-* ヘッダーを使用して要求を転送します
  • Front Door での SSL/TLS 終端
  • トークン対象ユーザーの検証には構成が必要

Azure Application Gateway

ユース ケース: リージョンの負荷分散、WAF、パスベースのルーティング

アーキテクチャ:

Client → Microsoft Entra ID → Token
Client → Application Gateway → Backend API (multiple instances)

重要な考慮事項:

  • Web Application Firewall (WAF) 統合
  • パスベースのルーティング規則
  • バックエンド正常性プローブには、認証されていないエンドポイントが必要です

一般的なパターンを構成する

これらの構成パターンを適用して、保護された API がゲートウェイの背後で正しく動作することを確認します。

ヘッダーの転送ミドルウェア

ゲートウェイの背後にある場合は、常にフォワード ヘッダー ミドルウェアを設定します。 次のコードは、ミドルウェアを登録し、認証前に実行するように設定します。

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

// Configure forwarded headers BEFORE authentication
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                                ForwardedHeaders.XForwardedProto |
                                ForwardedHeaders.XForwardedHost;

    // Clear known networks/proxies to accept forwarded headers from any source
    // (Azure infrastructure will be the proxy)
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();

    // Limit to specific headers if needed
    options.ForwardedForHeaderName = "X-Forwarded-For";
    options.ForwardedProtoHeaderName = "X-Forwarded-Proto";
    options.ForwardedHostHeaderName = "X-Forwarded-Host";
});

// Add authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

var app = builder.Build();

// USE forwarded headers BEFORE authentication middleware
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();

app.Run();

転送されたヘッダー ミドルウェアは、次の理由で重要です。

  • ログ記録のために元のクライアント IP アドレスを保持します
  • HttpContext.Request.Schemeが元の HTTPS スキームを反映していることを確認します
  • リダイレクト URL とトークン検証用の正しい Host ヘッダーを提供します

2. トークン対象ユーザーの構成

オプション A: ゲートウェイ URL とバックエンド URL の両方を受け入れる

appsettings.json構成に複数の有効な対象ユーザーを追加します。

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",
    "Audience": "api://your-client-id",
    "TokenValidationParameters": {
      "ValidAudiences": [
        "api://your-client-id",
        "https://your-backend.azurewebsites.net",
        "https://your-apim.azure-api.net"
      ]
    }
  }
}

または、 Program.csでプログラムによって複数の対象ユーザーを構成します。

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

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

// Customize token validation to accept multiple audiences
builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    var existingValidation = options.TokenValidationParameters.AudienceValidator;

    options.TokenValidationParameters.AudienceValidator = (audiences, token, parameters) =>
    {
        var validAudiences = new[]
        {
            "api://your-client-id",
            "https://your-backend.azurewebsites.net",
            "https://your-apim.azure-api.net",
            builder.Configuration["AzureAd:ClientId"] // Also accept ClientId
        };

        return audiences.Any(a => validAudiences.Contains(a, StringComparer.OrdinalIgnoreCase));
    };
});

オプション B: APIM ポリシーで対象ユーザーを書き換える

バックエンドに転送する前に対象ユーザー要求を検証するように APIM を構成します。

<policies>
    <inbound>
        <validate-jwt header-name="Authorization" failed-validation-httpcode="401">
            <openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
            <audiences>
                <audience>api://your-client-id</audience>
            </audiences>
        </validate-jwt>

        <!-- Optionally modify token claims for backend -->
        <set-header name="X-Gateway-Validated" exists-action="override">
            <value>true</value>
        </set-header>
    </inbound>
</policies>

3. ヘルスエンドポイントの構成

ゲートウェイでは、プローブに認証不要の正常性エンドポイントが必要です。 認証ミドルウェアの前に正常性エンドポイントをマップして、トークンの検証をバイパスします。

var app = builder.Build();

// Health endpoint BEFORE authentication middleware
app.MapGet("/health", () => Results.Ok(new { status = "healthy" }))
    .AllowAnonymous();

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

// Protected endpoints require authentication
app.MapControllers();

app.Run();

または、組み込みの ASP.NET Core Health Checks フレームワークを使用して、より充実した正常性レポートを作成することもできます。

using Microsoft.Extensions.Diagnostics.HealthChecks;

builder.Services.AddHealthChecks()
    .AddCheck("api", () => HealthCheckResult.Healthy());

var app = builder.Build();

app.MapHealthChecks("/health").AllowAnonymous();
app.MapHealthChecks("/ready").AllowAnonymous();

app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

4. ゲートウェイの背後での CORS 構成

フロントエンド アプリケーションで Azure Front Door または APIM を使用する場合は、ゲートウェイの配信元からの要求を許可するように CORS を構成します。

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowGateway", policy =>
    {
        policy.WithOrigins(
            "https://your-apim.azure-api.net",
            "https://your-frontend.azurefd.net",
            "https://your-app.azurewebsites.net"
        )
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials(); // If using cookies
    });
});

var app = builder.Build();

app.UseForwardedHeaders();
app.UseCors("AllowGateway");
app.UseAuthentication();
app.UseAuthorization();

app.Run();

重要

CORS は、転送されたヘッダー の後 と認証 の前に 構成する必要があります。


Azure API Managementとの統合

このセクションでは、保護された API をAzure API Managementの背後にデプロイするための完全な構成について説明します。

バックエンド API を構成する

Program.cs で転送されたヘッダーとMicrosoft Entra ID認証を設定します。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Forwarded headers for APIM
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.All;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddControllers();

var app = builder.Build();

// Middleware order matters
app.UseForwardedHeaders();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

Microsoft Entra構成を appsettings.json に追加します。

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-backend-api-client-id",
    "Audience": "api://your-backend-api-client-id"
  }
}

JWT 検証用の APIM 受信ポリシーを追加する

JWT トークンを検証し、レート制限を適用し、バックエンドに要求を転送する受信ポリシーを定義します。

<policies>
    <inbound>
        <base />

        <!-- Validate JWT token -->
        <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized">
            <openid-config url="https://login.microsoftonline.com/{your-tenant-id}/v2.0/.well-known/openid-configuration" />
            <audiences>
                <audience>api://your-backend-api-client-id</audience>
            </audiences>
            <issuers>
                <issuer>https://login.microsoftonline.com/{your-tenant-id}/v2.0</issuer>
            </issuers>
            <required-claims>
                <claim name="scp" match="any">
                    <value>access_as_user</value>
                </claim>
            </required-claims>
        </validate-jwt>

        <!-- Rate limiting -->
        <rate-limit calls="100" renewal-period="60" />

        <!-- Forward original host header -->
        <set-header name="X-Forwarded-Host" exists-action="override">
            <value>@(context.Request.OriginalUrl.Host)</value>
        </set-header>

        <!-- Forward to backend -->
        <set-backend-service base-url="https://your-backend.azurewebsites.net" />
    </inbound>

    <backend>
        <base />
    </backend>

    <outbound>
        <base />
    </outbound>

    <on-error>
        <base />
    </on-error>
</policies>

APIM API の設定を構成する

APIM 構成を完了するには、次の名前付き値と API 設定を使用します。

名前付き値 (再利用性のため):

  • tenant-id: Microsoft Entra テナント ID
  • backend-api-client-id: バックエンド API のクライアント ID
  • backend-base-url: https://your-backend.azurewebsites.net

API 設定:

  • API URL サフィックス: /api (省略可能なパス プレフィックス)
  • Web サービス URL: 名前付き値を使用してポリシーを使用して設定する
  • サブスクリプションが必要: はい (別のセキュリティ層を追加)

クライアント アプリケーションを構成する

クライアント アプリは、APIM ではなくバックエンド API のトークンを要求します。 次のコードは、トークンを取得し、APIM エンドポイントを介して API を呼び出します。

// Client app requests token
var result = await app.AcquireTokenSilent(
    scopes: new[] { "api://your-backend-api-client-id/access_as_user" },
    account)
    .ExecuteAsync();

// Call APIM URL with token
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", result.AccessToken);

// Add APIM subscription key
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "your-subscription-key");

var response = await client.GetAsync("https://your-apim.azure-api.net/api/weatherforecast");

Azure Front Door と統合する

Azure Front Doorの背後にあるグローバル分散用に保護された API を構成します。

バックエンド API を構成する

Azure Front Doorの転送されたヘッダーをProgram.csで設定します。

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

// Configure for Azure Front Door
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                                ForwardedHeaders.XForwardedProto |
                                ForwardedHeaders.XForwardedHost;

    // Accept headers from any source (Azure Front Door)
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();

    // Front Door specific headers
    options.ForwardedForHeaderName = "X-Forwarded-For";
    options.ForwardedProtoHeaderName = "X-Forwarded-Proto";
});

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

var app = builder.Build();

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

app.MapControllers();

app.Run();

Front Door のオリジンを設定する

Azure ポータルで次の手順を実行して、Front Door の配信元を設定します。

  1. Front Door プロファイルの作成
  2. バックエンド API インスタンスで配信元グループを追加する
  3. /health エンドポイントに正常性プローブを構成する
  4. HTTPS のみの転送を設定する
  5. WAF ポリシーを有効にする (省略可能)

ヘルスプローブの設定:

  • パス: /health
  • プロトコル: HTTPS
  • メソッド: GET
  • 間隔: 30 秒

複数のリージョンを処理する

Front Door の背後にある複数のリージョンにデプロイする場合は、ログ記録と診断に関するリージョン認識を追加します。

// Add region awareness for logging/diagnostics
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

app.Use(async (context, next) =>
{
    // Log the actual client IP and region
    var clientIp = context.Connection.RemoteIpAddress?.ToString();
    var forwardedFor = context.Request.Headers["X-Forwarded-For"].ToString();
    var frontDoorId = context.Request.Headers["X-Azure-FDID"].ToString();

    // Add to logger scope or response headers
    context.Response.Headers.Add("X-Served-By-Region",
        builder.Configuration["Region"] ?? "unknown");

    await next();
});

Front Door を使用してトークンを検証する

クライアントが Front Door URL のスコープを持つトークンを要求する場合は、有効な対象ユーザーリストに追加します。

builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters.ValidAudiences = new[]
    {
        "api://your-backend-api-client-id",
        "https://your-frontend.azurefd.net", // Front Door URL
        builder.Configuration["AzureAd:ClientId"]
    };
});

Azure Application Gateway との統合

Azure Application Gateway の背後にある保護された API を Web Application Firewall (WAF) サポートで構成します。

バックエンド API を構成する

Application Gateway の転送用ヘッダーを Program.cs で設定します。

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

// Application Gateway uses standard forwarded headers
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
                                ForwardedHeaders.XForwardedProto;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddHealthChecks();

var app = builder.Build();

// Health endpoint for Application Gateway probes
app.MapHealthChecks("/health").AllowAnonymous();

app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

app.Run();

Application Gateway の設定を構成する

Azure ポータルで、次のバックエンド、正常性プローブ、および WAF 設定を設定します。

バックエンド設定:

  • プロトコル: HTTPS (推奨) または HTTP
  • ポート: 443 または 80
  • オーバーライド バックエンド パス: いいえ (必要な場合を除く)
  • カスタム プローブ: はい、/healthを指しています

健康プローブ:

  • プロトコル: HTTPS または HTTP
  • ホスト: 既定値のままにするか、指定します
  • パス: /health
  • 間隔: 30 秒
  • 異常しきい値: 3

WAF ポリシー:

  • OWASP 3.2 ルールセットで WAF を有効にする
  • 重要: Authorization ヘッダー内の JWT トークンがブロックされていないことを確認する
  • "Authorization" を含む RequestHeaderNames の WAF 除外を作成することが必要になる場合があります

パスベースのルーティングを設定する

パスベースのルーティング規則を使用する場合は、パス プレフィックスを処理するようにバックエンド API を構成します。

// Backend API should work regardless of path prefix
var app = builder.Build();

// Option 1: Use path base (if gateway adds prefix)
app.UsePathBase("/api/v1");

// Option 2: Configure routing explicitly
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Application Gateway ルール:

  • パス: /api/v1/*
  • バックエンド ターゲット: バックエンド プール
  • バックエンド設定: 構成済みの設定を使用する

一般的な問題のトラブルシューティング

これらのソリューションを使用して、ゲートウェイの背後に保護された API をデプロイするときの最も一般的な問題を解決します。

問題: 401 ゲートウェイの背後にデプロイした後に承認されていません

症状:

  • API はローカルで動作しますが、ゲートウェイの背後で 401 を返します
  • トークンは、jwt.ms でデコードされると有効なようです

考えられる原因:

  1. 対象ユーザー要求の不一致

    # Check token audience
    # Decode token and verify 'aud' claim matches one of:
    # - api://your-client-id
    # - https://your-backend.azurewebsites.net
    # - https://your-gateway-url
    
  2. 転送されたヘッダー ミドルウェアが見つかりません

    // Ensure this is BEFORE authentication
    app.UseForwardedHeaders();
    app.UseAuthentication();
    
  3. HTTPS リダイレクトの問題

    // If gateway terminates SSL, may need to disable or configure carefully
    if (!app.Environment.IsDevelopment())
    {
        app.UseHttpsRedirection();
    }
    

Solution:

  • デバッグ ログを有効にしてトークン検証の詳細を表示する
  • トークン検証に複数の有効な対象ユーザーを追加する
  • X-Forwarded-* ヘッダーがゲートウェイによって転送されることを確認する

問題: ヘルスプローブが失敗する

症状:

  • ゲートウェイによってバックエンドが異常としてマークされる
  • ヘルスエンドポイントから 401 が返される

Solution:

認証ミドルウェアの前にヘルスチェックエンドポイントが実行されるようにします。

// Ensure health endpoint is BEFORE authentication
app.MapHealthChecks("/health").AllowAnonymous();

// Alternative: Use custom middleware
app.Map("/health", healthApp =>
{
    healthApp.Run(async context =>
    {
        context.Response.StatusCode = 200;
        await context.Response.WriteAsync("healthy");
    });
});

app.UseAuthentication(); // Health endpoint bypasses this

問題: Front Door の背後にある CORS エラー

症状:

  • プレフライト OPTIONS 要求が失敗する
  • ブラウザー コンソールに CORS エラーが表示される

Solution:

CORS ポリシーに Front Door とフロントエンドのオリジンを追加します。

builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.WithOrigins(
            "https://your-frontend.azurefd.net",
            "https://your-app.com"
        )
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials();
    });
});

var app = builder.Build();

app.UseForwardedHeaders();
app.UseCors(); // Before authentication
app.UseAuthentication();
app.UseAuthorization();

問題: ログにおける「転送されたヘッダー」の警告

症状:

Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersMiddleware: Unknown proxy

Solution:

既知のネットワークとプロキシをクリアして、Azureインフラストラクチャから転送されたヘッダーを受け入れます。

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    // Clear known networks to accept from any proxy
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();

    // Or explicitly add Azure IP ranges (more secure but complex)
    // options.KnownProxies.Add(IPAddress.Parse("20.x.x.x"));
});

問題: APIM は 401 を返しますが、バックエンドは 200 を返します

症状:

  • トークンはバックエンドに対して有効です
  • APIM validate-jwt ポリシーが失敗する

Solution:

APIM ポリシーの対象ユーザーがトークンの対象ユーザーと一致するかどうかを確認します。

<validate-jwt header-name="Authorization">
    <openid-config url="https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration" />
    <audiences>
        <!-- Must match the 'aud' claim in your token -->
        <audience>api://your-backend-api-client-id</audience>
    </audiences>
</validate-jwt>

問題: 複数の認証スキームが競合する

症状:

  • JWT ベアラーとその他のスキームの両方を使用する
  • 誤ったスキームが選択されている

Solution:

コントローラーで認証スキームを明示的に指定します。

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

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .AddScheme<MyCustomOptions, MyCustomHandler>("CustomScheme", options => {});

// In controller, specify scheme explicitly
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class WeatherForecastController : ControllerBase
{
    // ...
}

ベスト プラクティスに従う

これらのプラクティスを適用して、ゲートウェイの背後に安全で回復性の高い API デプロイを構築します。

1. 多層防御

ゲートウェイがトークンを検証した場合でも、バックエンド API でトークンを常に検証します。

// Gateway validates token (APIM policy)
// Backend ALSO validates token (Microsoft.Identity.Web)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

ゲートウェイの構成は変更でき、トークンを再生できます。 多層防御は、セキュリティにとって非常に重要です。

2. ゲートウェイ間通信にマネージド ID を使用する

ゲートウェイが独自の ID でバックエンドを呼び出す場合は、ユーザー トークンとマネージド ID トークンの両方を受け入れるようにバックエンドを構成します。

// Backend accepts both user tokens and gateway's managed identity
builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters.ValidAudiences = new[]
    {
        "api://backend-api-client-id", // User tokens
        "https://management.azure.com" // Managed identity tokens (if applicable)
    };
});

3.ゲートウェイ メトリックを監視する

ゲートウェイのデプロイの可視性を維持するために、次の主要なメトリックを追跡します。

  • 401/403 エラー率
  • トークン検証エラー
  • 正常性プローブの障害
  • 転送されたヘッダー (デバッグ用)

4. Application Insights を使用する

Application Insights テレメトリを追加して、ゲートウェイ固有の要求プロパティをログに記録します。

builder.Services.AddApplicationInsightsTelemetry();

// Log custom properties
app.Use(async (context, next) =>
{
    var telemetry = context.RequestServices.GetRequiredService<TelemetryClient>();
    telemetry.TrackEvent("ApiRequest", new Dictionary<string, string>
    {
        ["ForwardedFor"] = context.Request.Headers["X-Forwarded-For"],
        ["OriginalHost"] = context.Request.Headers["X-Forwarded-Host"],
        ["Gateway"] = "APIM" // or "FrontDoor", "AppGateway"
    });

    await next();
});

5. 正常性を準備完了から分離する

ライブネス (サービスは実行中ですか?) と準備 (サービスがトラフィックを受け入れるか) のチェックには、個別のエンドポイントを使用します。

// Health: Is the service running?
app.MapGet("/health", () => Results.Ok()).AllowAnonymous();

// Ready: Can the service accept traffic?
app.MapHealthChecks("/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready")
}).AllowAnonymous();

builder.Services.AddHealthChecks()
    .AddCheck("database", () => /* check DB */ , tags: new[] { "ready" })
    .AddCheck("cache", () => /* check cache */ , tags: new[] { "ready" });

6. ゲートウェイの構成を文書化する

次のドキュメントを含む README または Wiki ページを作成します。

  • 使用中のゲートウェイはどれですか
  • トークンの対象ユーザーの期待
  • CORS 構成
  • ヘルスプローブエンドポイント
  • 転送されたヘッダーの構成
  • 緊急ロールバック手順

Azure API Managementを使用して完全な例を作成する

このセクションでは、Microsoft Entra ID認証を使用したAzure API Managementの背後にある ASP.NET Core API の完全な実稼働対応の例を示します。

バックエンド API (ASP.NET Core)

次の Program.cs では、転送ヘッダー、Microsoft Entra 認証、正常性チェック、および Application Insights を構成します。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Forwarded headers for APIM
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.All;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddMicrosoftGraph()
    .AddInMemoryTokenCaches();

// Application Insights
builder.Services.AddApplicationInsightsTelemetry();

// Health checks
builder.Services.AddHealthChecks();

builder.Services.AddControllers();

var app = builder.Build();

// Health endpoint (unauthenticated)
app.MapHealthChecks("/health").AllowAnonymous();

// Middleware order is critical
app.UseForwardedHeaders();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

次のMicrosoft Entraと Application Insights の構成を appsettings.json に追加します。

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "backend-api-client-id",
    "Audience": "api://backend-api-client-id"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.Identity.Web": "Debug"
    }
  },
  "ApplicationInsights": {
    "ConnectionString": "your-connection-string"
  }
}

次のコントローラーでは、デバッグ用の認証ヘッダーとログ転送ヘッダーが必要です。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web.Resource;

[Authorize]
[ApiController]
[Route("[controller]")]
[RequiredScope("access_as_user")]
public class WeatherForecastController : ControllerBase
{
    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IActionResult Get()
    {
        // Log forwarded headers for debugging
        var forwardedFor = HttpContext.Request.Headers["X-Forwarded-For"];
        var forwardedHost = HttpContext.Request.Headers["X-Forwarded-Host"];

        _logger.LogInformation(
            "Request from {ForwardedFor} via {ForwardedHost}",
            forwardedFor,
            forwardedHost);

        return Ok(new[] { "Weather", "Forecast", "Data" });
    }
}

APIM 構成

次の受信ポリシーは、JWT トークンの検証、レート制限の適用、ヘッダーの転送、CORS の構成を行います。

<policies>
    <inbound>
        <base />

        <!-- Rate limiting per subscription -->
        <rate-limit-by-key calls="100" renewal-period="60"
                           counter-key="@(context.Subscription.Id)" />

        <!-- Validate JWT -->
        <validate-jwt header-name="Authorization"
                      failed-validation-httpcode="401"
                      failed-validation-error-message="Unauthorized">
            <openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
            <audiences>
                <audience>api://backend-api-client-id</audience>
            </audiences>
            <issuers>
                <issuer>https://login.microsoftonline.com/{tenant-id}/v2.0</issuer>
            </issuers>
            <required-claims>
                <claim name="scp" match="any">
                    <value>access_as_user</value>
                </claim>
            </required-claims>
        </validate-jwt>

        <!-- Forward headers -->
        <set-header name="X-Forwarded-Host" exists-action="override">
            <value>@(context.Request.OriginalUrl.Host)</value>
        </set-header>
        <set-header name="X-Forwarded-Proto" exists-action="override">
            <value>@(context.Request.OriginalUrl.Scheme)</value>
        </set-header>

        <!-- Backend URL -->
        <set-backend-service base-url="https://your-backend.azurewebsites.net" />
    </inbound>

    <backend>
        <base />
    </backend>

    <outbound>
        <base />

        <!-- Add CORS headers if needed -->
        <cors>
            <allowed-origins>
                <origin>https://your-frontend.com</origin>
            </allowed-origins>
            <allowed-methods>
                <method>GET</method>
                <method>POST</method>
            </allowed-methods>
            <allowed-headers>
                <header>*</header>
            </allowed-headers>
        </cors>
    </outbound>

    <on-error>
        <base />
    </on-error>
</policies>