Uso de Graph API con Blazor WebAssembly de ASP.NET Core

Nota

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión .NET 8 de este artículo.

En este artículo se explica cómo usar Microsoft Graph API en aplicaciones Blazor WebAssembly, que es una API web RESTful que permite a las aplicaciones acceder a los recursos del servicio Microsoft Cloud.

Hay dos enfoques disponibles para interactuar directamente con Microsoft Graph en aplicaciones Blazor:

  • SDK de Graph: los SDK de Microsoft Graph se han diseñado para simplificar la compilación de aplicaciones de alta calidad, eficaces y resistentes que acceden a Microsoft Graph. Seleccione el botón SDK de Graph situado en la parte superior de este artículo para adoptar este enfoque.

  • HttpClient con nombre con Graph API: un elemento con nombre HttpClient puede emitir solicitudes de API web para Graph API directamente. Seleccione el botón HttpClient con nombre con Graph API en la parte superior de este artículo para adoptar este enfoque.

Las instrucciones de este artículo no están diseñadas para reemplazar la documentación principal de Microsoft Graph y otras instrucciones de seguridad de Azure de otros conjuntos de documentación de Microsoft. Evalúe las instrucciones de seguridad de la sección Recursos adicionales de este artículo antes de implementar Microsoft Graph en un entorno de producción. Siga todos los procedimientos recomendados de Microsoft para limitar el área expuesta a ataques de las aplicaciones.

Importante

Los escenarios descritos en este artículo se aplican al uso de Microsoft Entra (ME-ID) como proveedor de identidades, no a AAD B2C. El uso de Microsoft Graph con una aplicación Blazor WebAssembly del lado cliente y el proveedor de identidades de AAD B2C no se admite en este momento porque la aplicación requeriría un secreto de cliente, que no se puede proteger en la aplicación Blazor del lado cliente. Para una aplicación Blazor WebAssembly independiente de AAD B2C use Graph API, cree una API de servidor backend (web) para acceder a Graph API en nombre de los usuarios. La aplicación del lado cliente autentica a los usuarios y los autoriza a llamar a la API web para acceder a Microsoft Graph de forma segura y devolver datos a la aplicación Blazor del lado cliente. El secreto de cliente se mantiene de forma segura en la API web basada en servidor, no en la aplicación Blazor en el cliente. Nunca almacene un secreto de cliente en una aplicación Blazor del lado cliente.

Se admite el uso de una aplicación Blazor WebAssembly hospedada, donde la aplicación Server usa el SDK de Graph o Graph API para proporcionar datos de Graph a la aplicación Client a través de la API web. Para más información, consulte la sección Soluciones Blazor WebAssembly hospedadas de este artículo.

Los ejemplos de este artículo aprovechan las nuevas características de .NET/C#. Al usar los ejemplos con .NET 7 o versiones anteriores, se requieren modificaciones menores. Sin embargo, los ejemplos de texto y código relacionados con la interacción con Microsoft Graph son los mismos para todas las versiones de ASP.NET Core.

Las instrucciones siguientes se aplican a Microsoft Graph v5.

El SDK de Microsoft Graph para su uso en aplicaciones Blazor se denomina Biblioteca cliente de .NET de Microsoft Graph.

Los ejemplos del SDK de Graph necesitan las siguientes referencias de paquete en la aplicación Blazor WebAssembly independiente. Ya se hace referencia a los dos primeros paquetes si la aplicación se ha habilitado para la autenticación MSAL, por ejemplo, al crear la aplicación siguiendo las instrucciones de Protección de una aplicación ASP.NET CoreBlazor WebAssembly independiente con Microsoft Entra ID.

Los ejemplos del SDK de Graph requieren las siguientes referencias de paquete en la aplicación Blazor WebAssembly independiente o en la aplicación Client de una solución Blazor WebAssembly hospedada. Ya se hace referencia a los dos primeros paquetes si la aplicación se ha habilitado para la autenticación MSAL, por ejemplo, al crear la aplicación siguiendo las instrucciones de Protección de una aplicación ASP.NET CoreBlazor WebAssembly independiente con Microsoft Entra ID.

Nota:

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos de Instalación y administración de paquetes en Flujo de trabajo de consumo de paquetes (NuGet documentación). Confirme las versiones correctas del paquete en NuGet.org.

En Azure Portal, conceda permisos delegados (ámbitos)† para los datos de Microsoft Graph a los que la aplicación debe poder acceder en nombre de un usuario. En el ejemplo de este artículo, el registro de la aplicación debe incluir un permiso delegado para leer datos de usuario (ámbito Microsoft.Graph>User.Read en Permisos de API, Tipo: Delegado). El ámbito User.Read permite a los usuarios iniciar sesión en la aplicación y, a la aplicación, leer el perfil y la información empresarial de los usuarios que han iniciado sesión. Para obtener más información, consulte Información general sobre los permisos y el consentimiento en la Plataforma de identidad de Microsoft e Información general sobre los permisos de Microsoft Graph.

Nota:

Permisos y ámbitos significan lo mismo y se usan indistintamente en la documentación de seguridad y Azure Portal. En este artículo se usa ámbito/ámbitos al hacer referencia a los permisos de Graph API.

Después de agregar los ámbitos de Microsoft Graph API al registro de la aplicación en Azure Portal, agregue la siguiente configuración de la aplicación al archivo wwwroot/appsettings.json de la aplicación, que incluye la dirección URL base de Graph, con la versión de Microsoft Graph y los ámbitos. En el siguiente ejemplo se especifica el ámbito User.Read para los ejemplos de secciones posteriores de este artículo. Los ámbitos no distinguen mayúsculas de minúsculas.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com/{VERSION}/",
  "Scopes": [
    "user.read"
  ]
}

En el ejemplo anterior, el marcador de posición {VERSION} es la versión de Microsoft Graph API (por ejemplo: v1.0). Se requiere la barra diagonal.

