Déployer des API protégées derrière des passerelles

Déployez des API web ASP.NET Core protégées par Microsoft.Identity.Web derrière des passerelles API Azure et des serveurs mandataires inverses, y compris Gestion des API Azure (APIM), Azure Front Door et Azure Application Gateway.

Comprendre la configuration requise pour la passerelle

Lorsque vous déployez des API protégées derrière des passerelles, vous devez gérer plusieurs problèmes :

  • En-têtes transférés - Conserver le contexte de requête d’origine (schéma, hôte, ADRESSE IP)
  • Validation de jeton - Vérifier que les assertions d’audience correspondent aux URL de passerelle
  • Configuration CORS - Gérer correctement les requêtes inter-origines
  • Points d'accès de santé - Fournir des vérifications de santé non authentifiées
  • Routage basé sur le chemin - Supporter les préfixes de chemin au niveau de la passerelle
  • Arrêt SSL/TLS - Gérer le protocole HTTPS correctement lorsque la passerelle met fin à SSL

Passer en revue les scénarios de passerelle courants

Choisissez une passerelle en fonction de vos besoins. Les sections suivantes décrivent les services de passerelle Azure les plus courants pour les API protégées.

Gestion des API Azure (APIM)

Cas d’usage : Passerelle d’API d’entreprise avec des stratégies, limitation de débit, transformation

Architecture :

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

Considérations clés :

  • Les stratégies APIM peuvent valider les jetons JWT avant de les transférer vers le back-end
  • L’API back-end valide toujours les jetons
  • La revendication d’audience doit correspondre à l’URL APIM ou à l’URL principale (configurer en conséquence)

Azure Front Door (une solution de gestion du trafic réseau de Microsoft)

Cas d’usage : Équilibrage de charge global, CDN, protection DDoS

Architecture :

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

Considérations clés :

  • Front Door transfère les demandes avec les en-têtes X-Forwarded-*
  • Terminaison SSL/TLS à Front Door
  • Configuration nécessaire pour la validation de l’audience de jeton

Azure Application Gateway

Cas d’usage : Équilibrage de charge régional, WAF, routage basé sur le chemin

Architecture :

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

Considérations clés :

  • Intégration de Web Application Firewall (WAF)
  • Règles de routage basées sur le chemin
  • Les sondes d’intégrité du serveur principal ont besoin de points de terminaison non authentifiés

Configurer des modèles courants

Appliquez ces modèles de configuration pour vous assurer que votre API protégée fonctionne correctement derrière n’importe quelle passerelle.

1. Middleware des en-têtes transmis

Configurez toujours le middleware des en-têtes transmis lorsque vous êtes derrière une passerelle. Le code suivant inscrit le middleware et le définit pour s’exécuter avant l’authentification :

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

L’intergiciel des en-têtes transférés est essentiel, car il :

  • Conserve l’adresse IP du client d’origine pour la journalisation
  • Garantit que HttpContext.Request.Scheme reflète le schéma HTTPS d'origine
  • Fournit l’en-tête correct Host pour les URL de redirection et la validation des jetons

2. Configuration de l’audience du jeton

Option A : Accepter les URL de passerelle et de back-end

Ajoutez plusieurs audiences valides dans votre appsettings.json configuration :

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

Vous pouvez également configurer plusieurs audiences par programmation dans 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));
    };
});

Option B : Réécrire l’audience dans la stratégie APIM

Configurez APIM pour valider la revendication d’audience avant de transférer vers le back-end :

<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. Configuration du point de terminaison de santé

Les passerelles nécessitent des points de terminaison de santé non authentifiés pour les sondes. Mappez un point de terminaison de santé avant le middleware d'authentification pour contourner la validation des jetons :

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

Vous pouvez également utiliser l'infrastructure intégrée d'ASP.NET Core Health Checks pour obtenir des rapports d’intégrité plus détaillés :

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. Configuration CORS derrière les passerelles

Lorsque vous utilisez Azure Front Door ou APIM avec des applications frontales, configurez CORS pour autoriser les requêtes provenant de vos origines de passerelle :

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

Important

CORS doit être configuré après les en-têtes transférés et avant l’authentification.


Intégrer à Gestion des API Azure

Cette section fournit la configuration complète pour le déploiement d’une API protégée derrière Gestion des API Azure.

