Tutorial: Inicio de sesión de usuarios y llamada a Microsoft Graph en la aplicación de escritorio de Windows Presentation Foundation (WPF)

En este tutorial, creará una aplicación nativa de .NET para escritorio de Windows (XAML) que inicia la sesión de usuario y obtiene un token de acceso para llamar a Microsoft Graph API.

Cuando haya completado la guía, la aplicación podrá llamar a una API protegida que usa cuentas personales (lo que incluye outlook.com, live.com y otras). La aplicación también usa cuentas profesionales y educativas de cualquier empresa u organización que utilice Microsoft Entra ID.

En este tutorial, aprenderá a:

  • Crear un proyecto de Windows Presentation Foundation (WPF) en Visual Studio
  • Instalar la biblioteca de autenticación de Microsoft (MSAL) para .NET
  • Registro de la aplicación
  • Agregar código para admitir el inicio y el cierre de sesión de usuario
  • Agregar código para llamar a Microsoft Graph API
  • Prueba de la aplicación

Requisitos previos

Funcionamiento de la aplicación de ejemplo generada por esta guía

Screenshot of how the sample app generated by this tutorial works.

La aplicación de ejemplo que se crea con esta guía permite que una aplicación de escritorio de Windows consulte Microsoft Graph API o una API web que acepte tokens de un punto de conexión de la Plataforma de identidad de Microsoft. En este escenario, agregará un token a las solicitudes HTTP mediante el encabezado de autorización. La biblioteca de autenticación de Microsoft (MSAL) administra la adquisición y renovación de los tokens.

Tratamiento de la adquisición de tokens para acceder a API web protegidas

Una vez que el usuario se autentica, la aplicación de ejemplo recibe un token que se puede usar para consultar Microsoft Graph API o a una API web protegida mediante la plataforma de identidad de Microsoft.

Las API, como Microsoft Graph, requieren un token para permitir el acceso a recursos específicos. Por ejemplo, un token es necesario para leer un perfil del usuario, acceder al calendario de un usuario o enviar correo electrónico. La aplicación puede solicitar un token de acceso mediante MSAL para acceder a estos recursos mediante la especificación de ámbitos de API. Este token de acceso se agrega luego al encabezado de autorización de HTTP para todas las llamadas efectuadas al recurso protegido.

MSAL administra el almacenamiento en caché y la actualización de los tokens de acceso automáticamente, así que no tiene que preocuparse por ello.

Paquetes NuGet

Esta guía utiliza los siguientes paquetes NuGet:

Biblioteca Descripción
Microsoft.Identity.Client Biblioteca de autenticación de Microsoft (MSAL.NET)

Configurar su proyecto

En esta sección, creará un nuevo proyecto para mostrar cómo integrar una aplicación .NET de escritorio de Windows (XAML) con el inicio de sesión en Microsoft para que la aplicación pueda consultar las API web que requieren un token.

En la aplicación que creará se mostrará un botón para llamar a la API de Microsoft Graph, un área para mostrar los resultados y un botón para cerrar la sesión.

Nota:

¿Prefiere descargar este proyecto de Visual Studio de ejemplo en su lugar? Descargue un proyecto y vaya al paso de configuración para configurar el código de ejemplo antes de ejecutarlo.

Cree la aplicación mediante los pasos siguientes:

  1. Apertura de Visual Studio
  2. En la ventana de inicio, seleccione Crear un proyecto.
  3. En la lista desplegable Todos los lenguajes, seleccione C#.
  4. Busque la plantilla Aplicación de WPF (.NET Framework), selecciónela y, luego, seleccione Siguiente.
  5. En el cuadro Nombre del proyecto, escriba un nombre, como Win-App-calling-MsGraph.
  6. Elija una ubicación para el proyecto o acepte la opción predeterminada.
  7. En Framework, seleccione .NET Framework 4.8.
  8. Seleccione Crear.