A continuación, se muestra un ejemplo de un archivo de configuración wwwroot/appsettings.json completo para una aplicación que usa ME-ID como proveedor de identidades, donde se especifica la lectura de datos de usuario (ámbito user.read) para Microsoft Graph:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0/",
    "Scopes": [
      "user.read"
    ]
  }
}

En el ejemplo anterior, el marcador de posición {TENANT ID} es el identificador de directorio (inquilino) y el marcador de posición {CLIENT ID} es el identificador de aplicación (cliente). Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

Agregue la siguiente clase GraphClientExtensions a la aplicación independiente. Los ámbitos se proporcionan a la propiedad Scopes de AccessTokenRequestOptions en el método AuthenticateRequestAsync.

Agregue la siguiente clase GraphClientExtensions a la aplicación independiente o la aplicación Client de una solución de Blazor WebAssembly hospedada. Los ámbitos se proporcionan a la propiedad Scopes de AccessTokenRequestOptions en el método AuthenticateRequestAsync.

Cuando no se obtiene un token de acceso, el código siguiente no establece un encabezado de autorización de portador para las solicitudes de Graph.

GraphClientExtensions.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal.Models;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;
using IAccessTokenProvider = 
    Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider;

namespace BlazorSample;

internal static class GraphClientExtensions
{
    public static IServiceCollection AddGraphClient(
            this IServiceCollection services, string? baseUrl, List<string>? scopes)
    {
        if (string.IsNullOrEmpty(baseUrl) || scopes?.Count == 0)
        {
            return services;
        }

        services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
            options =>
            {
                scopes?.ForEach((scope) =>
                {
                    options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
                });
            });

        services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>();

        services.AddScoped(sp =>
        {
            return new GraphServiceClient(
                new HttpClient(),
                sp.GetRequiredService<IAuthenticationProvider>(),
                baseUrl);
        });

        return services;
    }

    private class GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, 
        IConfiguration config) : IAuthenticationProvider
    {
        private readonly IConfiguration config = config;

        public IAccessTokenProvider TokenProvider { get; } = tokenProvider;

        public async Task AuthenticateRequestAsync(RequestInformation request, 
            Dictionary<string, object>? additionalAuthenticationContext = null, 
            CancellationToken cancellationToken = default)
        {
            var result = await TokenProvider.RequestAccessToken(
                new AccessTokenRequestOptions()
                {
                    Scopes = 
                        config.GetSection("MicrosoftGraph:Scopes").Get<string[]>()
                });

            if (result.TryGetToken(out var token))
            {
                request.Headers.Add("Authorization", 
                    $"{CoreConstants.Headers.Bearer} {token.Value}");
            }
        }
    }
}

Importante

Consulte la sección DefaultAccessTokenScopes frente a AdditionalScopesToConsent para obtener una explicación sobre por qué el código anterior usa DefaultAccessTokenScopes para agregar los ámbitos en lugar de AdditionalScopesToConsent.

En el archivo Program, agregue los servicios y la configuración del cliente de Graph con el método de extensión AddGraphClient:

var baseUrl = builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>();

builder.Services.AddGraphClient(baseUrl, scopes);

Llamada a Graph API desde un componente mediante el SDK de Graph

El siguiente componente UserData usa un GraphServiceClient insertado para obtener los datos de perfil de ME-ID del usuario y mostrar su número de teléfono móvil.

Para cualquier usuario de prueba que cree en ME-ID, asegúrese de proporcionar al perfil de ME-ID del usuario un número de teléfono móvil en Azure Portal.

UserData.razor:

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Graph
@attribute [Authorize]
@inject GraphServiceClient Client

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(user?.MobilePhone))
{
    <p>Mobile Phone: @user.MobilePhone</p>
}

@code {
    private Microsoft.Graph.Models.User? user;

    protected override async Task OnInitializedAsync()
    {
        user = await Client.Me.GetAsync();
    }
}

