Utiliser des identités d’utilisateur dans l’authentification Azure App Service

Cet article explique comment utiliser les identités d’utilisateur quand vous utilisez l’authentification et l’autorisation intégrées dans App Service.

Accéder aux revendications de l’utilisateur dans le code de l’application

Pour toutes les infrastructures de langage, App Service met les revendications du jeton entrant (qu’elles proviennent d’un utilisateur final authentifié ou d’une application cliente) à la disposition de votre code en les injectant dans les en-têtes des demandes. Les demandes externes ne sont pas autorisées à définir ces en-têtes, qui sont donc présents uniquement s’ils sont définis par App Service. Voici quelques exemples d’en-têtes :

En-tête Description
X-MS-CLIENT-PRINCIPAL Une représentation JSON encodée en Base64 des revendications disponibles. Pour plus d’informations, consultez Décodage de l’en-tête du principal du client.
X-MS-CLIENT-PRINCIPAL-ID Un identificateur de l’appelant défini par le fournisseur d’identité.
X-MS-CLIENT-PRINCIPAL-NAME Nom lisible par l’utilisateur pour l’appelant défini par le fournisseur d’identité, tel que l’adresse e-mail, nom d’utilisateur principal.
X-MS-CLIENT-PRINCIPAL-IDP Le nom du fournisseur d’identité utilisé par l’authentification App Service.

Les jetons de fournisseur sont également exposés via des en-têtes similaires. Par exemple, Microsoft Entra définit également X-MS-TOKEN-AAD-ACCESS-TOKEN et X-MS-TOKEN-AAD-ID-TOKEN selon les besoins.

Remarque

Différentes infrastructures de langage peuvent présenter ces en-têtes au code d’application dans différents formats, tels que des minuscules ou des majuscules.

Tout code, quels que soient le langage ou l’infrastructure utilisés, peut trouver les informations qu’il recherche dans ces en-têtes. Le décodage de l’en-tête du principal client couvre ce processus. Pour certains frameworks, la plateforme fournit également des options supplémentaires pouvant être plus pratiques.

Décodage de l’en-tête du principal du client

X-MS-CLIENT-PRINCIPAL contient l’ensemble complet des revendications disponibles sous forme de JSON encodé en Base64. Ces revendications passent par un processus de mappage de revendications par défaut, de sorte que certaines peuvent avoir des noms différents de ceux que vous verriez si le jeton était traité directement. La charge utile décodée de la réponse est structurée comme suit :

{
    "auth_typ": "",
    "claims": [
        {
            "typ": "",
            "val": ""
        }
    ],
    "name_typ": "",
    "role_typ": ""
}
Propriété Type Description
auth_typ string Le nom du fournisseur d’identité utilisé par l’authentification App Service.
claims tableau d’objets Un tableau d’objets représentant les revendications disponibles. Chaque objet contient les propriétés typ et val.
typ chaîne Le nom de la revendication. Cela peut faire l’objet d’un mappage de revendications par défaut et peut être différent de la revendication correspondante contenue dans un jeton.
val string Valeur de la revendication.
name_typ chaîne Le type de revendication de nom, qui est généralement un URI fournissant des informations de schéma sur la revendication name si l’une est définie.
role_typ chaîne Le type de revendication de type, qui est généralement un URI fournissant des informations de schéma sur la revendication role si l’une est définie.

Pour traiter cet en-tête, votre application doit décoder la charge utile et itérer à travers le tableau claims pour trouver les revendications qui vous intéressent. Il peut être pratique de les convertir en une représentation utilisée par l’infrastructure de langage de l’application. Voici un exemple de ce processus en C# qui construit un type ClaimsPrincipal pour que l’application l’utilise :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http;

public static class ClaimsPrincipalParser
{
    private class ClientPrincipalClaim
    {
        [JsonPropertyName("typ")]
        public string Type { get; set; }
        [JsonPropertyName("val")]
        public string Value { get; set; }
    }

    private class ClientPrincipal
    {
        [JsonPropertyName("auth_typ")]
        public string IdentityProvider { get; set; }
        [JsonPropertyName("name_typ")]
        public string NameClaimType { get; set; }
        [JsonPropertyName("role_typ")]
        public string RoleClaimType { get; set; }
        [JsonPropertyName("claims")]
        public IEnumerable<ClientPrincipalClaim> Claims { get; set; }
    }

    public static ClaimsPrincipal Parse(HttpRequest req)
    {
        var principal = new ClientPrincipal();

        if (req.Headers.TryGetValue("x-ms-client-principal", out var header))
        {
            var data = header[0];
            var decoded = Convert.FromBase64String(data);
            var json = Encoding.UTF8.GetString(decoded);
            principal = JsonSerializer.Deserialize<ClientPrincipal>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        }

        /** 
         *  At this point, the code can iterate through `principal.Claims` to
         *  check claims as part of validation. Alternatively, we can convert
         *  it into a standard object with which to perform those checks later
         *  in the request pipeline. That object can also be leveraged for 
         *  associating user data, etc. The rest of this function performs such
         *  a conversion to create a `ClaimsPrincipal` as might be used in 
         *  other .NET code.
         */

        var identity = new ClaimsIdentity(principal.IdentityProvider, principal.NameClaimType, principal.RoleClaimType);
        identity.AddClaims(principal.Claims.Select(c => new Claim(c.Type, c.Value)));
        
        return new ClaimsPrincipal(identity);
    }
}

Alternatives spécifiques une infrastructure

Dans le cas des applications ASP.NET 4.6, App Service remplit ClaimsPrincipal.Current avec les revendications de l’utilisateur authentifié, ce qui vous permet de suivre le modèle de code .NET standard, attribut [Authorize] compris. De même, pour les applications PHP, App Service remplit la variable _SERVER['REMOTE_USER']. Pour les applications Java, les revendications sont accessibles depuis le servlet Tomcat.

Pour Azure Functions, ClaimsPrincipal.Current n’est pas rempli pour le code .NET, mais vous pouvez toujours trouver les revendications utilisateur dans les en-têtes de demande, ou obtenir l’objet ClaimsPrincipal à partir du contexte de la demande ou même avec un paramètre de liaison. Pour plus d’informations, consultez Utilisation d’identités de client dans Azure Functions.

Pour .NET Core, Microsoft.Identity.Web prend en charge le remplissage de l’utilisateur actuel à l’aide de l’authentification App Service. Pour en savoir plus, consultez le wiki Microsoft.Identity.Web, ou regardez une démonstration dans ce tutoriel ayant trait à une application web qui accède à Microsoft Graph.

Notes

Pour que le mappage des revendications fonctionne, vous devez activer le magasin de jetons.

Accéder aux revendications de l’utilisateur à l’aide de l’API

Si le magasin de jetons est activé pour votre application, vous pouvez également obtenir d’autres informations sur l’utilisateur authentifié en appelant /.auth/me.

Étapes suivantes