Adición de MSAL al proyecto

  1. En Visual Studio, seleccione Herramientas>Administrador de paquetes NuGet>Consola del administrador de paquetes.

  2. En la ventana de la Consola del administrador de paquetes, pegue el siguiente comando de Azure PowerShell:

    Install-Package Microsoft.Identity.Client -Pre
    

Registrar su aplicación

Sugerencia

Los pasos de este artículo pueden variar ligeramente en función del portal desde donde comienza.

Para registrar y configurar la aplicación, siga estos pasos:

  1. Inicie sesión en el Centro de administración de Microsoft Entra al menos como Desarrollador de aplicaciones.
  2. Si tiene acceso a varios inquilinos, use el icono Configuración del menú superior para cambiar al inquilino en el que desea registrar la aplicación desde el menú Directorios y suscripciones.
  3. Vaya aIdentidad>Aplicaciones>Registros de aplicaciones.
  4. Seleccione Nuevo registro.
  5. Escriba el Nombre de la aplicación, por ejemplo Win-App-calling-MsGraph. Los usuarios de la aplicación pueden ver este nombre, el cual se puede cambiar más tarde.
  6. En la sección Tipos de cuenta admitidos, seleccione Cuentas en cualquier directorio organizativo (cualquier directorio de Microsoft Entra: multiinquilino) y cuentas de Microsoft personales (como Skype o Xbox).
  7. Seleccione Registrar.
  8. En Administrar, seleccione Autenticación>Agregar una plataforma.
  9. Seleccione Aplicaciones móviles y de escritorio.
  10. En la sección URI de redirección, seleccione https://login.microsoftonline.com/common/oauth2/nativeclient.
  11. Seleccione Configurar.

Adición del código para inicializar MSAL

En este caso, creará una clase para administrar la interacción con MSAL, como la administración de tokens.

  1. Abra el archivo App.xaml.cs y, a continuación, agregue la referencia de MSAL a la clase:

    using Microsoft.Identity.Client;
    
  2. Actualice la clase app a la siguiente:

    public partial class App : Application
    {
        static App()
        {
            _clientApp = PublicClientApplicationBuilder.Create(ClientId)
                .WithAuthority(AzureCloudInstance.AzurePublic, Tenant)
                .WithDefaultRedirectUri()
                .Build();
        }
    
        // Below are the clientId (Application Id) of your app registration and the tenant information.
        // You have to replace:
        // - the content of ClientID with the Application Id for your app registration
        // - the content of Tenant by the information about the accounts allowed to sign-in in your application:
        //   - For Work or School account in your org, use your tenant ID, or domain
        //   - for any Work or School accounts, use `organizations`
        //   - for any Work or School accounts, or Microsoft personal account, use `common`
        //   - for Microsoft Personal account, use consumers
        private static string ClientId = "Enter_the_Application_Id_here";
    
        private static string Tenant = "common";
    
        private static IPublicClientApplication _clientApp ;
    
        public static IPublicClientApplication PublicClientApp { get { return _clientApp; } }
    }
    

Creación de la interfaz de usuario de la aplicación

En esta sección se muestra cómo puede una aplicación consultar un servidor back-end protegido como Microsoft Graph.

Se creará automáticamente un archivo MainWindow.xaml como parte de la plantilla de proyecto. Abra este archivo y reemplace el nodo <Grid> de la aplicación por el código siguiente:

<Grid>
    <StackPanel Background="Azure">
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
            <Button x:Name="CallGraphButton" Content="Call Microsoft Graph API" HorizontalAlignment="Right" Padding="5" Click="CallGraphButton_Click" Margin="5" FontFamily="Segoe Ui"/>
            <Button x:Name="SignOutButton" Content="Sign-Out" HorizontalAlignment="Right" Padding="5" Click="SignOutButton_Click" Margin="5" Visibility="Collapsed" FontFamily="Segoe Ui"/>
        </StackPanel>
        <Label Content="API Call Results" Margin="0,0,0,-5" FontFamily="Segoe Ui" />
        <TextBox x:Name="ResultText" TextWrapping="Wrap" MinHeight="120" Margin="5" FontFamily="Segoe Ui"/>
        <Label Content="Token Info" Margin="0,0,0,-5" FontFamily="Segoe Ui" />
        <TextBox x:Name="TokenInfoText" TextWrapping="Wrap" MinHeight="70" Margin="5" FontFamily="Segoe Ui"/>
    </StackPanel>