Agregue un vínculo a la página del componente en el componente NavMenu (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Sugerencia

Para agregar usuarios a una aplicación, consulte la sección Asignación de usuarios a un registro de aplicaciones con o sin roles de aplicación.

Al realizar pruebas con el SDK de Graph de forma local, se recomienda utilizar una nueva sesión del explorador InPrivate/incógnito para cada prueba a fin de evitar que las cookies persistentes interfieran en las pruebas. Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

Personalización de notificaciones de usuario con el SDK de Graph

En el siguiente ejemplo, la aplicación crea notificaciones de número de teléfono móvil y ubicación de oficina para un usuario a partir de los datos de su perfil de usuario de ME-ID. La aplicación debe tener el ámbito de Graph API User.Read configurado en ME-ID. Cualquier usuario de prueba para este escenario debe tener un número de teléfono móvil y una ubicación de oficina en su perfil de ME-ID, que se puede agregar a través de Azure Portal.

En la siguiente fábrica de cuentas de usuario personalizada:

  • Se incluye ILogger (logger) para mayor comodidad en caso de que desee registrar información o errores en el método CreateUserAsync.
  • En caso de que se produzca una excepción AccessTokenNotAvailableException, se redirige al usuario al proveedor de identidades para iniciar sesión en su cuenta. Se pueden realizar acciones adicionales o diferentes cuando se produce un error al solicitar un token de acceso. Por ejemplo, la aplicación puede registrar AccessTokenNotAvailableException y crear una incidencia de soporte técnico para una investigación más detallada.
  • El RemoteUserAccount del marco representa la cuenta del usuario. Si la aplicación requiere una clase de cuenta de usuario personalizada que extienda RemoteUserAccount, intercambie la clase de cuenta de usuario personalizada con RemoteUserAccount en el código siguiente.

CustomAccountFactory.cs:

using System.Security.Claims;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions.Authentication;

namespace BlazorSample;

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor,
        IServiceProvider serviceProvider, ILogger<CustomAccountFactory> logger,
        IConfiguration config) 
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IServiceProvider serviceProvider = serviceProvider;
    private readonly string? baseUrl = 
        config.GetSection("MicrosoftGraph")["BaseUrl"];

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var initialUser = await base.CreateUserAsync(account, options);

        if (initialUser.Identity is not null &&
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null && !string.IsNullOrEmpty(baseUrl))
            {
                try
                {
                    var client = new GraphServiceClient(
                        new HttpClient(),
                        serviceProvider
                            .GetRequiredService<IAuthenticationProvider>(),
                        baseUrl);

                    var user = await client.Me.GetAsync();

                    if (user is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            user.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            user.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

Configure la autenticación de MSAL para usar la fábrica de cuentas de usuario personalizada.

Confirme que el archivo Program usa el espacio de nombres Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

El ejemplo de esta sección se basa en el enfoque de leer la dirección URL base, la versión y los ámbitos de la configuración de la aplicación mediante la sección MicrosoftGraph del archivo wwwroot/appsettings.json. Las líneas siguientes ya deben estar presentes en el archivo Program de acuerdo a las instrucciones anteriores en este artículo:

var baseUrl = builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>();

builder.Services.AddGraphClient(baseUrl, scopes);

En el archivo Program, busque la llamada al método de extensión AddMsalAuthentication. Actualice el código a lo siguiente, que incluye una llamada a AddAccountClaimsPrincipalFactory que agrega una fábrica de entidades de seguridad de notificaciones de cuenta con CustomAccountFactory.

Si la aplicación usa una clase de cuenta de usuario personalizada que extiende RemoteUserAccount, intercambie la clase de cuenta de usuario personalizada para RemoteUserAccount en el código siguiente.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount,
        CustomAccountFactory>();

Puede usar el siguiente componente UserClaims para estudiar las notificaciones del usuario después de que el usuario se autentique con ME-ID:

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}
else
{
    <p>No claims found.</p>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Agregue un vínculo a la página del componente en el componente NavMenu (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Al realizar pruebas con el SDK de Graph de forma local, se recomienda utilizar una nueva sesión del explorador InPrivate/incógnito para cada prueba a fin de evitar que las cookies persistentes interfieran en las pruebas. Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

Las instrucciones siguientes se aplican a Microsoft Graph v4. Si va a actualizar una aplicación de SDK v4 a v5, consulte la guía de actualización y registro de cambios del SDK de .NET para Microsoft Graph v5.

El SDK de Microsoft Graph para su uso en aplicaciones Blazor se denomina Biblioteca cliente de .NET de Microsoft Graph.

Los ejemplos del SDK de Graph necesitan las siguientes referencias de paquete en la aplicación Blazor WebAssembly independiente. Ya se hace referencia a los dos primeros paquetes si la aplicación se ha habilitado para la autenticación MSAL, por ejemplo, al crear la aplicación siguiendo las instrucciones de Protección de una aplicación ASP.NET CoreBlazor WebAssembly independiente con Microsoft Entra ID.

Los ejemplos del SDK de Graph requieren las siguientes referencias de paquete en la aplicación Blazor WebAssembly independiente o en la aplicación Client de una solución Blazor WebAssembly hospedada. Ya se hace referencia a los dos primeros paquetes si la aplicación se ha habilitado para la autenticación MSAL, por ejemplo, al crear la aplicación siguiendo las instrucciones de Protección de una aplicación ASP.NET CoreBlazor WebAssembly independiente con Microsoft Entra ID.

Nota:

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos de Instalación y administración de paquetes en Flujo de trabajo de consumo de paquetes (NuGet documentación). Confirme las versiones correctas del paquete en NuGet.org.

En Azure Portal, conceda permisos delegados (ámbitos)† para los datos de Microsoft Graph a los que la aplicación debe poder acceder en nombre de un usuario. En el ejemplo de este artículo, el registro de la aplicación debe incluir un permiso delegado para leer datos de usuario (ámbito Microsoft.Graph>User.Read en Permisos de API, Tipo: Delegado). El ámbito User.Read permite a los usuarios iniciar sesión en la aplicación y, a la aplicación, leer el perfil y la información empresarial de los usuarios que han iniciado sesión. Para obtener más información, consulte Información general sobre los permisos y el consentimiento en la Plataforma de identidad de Microsoft e Información general sobre los permisos de Microsoft Graph.

Nota:

Permisos y ámbitos significan lo mismo y se usan indistintamente en la documentación de seguridad y Azure Portal. En este artículo se usa ámbito/ámbitos al hacer referencia a los permisos de Graph API.

Después de agregar los ámbitos de Microsoft Graph API al registro de la aplicación en Azure Portal, agregue la siguiente configuración de la aplicación al archivo wwwroot/appsettings.json de la aplicación, que incluye la dirección URL base de Graph, con la versión de Microsoft Graph y los ámbitos. En el siguiente ejemplo se especifica el ámbito User.Read para los ejemplos de secciones posteriores de este artículo. Los ámbitos no distinguen mayúsculas de minúsculas.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com/{VERSION}/",
  "Scopes": [
    "user.read"
  ]
}

En el ejemplo anterior, el marcador de posición {VERSION} es la versión de Microsoft Graph API (por ejemplo: v1.0). Se requiere la barra diagonal.

A continuación, se muestra un ejemplo de un archivo de configuración wwwroot/appsettings.json completo para una aplicación que usa ME-ID como proveedor de identidades, donde se especifica la lectura de datos de usuario (ámbito user.read) para Microsoft Graph:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0/",
    "Scopes": [
      "user.read"
    ]
  }
}

En el ejemplo anterior, el marcador de posición {TENANT ID} es el identificador de directorio (inquilino) y el marcador de posición {CLIENT ID} es el identificador de aplicación (cliente). Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

Agregue la siguiente clase GraphClientExtensions a la aplicación independiente. Los ámbitos se proporcionan a la propiedad Scopes de AccessTokenRequestOptions en el método AuthenticateRequestAsync. IHttpProvider.OverallTimeout se extiende desde el valor predeterminado de 100 segundos a 300 segundos para dar a HttpClient más tiempo para recibir una respuesta de Microsoft Graph.

Agregue la siguiente clase GraphClientExtensions a la aplicación independiente o la aplicación Client de una solución de Blazor WebAssembly hospedada. Los ámbitos se proporcionan a la propiedad Scopes de AccessTokenRequestOptions en el método AuthenticateRequestAsync. IHttpProvider.OverallTimeout se extiende desde el valor predeterminado de 100 segundos a 300 segundos para dar a HttpClient más tiempo para recibir una respuesta de Microsoft Graph.

Cuando no se obtiene un token de acceso, el código siguiente no establece un encabezado de autorización de portador para las solicitudes de Graph.

GraphClientExtensions.cs:

using System.Net.Http.Headers;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal.Models;
using Microsoft.Graph;

namespace BlazorSample;

internal static class GraphClientExtensions
{
    public static IServiceCollection AddGraphClient(
        this IServiceCollection services, string? baseUrl, List<string>? scopes)
    {
        if (string.IsNullOrEmpty(baseUrl) || scopes?.Count == 0)
        {
            return services;
        }

        services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
            options =>
            {
                scopes?.ForEach((scope) =>
                {
                    options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
                });
            });

        services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>();

        services.AddScoped<IHttpProvider, HttpClientHttpProvider>(sp =>
            new HttpClientHttpProvider(new HttpClient()));

        services.AddScoped(sp =>
        {
            return new GraphServiceClient(
                baseUrl,
                sp.GetRequiredService<IAuthenticationProvider>(),
                sp.GetRequiredService<IHttpProvider>());
        });

        return services;
    }

    private class GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, 
        IConfiguration config) : IAuthenticationProvider
    {
        private readonly IConfiguration config = config;

        public IAccessTokenProvider TokenProvider { get; } = tokenProvider;

        public async Task AuthenticateRequestAsync(HttpRequestMessage request)
        {
            var result = await TokenProvider.RequestAccessToken(
                new AccessTokenRequestOptions()
                { 
                    Scopes = config.GetSection("MicrosoftGraph:Scopes").Get<string[]>()
                });

            if (result.TryGetToken(out var token))
            {
                request.Headers.Authorization ??= new AuthenticationHeaderValue(
                    "Bearer", token.Value);
            }
        }
    }

    private class HttpClientHttpProvider(HttpClient client) : IHttpProvider
    {
        private readonly HttpClient client = client;

        public ISerializer Serializer { get; } = new Serializer();

        public TimeSpan OverallTimeout { get; set; } = TimeSpan.FromSeconds(300);

        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
        {
            return client.SendAsync(request);
        }

        public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
            HttpCompletionOption completionOption,
            CancellationToken cancellationToken)
        {
            return client.SendAsync(request, completionOption, cancellationToken);
        }

        public void Dispose()
        {
        }
    }
}