Configurer l’API back-end

Configurez les en-têtes transférés et l’authentification Microsoft Entra ID dans 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();

Ajoutez la configuration 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"
  }
}

Ajouter une stratégie de trafic entrant APIM pour la validation JWT

Définissez une stratégie de trafic entrant qui valide le jeton JWT, applique la limitation du débit et transfère la requête au back-end :

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

Configurer les paramètres de l’API APIM

Utilisez les valeurs nommées et les paramètres d’API suivants pour terminer la configuration APIM :

Valeurs nommées (pour une réutilisation) :

  • tenant-id : Votre ID de client Microsoft Entra
  • backend-api-client-id: ID client de l’API back-end
  • backend-base-url: https://your-backend.azurewebsites.net

Paramètres de l’API :

  • Suffixe d’URL de l’API : /api (préfixe de chemin facultatif)
  • URL du service web : définir par le biais d’une stratégie à l’aide de valeurs nommées
  • Abonnement requis : Oui (ajoute une autre couche de sécurité)

Configurer l’application cliente

Les applications clientes demandent des jetons pour l’API back-end, et non APIM. Le code suivant acquiert un jeton et appelle l’API via le point de terminaison APIM :

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

Intégrer avec Azure Front Door

Configurez votre API protégée pour la distribution globale derrière Azure Front Door.

Configurer l’API back-end

Configurez les en-têtes transférés pour Azure Front Door dans 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();

Configurer les origines Front Door

Effectuez les étapes suivantes dans le portail Azure pour configurer l’origine Front Door :

  1. Créer un profil Front Door
  2. Ajouter un groupe d’origine avec vos instances d’API back-end
  3. Configurer des sondes d’intégrité sur le point de terminaison /health
  4. Définir le transfert HTTPS uniquement
  5. Activer la stratégie WAF (facultatif)

Paramètres de la sonde d’intégrité :

  • Path (Chemin d’accès) : /health
  • Protocole : HTTPS
  • Méthode : GET
  • Intervalle : 30 secondes

Gérer plusieurs régions

Lorsque vous effectuez un déploiement dans plusieurs régions derrière Front Door, ajoutez la prise en compte des régions pour la journalisation et les diagnostics :

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

Valider des jetons avec Front Door

Si les clients demandent des jetons délimités à l’URL Front Door, ajoutez-le à la liste des audiences valides :

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

Intégrer à Azure Application Gateway

Configurez votre API protégée derrière Azure Application Gateway avec la prise en charge de Web Application Firewall (WAF).

Configurer l’API back-end

Configurez les en-têtes proxy pour Application Gateway dans 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();

Configurer les paramètres d’Application Gateway

Définissez les paramètres de back-end, de sonde d’intégrité et de WAF suivants dans le portail Azure :

Paramètres principaux :

  • Protocole : HTTPS (recommandé) ou HTTP
  • Port : 443 ou 80
  • Remplacer le chemin principal : Non (sauf si nécessaire)
  • Sonde personnalisée : Oui, pointant vers /health

Sonde d’intégrité :

  • Protocole : HTTPS ou HTTP
  • Hôte : conservez la valeur par défaut ou spécifiez
  • Path (Chemin d’accès) : /health
  • Intervalle : 30 secondes
  • Seuil non sain : 3

Stratégie WAF :

  • Activer WAF avec l’ensemble de règles OWASP 3.2
  • Important : Vérifier que les jetons JWT dans les en-têtes Authorization ne sont pas bloqués
  • Vous devrez peut-être créer des exclusions WAF pour RequestHeaderNames contenir « Autorisation »

Configurer le routage basé sur le chemin

Lorsque vous utilisez des règles de routage basées sur le chemin, configurez votre API back-end pour gérer le préfixe de chemin d’accès :

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

