Condividi tramite


Distribuzione di API protette dietro i gateway

Questa guida illustra come distribuire ASP.NET Core API Web protette con Microsoft. Identity.Web dietro Azure gateway API e proxy inversi, tra cui Gestione API di Azure (GESTIONE API), Frontdoor di Azure e gateway applicazione di Azure.

Informazioni generali

Quando si distribuiscono API protette dietro i gateway, è necessario gestire:

  • Intestazioni inoltrate - Mantenere il contesto di richiesta originale (schema, host, IP)
  • Convalida dei token : assicurarsi che le attestazioni del gruppo di destinatari corrispondano agli URL del gateway
  • Configurazione CORS : gestire correttamente le richieste tra le origini
  • Endpoint di integrità : fornire controlli di integrità non autenticati
  • Routing basato sul percorso - Supportare i prefissi di percorso a livello di gateway
  • Terminazione SSL/TLS : gestire correttamente HTTPS quando il gateway termina SSL

Scenari di gateway comuni

Gestione API di Azure (APIM)

Caso d'uso: Gateway API aziendale con criteri, limitazione della frequenza, trasformazione

Architettura:

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

Considerazioni chiave:

  • Alcuni criteri di Gestione API possono convalidare i token JWT prima dell'inoltro al back-end
  • L'API back-end convalida ancora i token
  • L'attestazione del gruppo di destinatari deve corrispondere all'URL di Gestione API o all'URL back-end (configurare di conseguenza)

Frontdoor di Azure

Caso d'uso: Bilanciamento del carico globale, rete CDN, protezione DDoS

Architettura:

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

Considerazioni chiave:

  • Frontdoor inoltra le richieste con X-Forwarded-* intestazioni
  • Terminazione SSL/TLS in Frontdoor
  • La convalida del gruppo di destinatari dei token richiede la configurazione

gateway applicazione di Azure

Caso d'uso: Bilanciamento del carico a livello di area, WAF, routing basato sul percorso

Architettura:

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

Considerazioni chiave:

  • Integrazione di Web application firewall (WAF)
  • Regole di routing basate sul percorso
  • I probe di integrità back-end necessitano di endpoint non autenticati

Modelli di configurazione

1. Middleware delle intestazioni inoltrate

Configurare sempre il middleware delle intestazioni inoltrate quando si è dietro un gateway:

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();

Perché questo è importante:

  • Mantiene l'indirizzo IP del client originale per la registrazione
  • Assicura che HttpContext.Request.Scheme rifletta https originale
  • Intestazione corretta Host per gli URL di reindirizzamento e la convalida del token

2. Configurazione del gruppo di destinatari dei token

Opzione A: accettare url gateway e back-end

{
  "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"
      ]
    }
  }
}

Configurazione del codice:

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));
    };
});

Opzione B: Riscrivere i destinatari nei criteri di Gestione API

Configurare Gestione API per riscrivere l'attestazione del gruppo di destinatari prima dell'inoltro:

<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. Configurazione dell'endpoint di integrità

I gateway necessitano di endpoint di integrità non autenticati per i probe:

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();

Alternative con controlli di integrità ASP.NET Core:

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. Configurazione CORS dietro i gateway

Quando si usano Frontdoor di Azure o Gestione API con applicazioni front-end:

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();

Importante: È necessario configurare CORS dopo le intestazioni inoltrate e prima dell'autenticazione.


Integrazione di Gestione API di Azure (APIM)

Completare la configurazione di Gestione API

1. Configurazione dell'API back-end

Program.cs:

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();

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"
  }
}

2. Criteri in ingresso di Gestione API (convalida 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>

3. Configurazione api di Gestione API

Valori denominati (per la riutilizzabilità):

  • tenant-id: ID tenant Microsoft Entra
  • backend-api-client-id: ID client dell'API back-end
  • backend-base-url: https://your-backend.azurewebsites.net

Impostazioni API:

  • Suffisso URL API: /api (prefisso del percorso facoltativo)
  • URL del servizio Web: impostare tramite criteri usando valori denominati
  • Sottoscrizione obbligatoria: Sì (aggiunge un altro livello di sicurezza)

4. Configurazione dell'applicazione client

Le app client richiedono token per l'API back-end, non gestione 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");

integrazione di Frontdoor di Azure

Configurazione per la distribuzione globale

1. Configurazione dell'API back-end

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();

2. Configurazione dell'origine frontdoor

portale di Azure Impostazioni:

  1. Creare un profilo frontdoor
  2. Aggiungere un gruppo di origine con le istanze dell'API back-end
  3. Configurare i probe di integrità per /health l'endpoint
  4. Impostare solo l'inoltro HTTPS
  5. Abilitare i criteri WAF (facoltativo)

Impostazioni probe di integrità:

  • Percorso: /health
  • Protocollo: HTTPS
  • Metodo: GET
  • Intervallo: 30 secondi

3. Gestione di più aree

Quando si esegue la distribuzione in più aree dietro Frontdoor:

// 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();
});

4. Frontdoor e convalida dei token

I destinatari dei token devono includere l'URL di Frontdoor se i client richiedono token:

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"]
    };
});

integrazione di gateway applicazione di Azure

Configurazione con WAF

1. Configurazione dell'API back-end

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();

2. Configurazione del gateway applicazione

Impostazioni back-end:

  • Protocollo: HTTPS (scelta consigliata) o HTTP
  • Porta: 443 o 80
  • Eseguire l'override del percorso back-end: No (a meno che non sia necessario)
  • Probe personalizzato: Sì, che punta a /health

Probe di integrità:

  • Protocollo: HTTPS o HTTP
  • Host: lasciare l'impostazione predefinita o specificare
  • Percorso: /health
  • Intervallo: 30 secondi
  • Soglia non integra: 3

Criteri WAF:

  • Abilitare WAF con il set di regole OWASP 3.2
  • Importante: assicurarsi che i token JWT nelle intestazioni di autorizzazione non siano bloccati
  • Potrebbe essere necessario creare esclusioni WAF per RequestHeaderNames contenere "Autorizzazione"

3. Routing basato sul percorso

Quando si usano regole di routing basate sul percorso:

// 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();

Regola del gateway applicazione:

  • Percorso: /api/v1/*
  • Destinazione back-end: pool back-end
  • Impostazioni back-end: usare le impostazioni configurate

Risoluzione dei problemi

Problema: 401 Non autorizzato dopo la distribuzione dietro il gateway

Sintomi:

  • L'API funziona in locale ma restituisce 401 dietro il gateway
  • Il token sembra valido quando decodificato in jwt.ms

Possibili cause:

  1. Mancata corrispondenza dell'attestazione del gruppo di destinatari

    # 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. Middleware delle intestazioni inoltrate mancanti

    // Ensure this is BEFORE authentication
    app.UseForwardedHeaders();
    app.UseAuthentication();
    
  3. Problemi di reindirizzamento HTTPS

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

Soluzione:

  • Abilitare la registrazione di debug per visualizzare i dettagli di convalida dei token
  • Aggiungere più gruppi di destinatari validi nella convalida del token
  • Controllare che le intestazioni X-Forwarded-* vengano inoltrate dal gateway

Problema: i probe di integrità hanno esito negativo

Sintomi:

  • Il gateway contrassegna il back-end come non integro
  • L'endpoint di integrità restituisce 401

Soluzione:

// 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

Problema: errori CORS dietro Frontdoor

Sintomi:

  • Le richieste OPTIONS preliminari hanno esito negativo
  • La console del browser mostra gli errori CORS

Soluzione:

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();

Problema: i log di convalida dei token mostrano avvisi "Intestazione inoltrata"

Sintomi:

Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersMiddleware: Unknown proxy

Soluzione:

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"));
});

Problema: Gestione API restituisce 401 ma il back-end restituisce 200

Sintomi:

  • Il token è valido per il back-end
  • I criteri validate-jwt di Gestione API hanno esito negativo

Soluzione:

Controllare che il gruppo di destinatari dei criteri di Gestione API corrisponda al gruppo di destinatari dei token:

<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>

Problema: conflitti tra più schemi di autenticazione

Sintomi:

  • Uso sia del bearer JWT che di altri schemi
  • Schema errato selezionato

Soluzione:

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
{
    // ...
}

Procedure consigliate

1. Difesa in profondità

Convalidare sempre i token nell'API back-end, anche se il gateway li convalida

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

Perché: La configurazione del gateway può cambiare, i token possono essere riprodotti, la difesa avanzata è fondamentale per la sicurezza.

2. Usare le identità gestite per gateway-to-backend

Se il gateway deve chiamare il back-end con la propria identità:

// 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. Monitorare le metriche del gateway

  • Tenere traccia delle percentuali di errore 401/403
  • Monitorare gli errori di convalida dei token
  • Avviso per gli errori del probe di integrità
  • Registrare le intestazioni inoltrate per il debug

4. Usare 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. Separare l'integrità da pronto

// 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. Documentare la configurazione del gateway

Creare una pagina README o wiki che documenta:

  • Quali gateway sono in uso
  • Aspettative dei destinatari dei token
  • Configurazione CORS
  • Endpoint del probe di integrità
  • Configurazione delle intestazioni inoltrate
  • Procedure di rollback di emergenza

Esempio completo: API dietro Gestione API di Azure

API back-end (ASP.NET Core)

Program.cs:

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();

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"
  }
}

WeatherForecastController.cs:

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" });
    }
}

Configurazione di Gestione API

Criteri in ingresso:

<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>