Importante

Consulte la sección DefaultAccessTokenScopes frente a AdditionalScopesToConsent para obtener una explicación sobre por qué el código anterior usa DefaultAccessTokenScopes para agregar los ámbitos en lugar de AdditionalScopesToConsent.

En el archivo Program, agregue los servicios y la configuración del cliente de Graph con el método de extensión AddGraphClient:

var baseUrl = builder.Configuration
    .GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>();

builder.Services.AddGraphClient(baseUrl, scopes);

Llamada a Graph API desde un componente mediante el SDK de Graph

El siguiente componente UserData usa un GraphServiceClient insertado para obtener los datos de perfil de ME-ID del usuario y mostrar su número de teléfono móvil. Para cualquier usuario de prueba que cree en ME-ID, asegúrese de proporcionar al perfil de ME-ID del usuario un número de teléfono móvil en Azure Portal.

UserData.razor:

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.Graph
@attribute [Authorize]
@inject GraphServiceClient Client

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(user?.MobilePhone))
{
    <p>Mobile Phone: @user.MobilePhone</p>
}

@code {
    private Microsoft.Graph.User? user;

    protected override async Task OnInitializedAsync()
    {
        var request = Client.Me.Request();
        user = await request.GetAsync();
    }
}

Agregue un vínculo a la página del componente en el componente NavMenu (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Sugerencia

Para agregar usuarios a una aplicación, consulte la sección Asignación de usuarios a un registro de aplicaciones con o sin roles de aplicación.

Al realizar pruebas con el SDK de Graph de forma local, se recomienda utilizar una nueva sesión del explorador InPrivate/incógnito para cada prueba a fin de evitar que las cookies persistentes interfieran en las pruebas. Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

Personalización de notificaciones de usuario con el SDK de Graph

En el siguiente ejemplo, la aplicación crea notificaciones de número de teléfono móvil y ubicación de oficina para un usuario a partir de los datos de su perfil de usuario de ME-ID. La aplicación debe tener el ámbito de Graph API User.Read configurado en ME-ID. Cualquier usuario de prueba para este escenario debe tener un número de teléfono móvil y una ubicación de oficina en su perfil de ME-ID, que se puede agregar a través de Azure Portal.

En la siguiente fábrica de cuentas de usuario personalizada:

  • Se incluye ILogger (logger) para mayor comodidad en caso de que desee registrar información o errores en el método CreateUserAsync.
  • En caso de que se produzca una excepción AccessTokenNotAvailableException, se redirige al usuario al proveedor de identidades para iniciar sesión en su cuenta. Se pueden realizar acciones adicionales o diferentes cuando se produce un error al solicitar un token de acceso. Por ejemplo, la aplicación puede registrar AccessTokenNotAvailableException y crear una incidencia de soporte técnico para una investigación más detallada.
  • El RemoteUserAccount del marco representa la cuenta del usuario. Si la aplicación requiere una clase de cuenta de usuario personalizada que extienda RemoteUserAccount, intercambie la clase de cuenta de usuario personalizada con RemoteUserAccount en el código siguiente.

CustomAccountFactory.cs:

using System.Security.Claims;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using Microsoft.Graph;

namespace BlazorSample;

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor, 
        IServiceProvider serviceProvider, ILogger<CustomAccountFactory> logger)
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IServiceProvider serviceProvider = serviceProvider;

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var initialUser = await base.CreateUserAsync(account, options);

        if (initialUser.Identity is not null && 
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null)
            {
                try
                {
                    var client = ActivatorUtilities
                        .CreateInstance<GraphServiceClient>(serviceProvider);
                    var request = client.Me.Request();
                    var user = await request.GetAsync();

                    if (user is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            user.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            user.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

Configure la autenticación de MSAL para usar la fábrica de cuentas de usuario personalizada.

Confirme que el archivo Program usa el espacio de nombres Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

El ejemplo de esta sección se basa en el enfoque de leer la dirección URL base, la versión y los ámbitos de la configuración de la aplicación mediante la sección MicrosoftGraph del archivo wwwroot/appsettings.json. Las líneas siguientes ya deben estar presentes en el archivo Program de acuerdo a las instrucciones anteriores en este artículo:

var baseUrl = string.Join("/", 
    builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
    .Get<List<string>>();

builder.Services.AddGraphClient(baseUrl, scopes);

En el archivo Program, busque la llamada al método de extensión AddMsalAuthentication. Actualice el código a lo siguiente, que incluye una llamada a AddAccountClaimsPrincipalFactory que agrega una fábrica de entidades de seguridad de notificaciones de cuenta con CustomAccountFactory.

Si la aplicación usa una clase de cuenta de usuario personalizada que extiende RemoteUserAccount, intercambie la clase de cuenta de usuario personalizada para RemoteUserAccount en el código siguiente.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount,
        CustomAccountFactory>();

Puede usar el siguiente componente UserClaims para estudiar las notificaciones del usuario después de que el usuario se autentique con ME-ID:

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}
else
{
    <p>No claims found.</p>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Agregue un vínculo a la página del componente en el componente NavMenu (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Al realizar pruebas con el SDK de Graph de forma local, se recomienda utilizar una nueva sesión del explorador InPrivate/incógnito para cada prueba a fin de evitar que las cookies persistentes interfieran en las pruebas. Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

En los ejemplos siguientes se usa un HttpClient con nombre para las llamadas de Graph API para obtener el número de teléfono móvil de un usuario para procesar una llamada o para personalizar las notificaciones de un usuario para incluir una notificación de número de teléfono móvil y una notificación de ubicación de la oficina.

En los ejemplos se necesita una referencia de paquete de Microsoft.Extensions.Http para la aplicación Blazor WebAssembly independiente.

Los ejemplos requieren una referencia de paquete para Microsoft.Extensions.Http para la aplicación Blazor WebAssembly independiente o la aplicación Client de una solución Blazor WebAssembly hospedada.

Nota

Para obtener instrucciones sobre cómo agregar paquetes a aplicaciones .NET, consulte los artículos de Instalación y administración de paquetes en Flujo de trabajo de consumo de paquetes (NuGet documentación). Confirme las versiones correctas del paquete en NuGet.org.

En Azure Portal, conceda permisos delegados (ámbitos)† para los datos de Microsoft Graph a los que la aplicación debe poder acceder en nombre de un usuario. En el ejemplo de este artículo, el registro de la aplicación debe incluir un permiso delegado para leer datos de usuario (ámbito Microsoft.Graph>User.Read en Permisos de API, Tipo: Delegado). El ámbito User.Read permite a los usuarios iniciar sesión en la aplicación y, a la aplicación, leer el perfil y la información empresarial de los usuarios que han iniciado sesión. Para obtener más información, consulte Información general sobre los permisos y el consentimiento en la Plataforma de identidad de Microsoft e Información general sobre los permisos de Microsoft Graph.

Nota:

Permisos y ámbitos significan lo mismo y se usan indistintamente en la documentación de seguridad y Azure Portal. En este artículo se usa ámbito/ámbitos al hacer referencia a los permisos de Graph API.

Después de agregar los ámbitos de Microsoft Graph API al registro de la aplicación en Azure Portal, agregue la siguiente configuración de la aplicación al archivo wwwroot/appsettings.json de la aplicación, que incluye la dirección URL base de Graph, con la versión de Microsoft Graph y los ámbitos. En el siguiente ejemplo se especifica el ámbito User.Read para los ejemplos de secciones posteriores de este artículo. Los ámbitos no distinguen mayúsculas de minúsculas.

"MicrosoftGraph": {
  "BaseUrl": "https://graph.microsoft.com/{VERSION}/",
  "Scopes": [
    "user.read"
  ]
}

En el ejemplo anterior, el marcador de posición {VERSION} es la versión de Microsoft Graph API (por ejemplo: v1.0). Se requiere la barra diagonal.

A continuación, se muestra un ejemplo de un archivo de configuración wwwroot/appsettings.json completo para una aplicación que usa ME-ID como proveedor de identidades, donde se especifica la lectura de datos de usuario (ámbito user.read) para Microsoft Graph:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/{TENANT ID}",
    "ClientId": "{CLIENT ID}",
    "ValidateAuthority": true
  },
  "MicrosoftGraph": {
    "BaseUrl": "https://graph.microsoft.com/v1.0/",
    "Scopes": [
      "user.read"
    ]
  }
}

En el ejemplo anterior, el marcador de posición {TENANT ID} es el identificador de directorio (inquilino) y el marcador de posición {CLIENT ID} es el identificador de aplicación (cliente). Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

Cree la siguiente configuración de clase y proyecto GraphAuthorizationMessageHandler en el archivo Program para trabajar con Graph API. La dirección URL base y los ámbitos se proporcionan al controlador desde la configuración.

GraphAuthorizationMessageHandler.cs:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

namespace BlazorSample;

public class GraphAuthorizationMessageHandler : AuthorizationMessageHandler
{
    public GraphAuthorizationMessageHandler(IAccessTokenProvider provider,
        NavigationManager navigation, IConfiguration config)
        : base(provider, navigation)
    {
        ConfigureHandler(
            authorizedUrls: [ config.GetSection("MicrosoftGraph")["BaseUrl"] ?? 
                string.Empty ],
            scopes: config.GetSection("MicrosoftGraph:Scopes").Get<List<string>>());
    }
}

En el archivo Program, configure la instancia de HttpClient con nombre para Graph API:

builder.Services.AddTransient<GraphAuthorizationMessageHandler>();

builder.Services.AddHttpClient("GraphAPI",
        client => client.BaseAddress = new Uri(
            builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ?? 
                string.Empty))
    .AddHttpMessageHandler<GraphAuthorizationMessageHandler>();

En el ejemplo anterior, el GraphAuthorizationMessageHandlerDelegatingHandler se registra como un servicio transitorio para AddHttpMessageHandler. Se recomienda el registro transitorio para IHttpClientFactory, que administra sus propios ámbitos de inserción de dependencias. Para obtener más información, consulte los siguientes recursos:

Llamada a Graph API desde un componente mediante HttpClient con nombre

La clase UserInfo.cs designa las propiedades de perfil de usuario necesarias con el atributo JsonPropertyNameAttribute y el nombre JSON que usa ME-ID. En el ejemplo siguiente se configuran propiedades para el número de teléfono móvil y la ubicación de la oficina del usuario.

UserInfo.cs:

using System.Text.Json.Serialization;

namespace BlazorSample;

public class UserInfo
{
    [JsonPropertyName("mobilePhone")]
    public string? MobilePhone { get; set; }

    [JsonPropertyName("officeLocation")]
    public string? OfficeLocation { get; set; }
}

En el componente UserData siguiente, se crea un objeto HttpClient para que Graph API emita una solicitud para los datos de perfil del usuario. El recurso me (me) se agrega a la dirección URL base con la versión de la solicitud de Graph API. Los datos JSON devueltos por Graph se deserializan en las propiedades de clase UserInfo. En el ejemplo siguiente, se obtiene el número de teléfono móvil. Puede agregar un código similar para incluir la ubicación de la oficina del perfil de ME-ID del usuario si lo desea (userInfo.OfficeLocation). Si se produce un error en la solicitud de token de acceso, se redirige al usuario para que inicie sesión en la aplicación para un nuevo token de acceso.

UserData.razor:

@page "/user-data"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@attribute [Authorize]
@inject IConfiguration Config
@inject IHttpClientFactory ClientFactory

<PageTitle>User Data</PageTitle>

<h1>Microsoft Graph User Data</h1>

@if (!string.IsNullOrEmpty(userInfo?.MobilePhone))
{
    <p>Mobile Phone: @userInfo.MobilePhone</p>
}

@code {
    private UserInfo? userInfo;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            var client = ClientFactory.CreateClient("GraphAPI");

            userInfo = await client.GetFromJsonAsync<UserInfo>("me");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    }
}

Agregue un vínculo a la página del componente en el componente NavMenu (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-data">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Data
    </NavLink>
</div>

Sugerencia

Para agregar usuarios a una aplicación, consulte la sección Asignación de usuarios a un registro de aplicaciones con o sin roles de aplicación.

En la secuencia siguiente se describe el nuevo flujo de usuario para ámbitos de Graph API:

  1. El nuevo usuario inicia sesión en la aplicación por primera vez.
  2. El usuario da su consentimiento para usar la aplicación en la interfaz de usuario de consentimiento de Azure.
  3. El usuario accede a una página de componentes que solicita datos de Graph API por primera vez.
  4. Se redirige al usuario a la interfaz de usuario de consentimiento de Azure para dar su consentimiento a los ámbitos de Graph API.
  5. Se devuelven los datos del usuario de Graph API.

Si prefiere que el aprovisionamiento de ámbitos (consentimiento para ámbitos de Graph API) tenga lugar al iniciar sesión por primera vez, proporcione los ámbitos a la autenticación MSAL como ámbitos de tokens de acceso predeterminados en el archivo Program:

+ var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes")
+     .Get<List<string>>() ?? [];

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);

+   foreach (var scope in scopes)
+   {
+       options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
+   }
});

Importante

Consulte la sección DefaultAccessTokenScopes frente a AdditionalScopesToConsent para obtener una explicación sobre por qué el código anterior usa DefaultAccessTokenScopes para agregar los ámbitos en lugar de AdditionalScopesToConsent.

Cuando se realizan los cambios anteriores en la aplicación, el flujo de usuario adopta la siguiente secuencia:

  1. El nuevo usuario inicia sesión en la aplicación por primera vez.
  2. El usuario da su consentimiento para usar los ámbitos de Graph API y la aplicación en la interfaz de usuario de consentimiento de Azure.
  3. El usuario accede a una página de componentes que solicita datos de Graph API por primera vez.
  4. Se devuelven los datos del usuario de Graph API.

Al realizar pruebas con Graph API de forma local, se recomienda utilizar una nueva sesión de explorador InPrivate/incógnito para cada prueba a fin de evitar que las cookies persistentes interfieran con las pruebas. Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

Personalización de notificaciones de usuario mediante HttpClient con nombre

En el siguiente ejemplo, la aplicación crea notificaciones de número de teléfono móvil y ubicación de oficina para un usuario a partir de los datos de su perfil de usuario de ME-ID. La aplicación debe tener el ámbito de Graph API User.Read configurado en ME-ID. Las cuentas de usuario de prueba de ME-ID requieren una entrada para el número de teléfono móvil y la ubicación de la oficina, que se pueden agregar a través de Azure Portal a sus perfiles de usuario.