</Grid>

Uso de MSAL para obtener un token de Microsoft Graph API

En esta sección, usará MSAL para obtener un token de la API de Microsoft Graph.

  1. En el archivo MainWindow.xaml.cs, añada la referencia de MSAL a la clase:

    using Microsoft.Identity.Client;
    
  2. Reemplace el código de clase MainWindow por el siguiente código:

    public partial class MainWindow : Window
    {
        //Set the API Endpoint to Graph 'me' endpoint
        string graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";
    
        //Set the scope for API call to user.read
        string[] scopes = new string[] { "user.read" };
    
    
        public MainWindow()
        {
            InitializeComponent();
        }
    
      /// <summary>
        /// Call AcquireToken - to acquire a token requiring user to sign-in
        /// </summary>
        private async void CallGraphButton_Click(object sender, RoutedEventArgs e)
        {
            AuthenticationResult authResult = null;
            var app = App.PublicClientApp;
            ResultText.Text = string.Empty;
            TokenInfoText.Text = string.Empty;
    
            var accounts = await app.GetAccountsAsync();
            var firstAccount = accounts.FirstOrDefault();
    
            try
            {
                authResult = await app.AcquireTokenSilent(scopes, firstAccount)
                    .ExecuteAsync();
            }
            catch (MsalUiRequiredException ex)
            {
                // A MsalUiRequiredException happened on AcquireTokenSilent.
                // This indicates you need to call AcquireTokenInteractive to acquire a token
                System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
    
                try
                {
                    authResult = await app.AcquireTokenInteractive(scopes)
                        .WithAccount(accounts.FirstOrDefault())
                        .WithPrompt(Prompt.SelectAccount)
                        .ExecuteAsync();
                }
                catch (MsalException msalex)
                {
                    ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
                }
            }
            catch (Exception ex)
            {
                ResultText.Text = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
                return;
            }
    
            if (authResult != null)
            {
                ResultText.Text = await GetHttpContentWithToken(graphAPIEndpoint, authResult.AccessToken);
                DisplayBasicTokenInfo(authResult);
                this.SignOutButton.Visibility = Visibility.Visible;
            }
        }
        }
    

Más información

Obtención de un token de usuario interactivamente

La llamada al método AcquireTokenInteractive genera una ventana que pide al usuario que inicie sesión. Las aplicaciones suelen requerir a los usuarios que inicien sesión de forma interactiva la primera vez que necesitan acceder a un recurso protegido. Es posible que también deban iniciar sesión cuando se produce un error en una operación silenciosa para adquirir un token (por ejemplo, si ha caducado la contraseña de un usuario).

Obtención de un token de usuario en silencio

El método AcquireTokenSilent controla las renovaciones y las adquisiciones de tokens sin la interacción del usuario. Después de que AcquireTokenInteractive se ejecute por primera vez, AcquireTokenSilent es el método habitual que se utiliza para obtener los tokens empleados para acceder a recursos protegidos en las llamadas posteriores, ya que las llamadas para solicitar o renovar los tokens se realizan en modo silencioso.