Règle Application Gateway :

  • Path (Chemin d’accès) : /api/v1/*
  • Cible back-end : votre pool de backend
  • Paramètres principaux : Utiliser les paramètres configurés

Résoudre les problèmes courants

Utilisez ces solutions pour résoudre les problèmes les plus courants lors du déploiement d’API protégées derrière des passerelles.

Problème : 401 Non autorisé après le déploiement derrière la passerelle

Symptômes :

  • L’API fonctionne localement, mais retourne 401 derrière la passerelle
  • Le jeton semble valide lorsqu’il est décodé à jwt.ms

Causes possibles :

  1. Incompatibilité de revendication d’audience

    # 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. Intergiciel des en-têtes transférés manquants

    // Ensure this is BEFORE authentication
    app.UseForwardedHeaders();
    app.UseAuthentication();
    
  3. Problèmes de redirection HTTPS

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

Solution:

  • Activer la journalisation du débogage pour afficher les détails de validation des jetons
  • Ajouter plusieurs audiences valides dans la validation des jetons
  • Vérifier que les en-têtes X-Forwarded-* sont transmis par la passerelle

Problème : Échec des sondes d’intégrité

Symptômes :

  • La passerelle marque le backend comme en mauvais état
  • Le point de terminaison d’intégrité retourne 401

Solution:

Assurez-vous que le point de terminaison de santé s’exécute avant l’intergiciel d’authentification :

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

Problème : erreurs CORS derrière Front Door

Symptômes :

  • Échec des requêtes OPTIONS préliminaires
  • La console du navigateur affiche les erreurs CORS

Solution:

Ajoutez vos origines Front Door et frontend à la stratégie CORS :

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

Problème : avertissements « En-tête transféré » dans les logs

Symptômes :

Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersMiddleware: Unknown proxy

Solution:

Effacez les réseaux et proxys connus pour accepter les en-têtes transférés de Azure infrastructure :

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

Problème : APIM retourne 401, mais le back-end retourne 200

Symptômes :

  • Le jeton est valide pour le back-end
  • Échec de la stratégie APIM validate-jwt

Solution:

Vérifiez que l’audience de la stratégie APIM correspond à celle du jeton :

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

Problème : Conflit de plusieurs schémas d’authentification

Symptômes :

  • Utilisation du jeton porteur JWT et d’autres schémas
  • Le schéma incorrect est sélectionné

Solution:

Spécifiez explicitement le schéma d’authentification dans le contrôleur :

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

Suivre les bonnes pratiques

Appliquez ces pratiques pour créer un déploiement d’API sécurisé et résilient derrière les passerelles.

1. Défense en profondeur

Validez toujours les jetons dans l’API back-end, même si la passerelle les valide :

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

La configuration de la passerelle peut changer et les jetons peuvent être rejoués. La défense en profondeur est essentielle pour la sécurité.

2. Utiliser des identités managées pour la communication de passerelle à back-end

Si votre passerelle appelle le back-end avec sa propre identité, configurez le serveur principal pour accepter à la fois les jetons utilisateur et les jetons d’identité managée :

// 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. Surveiller les métriques de passerelle

Suivez ces métriques clés pour maintenir la visibilité de votre déploiement de passerelle :

  • Taux d’erreur 401/403
  • Échecs de validation de jetons
  • Échecs de sonde de santé
  • En-têtes transférés (pour le débogage)

4. Utiliser Application Insights

Ajoutez des données de télémétrie Application Insights pour journaliser les propriétés de requête spécifiques à la passerelle :

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. Séparer les éléments fonctionnels des éléments prêts

Utilisez des points de terminaison distincts pour la liveness (le service est-il en cours d’exécution ?) et la readiness (le service peut-il accepter le trafic ?) vérifications :

// 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. Documenter la configuration de votre passerelle

Créez une page README ou wiki qui documente :

  • Quelles passerelles sont en cours d’utilisation
  • Attentes des utilisateurs de tokens
  • Configuration CORS
  • Points de terminaison de sonde de santé
  • Configuration des en-têtes transférés
  • Procédures de restauration d’urgence

Créer un exemple complet avec Gestion des API Azure

Cette section fournit un exemple complet et prêt pour la production d’une API ASP.NET Core derrière Gestion des API Azure avec l’authentification Microsoft Entra ID.

API back-end (ASP.NET Core)

Le Program.cs suivant configure les en-têtes transférés, l’authentification Microsoft Entra, les contrôles de l'état de santé et 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();

Ajoutez la configuration Microsoft Entra et Application Insights suivante à 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"
  }
}

Le contrôleur suivant requiert une authentification et enregistre les en-têtes transférés pour le débogage :

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

Configuration APIM

La stratégie de trafic entrant suivante valide les jetons JWT, applique la limitation du débit, les en-têtes de transfert et configure 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>