Si aún no ha agregado la clase UserInfo a la aplicación siguiendo las instrucciones anteriores de este artículo, agregue la siguiente clase y designe las propiedades de perfil de usuario necesarias con el atributo JsonPropertyNameAttribute y el nombre JSON usado por ME-ID. En el ejemplo siguiente se configuran propiedades para el número de teléfono móvil y la ubicación de la oficina del usuario.

UserInfo.cs:

using System.Text.Json.Serialization;

namespace BlazorSample;

public class UserInfo
{
    [JsonPropertyName("mobilePhone")]
    public string? MobilePhone { get; set; }

    [JsonPropertyName("officeLocation")]
    public string? OfficeLocation { get; set; }
}

En la siguiente fábrica de cuentas de usuario personalizada:

  • Se incluye ILogger (logger) para mayor comodidad en caso de que desee registrar información o errores en el método CreateUserAsync.
  • En caso de que se produzca una excepción AccessTokenNotAvailableException, se redirige al usuario al proveedor de identidades para iniciar sesión en su cuenta. Se pueden realizar acciones adicionales o diferentes cuando se produce un error al solicitar un token de acceso. Por ejemplo, la aplicación puede registrar AccessTokenNotAvailableException y crear una incidencia de soporte técnico para una investigación más detallada.
  • El RemoteUserAccount del marco representa la cuenta del usuario. Si la aplicación requiere una clase de cuenta de usuario personalizada que extienda RemoteUserAccount, intercambie la clase de cuenta de usuario personalizada con RemoteUserAccount en el código siguiente.

CustomAccountFactory.cs:

using System.Net.Http.Json;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

namespace BlazorSample;

public class CustomAccountFactory(IAccessTokenProviderAccessor accessor,
        IHttpClientFactory clientFactory,
        ILogger<CustomAccountFactory> logger)
    : AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
    private readonly ILogger<CustomAccountFactory> logger = logger;
    private readonly IHttpClientFactory clientFactory = clientFactory;

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var initialUser = await base.CreateUserAsync(account, options);

        if (initialUser.Identity is not null && 
            initialUser.Identity.IsAuthenticated)
        {
            var userIdentity = initialUser.Identity as ClaimsIdentity;

            if (userIdentity is not null)
            {
                try
                {
                    var client = clientFactory.CreateClient("GraphAPI");

                    var userInfo = await client.GetFromJsonAsync<UserInfo>("me");

                    if (userInfo is not null)
                    {
                        userIdentity.AddClaim(new Claim("mobilephone",
                            userInfo.MobilePhone ?? "(000) 000-0000"));
                        userIdentity.AddClaim(new Claim("officelocation",
                            userInfo.OfficeLocation ?? "Not set"));
                    }
                }
                catch (AccessTokenNotAvailableException exception)
                {
                    exception.Redirect();
                }
            }
        }

        return initialUser;
    }
}

La autenticación de MSAL está configurada para usar la fábrica de cuentas de usuario personalizada. Para empezar, confirme que el archivo Program usa el espacio de nombres Microsoft.AspNetCore.Components.WebAssembly.Authentication:

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

En el archivo Program, busque la llamada al método de extensión AddMsalAuthentication. Actualice el código a lo siguiente, que incluye una llamada a AddAccountClaimsPrincipalFactory que agrega una fábrica de entidades de seguridad de notificaciones de cuenta con CustomAccountFactory.

Si la aplicación usa una clase de cuenta de usuario personalizada que extiende RemoteUserAccount, intercambie la clase de cuenta de usuario personalizada de la aplicación para RemoteUserAccount en el código siguiente.

builder.Services.AddMsalAuthentication<RemoteAuthenticationState, 
    RemoteUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", 
            options.ProviderOptions.Authentication);
    })
    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, RemoteUserAccount, 
        CustomAccountFactory>();

El ejemplo anterior corresponde a una aplicación que usa autenticación de ME-ID con MSAL. Existen patrones similares para la autenticación de API y OIDC. Para obtener más información, consulte los ejemplos de la sección Personalización del usuario con una notificación de carga del artículo Escenarios de seguridad adicionales de ASP.NET Core Blazor WebAssembly.

Puede usar el siguiente componente UserClaims para estudiar las notificaciones del usuario después de que el usuario se autentique con ME-ID:

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject AuthenticationStateProvider AuthenticationStateProvider

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li>@claim.Type: @claim.Value</li>
        }
    </ul>
}
else
{
    <p>No claims found.</p>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthenticationStateProvider
            .GetAuthenticationStateAsync();
        var user = authState.User;

        claims = user.Claims;
    }
}

Agregue un vínculo a la página del componente en el componente NavMenu (Layout/NavMenu.razor):

<div class="nav-item px-3">
    <NavLink class="nav-link" href="user-claims">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User Claims
    </NavLink>
</div>

Al realizar pruebas con Graph API de forma local, se recomienda utilizar una nueva sesión de explorador InPrivate/incógnito para cada prueba a fin de evitar que las cookies persistentes interfieran con las pruebas. Para más información, consulte Protección de una aplicación ASP.NET Core Blazor WebAssembly independiente con Cuentas de Microsoft.

Asignación de usuarios a un registro de aplicaciones con o sin roles de aplicación

Puede agregar usuarios a un registro de aplicaciones y asignar roles a estos mediante los pasos siguientes en Azure Portal.

Para agregar un usuario, seleccione Usuarios en el área ME-ID de Azure Portal:

  1. Seleccione Nuevo usuario>Crear nuevo usuario.
  2. Use la plantilla Crear usuario.
  3. Proporcione la información del usuario en el área Identity.
  4. Puede generar o asignar una contraseña inicial que el usuario cambiará al iniciar sesión por primera vez. Si usa la contraseña generada por el portal, anótela ahora.
  5. Seleccione Crear para crear el usuario. Cuando se cierre la interfaz Crear un usuario nuevo, seleccione Actualizar para actualizar la lista de usuarios y mostrar el usuario nuevo.
  6. En los ejemplos de este artículo, asigne un número de teléfono móvil al nuevo usuario; para ello, seleccione su nombre en la lista de usuarios, elija Propiedades y edite la información de contacto para proporcionar un número de teléfono móvil.

Para asignar usuarios a la aplicación sin roles de aplicación:

  1. En el área ME-ID de Azure Portal, abra Aplicaciones empresariales.
  2. Seleccione la aplicación en la lista.
  3. Selecciona Usuarios y grupos
  4. Seleccione Agregar usuario o grupo.
  5. Seleccione un usuario.
  6. Seleccione el botón Asignar.