Es posible que se produzca un error en el método AcquireTokenSilent en algún momento. El error puede deberse a que el usuario haya cerrado sesión o cambiado su contraseña en otro dispositivo. Si MSAL detecta que el problema puede solucionarse requiriendo una acción interactiva, desencadena una excepción MsalUiRequiredException. La aplicación puede abordar esta excepción de dos maneras:

  • Puede realizar una llamada en AcquireTokenInteractive inmediatamente. Esta llamada provoca que se solicite al usuario que inicie sesión. Este patrón se usa en aplicaciones en línea en las que no hay ningún contenido disponible sin conexión para el usuario. La aplicación de ejemplo generada en esta instalación utiliza este patrón, que puede verse en acción la primera vez que se ejecuta la aplicación.

  • Dado que ningún usuario ha usado la aplicación, PublicClientApp.Users.FirstOrDefault() contendrá un valor NULL y se iniciará una excepción MsalUiRequiredException.

  • El código del ejemplo trata entonces la excepción llamando a AcquireTokenInteractive, lo que provoca que se pida al usuario que inicie sesión.

  • En su lugar, puede presentar una indicación visual a los usuarios para señalar que es necesario iniciar sesión de manera interactiva, lo que les permitirá escoger el momento oportuno para iniciar sesión. Asimismo, la aplicación puede volver a probar el método AcquireTokenSilent más tarde. Este patrón se usa con frecuencia cuando los usuarios pueden utilizar otra funcionalidad de aplicación sin interrupciones. Por ejemplo, si hay contenido sin conexión disponible en la aplicación. En este caso, los usuarios pueden decidir cuándo desean iniciar sesión para acceder al recurso protegido o para actualizar la información obsoleta. Como alternativa, la aplicación puede decidir probar el método AcquireTokenSilent de nuevo cuando se restablezca la red después de no haber estado disponible temporalmente.

Llamada a Microsoft Graph API con el token que acaba de obtener

Añada el siguiente método nuevo a su archivo MainWindow.xaml.cs. El método se utiliza para realizar una solicitud de GET a Graph API con un encabezado de autorización:

/// <summary>
/// Perform an HTTP GET request to a URL using an HTTP Authorization header
/// </summary>
/// <param name="url">The URL</param>
/// <param name="token">The token</param>
/// <returns>String containing the results of the GET operation</returns>
public async Task<string> GetHttpContentWithToken(string url, string token)
{
    var httpClient = new System.Net.Http.HttpClient();
    System.Net.Http.HttpResponseMessage response;
    try
    {
        var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
        //Add the token in Authorization header
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
        response = await httpClient.SendAsync(request);
        var content = await response.Content.ReadAsStringAsync();
        return content;
    }
    catch (Exception ex)
    {
        return ex.ToString();
    }
}

Más información acerca de cómo realizar una llamada de REST a una API protegida

En esta aplicación de ejemplo, el método GetHttpContentWithToken se usa para realizar una solicitud HTTP GET a un recurso protegido que requiere un token y, a continuación, devolver el contenido al autor de la llamada. Este método agrega el token adquirido al encabezado de autorización HTTP. En este ejemplo, el recurso es el punto de conexión me de Microsoft Graph API, que muestra información del perfil del usuario.

Adición de un método para cerrar la sesión de un usuario

Añada el método siguiente a su archivo MainWindow.xaml.cs para cerrar la sesión de un usuario:

/// <summary>
/// Sign out the current user
/// </summary>
private async void SignOutButton_Click(object sender, RoutedEventArgs e)
{
    var accounts = await App.PublicClientApp.GetAccountsAsync();

    if (accounts.Any())
    {
        try
        {
            await App.PublicClientApp.RemoveAsync(accounts.FirstOrDefault());
            this.ResultText.Text = "User has signed-out";
            this.CallGraphButton.Visibility = Visibility.Visible;
            this.SignOutButton.Visibility = Visibility.Collapsed;
        }
        catch (MsalException ex)
        {
            ResultText.Text = $"Error signing-out user: {ex.Message}";
        }
    }
}

Más información sobre cerrar la sesión de un usuario

El método SignOutButton_Click anterior quita a los usuarios de la caché de usuario de MSAL, lo que de hecho indicará a MSAL que olvide al usuario actual, por lo que una solicitud futura para adquirir un token solo se completará correctamente si se realiza de modo que sea interactiva.