Para asignar usuarios a la aplicación con roles de aplicación:

  1. Agregue roles al registro de la aplicación en Azure Portal de acuerdo con las instrucciones de ASP.NET Core Blazor WebAssembly con grupos y roles de Microsoft Entra ID.
  2. En el área ME-ID de Azure Portal, abra Aplicaciones empresariales.
  3. Seleccione la aplicación en la lista.
  4. Selecciona Usuarios y grupos
  5. Seleccione Agregar usuario o grupo.
  6. Seleccione un usuario y su rol para acceder a la aplicación. Para asignar varios roles a un usuario, se repite el proceso de agregar el usuario a la aplicación hasta que se hayan asignado todos los roles para este. Los usuarios con varios roles se enumeran una vez por cada rol asignado en la lista Usuarios y grupos de usuarios de la aplicación.
  7. Seleccione el botón Asignar.

Diferencias entre DefaultAccessTokenScopes y AdditionalScopesToConsent

Los ejemplos de este artículo aprovisionan ámbitos de Graph API con DefaultAccessTokenScopes, no AdditionalScopesToConsent.

AdditionalScopesToConsent no se usa porque no puede aprovisionar ámbitos de Graph API para los usuarios cuando inician sesión en la aplicación por primera vez con MSAL a través de la interfaz de usuario de consentimiento de Azure. Cuando el usuario intenta acceder a Graph API por primera vez con el SDK de Graph, se encuentra con una excepción:

Microsoft.Graph.Models.ODataErrors.ODataError: Access token is empty.

Después de que un usuario aprovisione los ámbitos de Graph API proporcionados a través de DefaultAccessTokenScopes, la aplicación puede usar AdditionalScopesToConsent para un inicio de sesión de usuario posterior. Sin embargo, cambiar el código de aplicación no tiene sentido para una aplicación de producción que requiera la adición periódica de nuevos usuarios con ámbitos de Graph delegados o la adición de nuevos ámbitos delegados de Graph API a la aplicación.

La explicación anterior sobre cómo aprovisionar ámbitos para el acceso a Graph API cuando el usuario inicie sesión en la aplicación por primera vez solo es válida para:

  • Aplicaciones que adoptan el SDK de Graph.
  • Aplicaciones que usan un HttpClient con nombre para el acceso de Graph API que pide a los usuarios su consentimiento de los ámbitos de Graph al iniciar sesión por primera vez en la aplicación.

Cuando se usa un HttpClient con nombre que no pide a los usuarios su consentimiento de los ámbitos de Graph en su primer inicio de sesión, se redirige a los usuarios a la interfaz de usuario de consentimiento de Azure para el consentimiento de los ámbitos de Graph API cuando soliciten acceso a Graph API por primera vez a través del DelegatingHandler del HttpClient preconfigurado con nombre. Cuando los ámbitos de Graph no se consientan inicialmente con el enfoque de HttpClient con nombre, la aplicación no llama a DefaultAccessTokenScopes ni a AdditionalScopesToConsent. Para obtener más información, consulte el apartado sobre con nombre HttpClient en este artículo.

Soluciones Blazor WebAssembly hospedadas

Los ejemplos de este artículo se refieren al uso del SDK de Graph o un HttpClient con nombre con Graph API directamente desde una aplicación Blazor WebAssembly independiente o directamente desde la aplicación Client de una Blazor WebAssemblysolución hospedada. Un escenario adicional que no se trata en este artículo es que una aplicación Client de una solución hospedada llame a la aplicación Server de la solución a través de la API web y, a continuación, la aplicación Server use el SDK de Graph o Graph API para llamar a Microsoft Graph y devolver datos a la aplicación Client. Aunque se trata de un enfoque compatible, no se trata en este artículo. Si desea adoptar este enfoque:

  • Siga las instrucciones de Llamada a una API web desde una aplicación de ASP.NET Core Blazor para los aspectos de la API web sobre la emisión de solicitudes a la aplicación Server desde la aplicación Client y la devolución de datos a la aplicación Client.
  • Siga las instrucciones de la documentación principal de Microsoft Graph para usar el SDK de Graph con una aplicación de ASP.NET Core típica, que en este escenario es la aplicación Server de la solución. Si usa la plantilla de proyecto Blazor WebAssembly para crear la solución Blazor WebAssembly hospedada (ASP.NET Core hospedada/-h|--hosted) con autorización organizativa (organización única/SingleOrg o varias organizaciones/MultiOrg) y la opción Microsoft Graph (Plataforma de identidad de Microsoft>Servicios conectados>Agregar permisos de Microsoft Graph en Visual Studio o la opción --calls-graph con el comando de la CLI dotnet new de .NET), la aplicación Server de la solución se configura para usar el SDK de Graph cuando se crea la solución a partir de la plantilla de proyecto.

Recursos adicionales

Instrucciones generales

  • Documentación de Microsoft Graph
  • Aplicación de ejemplo de Microsoft GraphBlazor WebAssembly: en este ejemplo se muestra cómo usar el SDK de .NET de Microsoft Graph para acceder a datos de Office 365 desde aplicaciones Blazor WebAssembly.
  • Tutorial de compilación de aplicaciones .NET con Microsoft Graph y Aplicación de ASP.NET Core de ejemplo de Microsoft Graph: estos recursos son más adecuados para las soluciones hospedadasBlazor WebAssembly, donde la aplicación Server está configurada para acceder a Microsoft Graph como una aplicación ASP.NET Core típica en nombre de la aplicación Client. La aplicación Client usa la API web para realizar solicitudes a la aplicación Server para los datos de Graph. Aunque estos recursos no se aplican directamente a la llamada a Graph desde aplicaciones del lado clienteBlazor WebAssembly, la configuración de la aplicación de ME-ID y los procedimientos de codificación de Microsoft Graph en los recursos vinculados son pertinentes para las aplicaciones Blazor WebAssembly independientes y deben consultarse para ejecutar procedimientos recomendados generales.

Guía de seguridad