Aunque la aplicación en este ejemplo es compatible con usuarios individuales, MSAL admite escenarios en los que es posible iniciar sesión en varias cuentas al mismo tiempo. Un ejemplo de esto es una aplicación de correo electrónico donde un usuario tiene varias cuentas.

Visualización de información de token básica

Para mostrar información básica sobre el token, añada el método siguiente a su archivo MainWindow.xaml.cs:

/// <summary>
/// Display basic information contained in the token
/// </summary>
private void DisplayBasicTokenInfo(AuthenticationResult authResult)
{
    TokenInfoText.Text = "";
    if (authResult != null)
    {
        TokenInfoText.Text += $"Username: {authResult.Account.Username}" + Environment.NewLine;
        TokenInfoText.Text += $"Token Expires: {authResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
    }
}

Más información

Además del token de acceso que se usa para llamar a la API de Microsoft Graph, después de que el usuario inicie sesión, MSAL también obtiene un token de identificador. Este token contiene un pequeño subconjunto de información que es pertinente para los usuarios. El método DisplayBasicTokenInfo muestra la información básica que contiene el token. Por ejemplo, muestra el nombre para mostrar del usuario y su identificador, así como la fecha de expiración del token y la cadena que representa al propio token de acceso. Puede seleccionar varias veces el botón Call Microsoft Graph API (Llamar a la API de Microsoft Graph) y ver que el mismo token se reutilizó para solicitudes posteriores. También puede ver que la fecha de expiración se amplía si MSAL decide que es el momento de renovar el token.

Probar el código

Presione F5 para ejecutar el proyecto en Visual Studio. La aplicación MainWindow se muestra tal como aparece aquí:

Test your application.

La primera vez que ejecute la aplicación y seleccione el botón Llamar a Microsoft Graph API, se le pedirá que inicie sesión. Para probar la aplicación, use una cuenta de Microsoft Entra (cuenta profesional o educativa) o una cuenta Microsoft (live.com, outlook.com).

Sign in to the application.

La primera vez que inicie sesión en su aplicación, también se le pedirá que dé su consentimiento para permitir que la aplicación acceda a su perfil e inicie sesión, como se muestra a continuación:

Provide your consent for application access.

Visualización de los resultados de la aplicación

Después de iniciar sesión, verá la información del perfil de usuario que devuelve la llamada a Microsoft Graph API. Los resultados se muestran en el cuadro API Call Results (Resultados de la llamada a la API). La información básica sobre el token que se ha adquirido a través de la llamada a AcquireTokenInteractive o a AcquireTokenSilent debe estar visible en el cuadro Información de token. Los resultados contienen las siguientes propiedades:

Propiedad Formato Descripción
Nombre de usuario user@domain.com Nombre de usuario que se usa para identificar al usuario.
Expiración del token DateTime Hora a la que expira el token. MSAL amplía la fecha de expiración al renovar el token según sea necesario.

Más información sobre los ámbitos y permisos delegados

Microsoft Graph API requiere el ámbito user.read para leer el perfil del usuario. Este ámbito se agrega automáticamente de forma predeterminada en todas las aplicaciones que se van a registrar en el Portal de registro de aplicaciones. Otras API de Microsoft Graph, así como las API personalizadas para el servidor back-end, pueden requerir ámbitos adicionales. Microsoft Graph API requiere el ámbito Calendars.Read para mostrar los calendarios del usuario.

Para acceder a los calendarios del usuario en el contexto de una aplicación, agregue el permiso delegado Calendars.Read a la información del registro de la aplicación. A continuación, agregue el ámbito Calendars.Read a la llamada acquireTokenSilent.

Nota:

Es posible que se pida al usuario algún consentimiento adicional a medida que aumente el número de ámbitos.

Ayuda y soporte técnico

Si necesita ayuda, desea informar de un problema o desea obtener información sobre las opciones de soporte técnico, consulte Opciones de ayuda y soporte técnico para desarrolladores.

Pasos siguientes

Más información sobre la creación de aplicaciones de escritorio que llaman a las API web protegidas en nuestra serie de escenarios de varias partes: