Esercitazione: Far accedere gli utenti e chiamare Microsoft Graph nell'app desktop Windows Presentation Foundation (WPF)

In questa esercitazione si creerà un'app .NET (XAML) desktop di Windows nativa che consente agli utenti di accedere e ottiene un token di accesso per chiamare l'API Microsoft Graph.

Dopo aver completato la guida, l'applicazione sarà in grado di chiamare un'API protetta che usa account personali (inclusi outlook.com, live.com e altri). L'applicazione userà anche gli account aziendali e dell'istituto di istruzione di qualsiasi azienda o organizzazione che usa l'ID Microsoft Entra.

Contenuto dell'esercitazione:

  • Creare un progetto Windows Presentation Foundation (WPF) in Visual Studio
  • Installare Microsoft Authentication Library (MSAL) per .NET
  • Registrare l'applicazione
  • Aggiungere il codice per supportare l'accesso e la disconnessione
  • Aggiungere il codice per chiamare l'API Microsoft Graph
  • Testare l'app

Prerequisiti

Funzionamento dell'app di esempio generata da questa guida

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

L'applicazione di esempio creata con questa guida abilita un'applicazione Desktop di Windows che esegue query sull'API Microsoft Graph o su un'API Web che accetta token da un endpoint di Microsoft Identity Platform. Per questo scenario, viene aggiunto un token a richieste HTTP tramite l'intestazione di autorizzazione. L'acquisizione e il rinnovo del token sono gestiti da Microsoft Authentication Library (MSAL).

Gestione dell'acquisizione di token per l'accesso ad API Web protette

Dopo l'autenticazione dell'utente, l'applicazione di esempio riceve un token che è possibile usare per eseguire query sull'API Microsoft Graph o su un'API Web protetta da Microsoft Identity Platform.

Le API come Microsoft Graph richiedono un token per consentire l'accesso a specifiche risorse. Ad esempio, è necessario un token per leggere il profilo di un utente, accedere al calendario di un utente o inviare un messaggio di posta elettronica. L'applicazione può richiedere un token di accesso usando MSAL per accedere alle risorse tramite la specifica degli ambiti dell'API. Questo token di accesso viene quindi aggiunto all'intestazione di autorizzazione HTTP per ogni chiamata effettuata per la risorsa protetta.

La memorizzazione nella cache e l'aggiornamento dei token di accesso vengono gestiti da MSAL e non devono quindi essere eseguiti dall'applicazione.

Pacchetti NuGet

Questa guida usa i pacchetti NuGet seguenti:

Libreria Descrizione
Microsoft.Identity.Client Microsoft Authentication Library (MSAL.NET)

Impostare il progetto

In questa sezione verrà creato un nuovo progetto per illustrare come integrare un'applicazione .NET di Windows Desktop (XAML) con l'accesso con Microsoft in modo che l'applicazione possa eseguire query sulle API Web che richiedono un token.

L'applicazione che verrà creata visualizza un pulsante che chiamerà l'API Microsoft Graph, un'area per visualizzare i risultati e un pulsante di disconnessione.

Nota

Se invece si preferisce scaricare questo progetto Visual Studio di esempio, scaricare un progetto e andare direttamente al passaggio della configurazione per configurare il codice di esempio prima di eseguirlo.

Creare l'applicazione seguendo questa procedura:

  1. Aprire Visual Studio.
  2. Nella finestra iniziale selezionare Crea un nuovo progetto.
  3. Nell'elenco a discesa Tutto il linguaggio selezionare C#.
  4. Cercare e scegliere il modello App WPF (.NET Framework) e quindi selezionare Avanti.
  5. Nella casella Nome progetto immettere un nome come Win-App-calling-MsGraph.
  6. Scegliere un percorso per il progetto o accettare l'opzione predefinita.
  7. In Framework selezionare .NET Framework 4.8.
  8. Seleziona Crea.

Aggiungere MSAL al progetto

  1. In Visual Studio, seleziona Strumenti>Gestione pacchetti NuGet>Console di gestione pacchetti.

  2. Nella finestra Console di Gestione pacchetti incollare il seguente comando di Azure PowerShell:

    Install-Package Microsoft.Identity.Client -Pre
    

Registrare l'applicazione

Suggerimento

I passaggi descritti in questo articolo possono variare leggermente in base al portale da cui si inizia.

Per registrare e configurare l'applicazione, seguire questa procedura:

  1. Accedere all'interfaccia di amministrazione di Microsoft Entra come almeno uno sviluppatore di applicazioni.
  2. Se si ha accesso a più tenant, usare l'icona Impostazioni nel menu in alto per passare al tenant in cui si vuole registrare l'applicazione dal menu Directory e sottoscrizioni.
  3. Passare a Applicazioni> di identità>Registrazioni app.
  4. Seleziona Nuova registrazione.
  5. In Nome immettere un nome per l'applicazione, ad esempio Win-App-calling-MsGraph. Tale nome, che potrebbe essere visualizzato dagli utenti dell'app, può essere modificato in un secondo momento.
  6. Nella sezione Tipi di account supportati selezionare Account in qualsiasi directory organizzativa (qualsiasi directory Microsoft Entra - Multi-tenant) e account Microsoft personali (ad esempio Skype, Xbox).
  7. Selezionare Registra.
  8. In Gestisci selezionare Autenticazione>Aggiungi una piattaforma.
  9. Selezionare Applicazioni per dispositivi mobili e desktop.
  10. Nella sezione URI di reindirizzamento selezionare https://login.microsoftonline.com/common/oauth2/nativeclient.
  11. Seleziona Configura.

Aggiungere il codice per inizializzare MSAL

Questo passaggio consente di creare una classe per gestire l'interazione con la libreria MSAL, ad esempio per la gestione dei token.

  1. Aprire il file App.xaml.cs, quindi aggiungere alla classe il riferimento relativo alla libreria MSAL:

    using Microsoft.Identity.Client;
    
  2. Aggiornare la classe App con il codice seguente:

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

Creare l'interfaccia utente dell'applicazione

Questa sezione illustra come un'applicazione può eseguire una query su un server back-end protetto come Microsoft Graph.

Un file MainWindow.xaml viene creato automaticamente come parte del modello di progetto. Aprire il file e sostituire il nodo <Grid> dell'applicazione con il codice seguente:

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

Usare MSAL per ottenere un token per l'API Microsoft Graph

In questa sezione si usa MSAL per ottenere un token per l'API Microsoft Graph.

  1. Nel file MainWindow.xaml.cs aggiungere alla classe il riferimento relativo a MSAL:

    using Microsoft.Identity.Client;
    
  2. Sostituire il codice della MainWindow classe con il codice seguente:

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

Altre informazioni

Ottenere un token utente in modo interattivo

Se si chiama il metodo AcquireTokenInteractive, viene visualizzata una finestra in cui viene chiesto agli utenti di eseguire l'accesso. Le applicazioni in genere richiedono agli utenti di eseguire l'accesso in modo interattivo quando devono accedere per la prima volta a una risorsa protetta. Potrebbe anche essere necessario accedere quando un'operazione invisibile all'utente per acquisire un token non riesce , ad esempio quando la password di un utente è scaduta.

Ottenere un token utente in modo automatico

Il metodo AcquireTokenSilent gestisce le acquisizioni e i rinnovi dei token senza alcuna interazione da parte dell'utente. Dopo la prima esecuzione di AcquireTokenInteractive, per le chiamate successive il metodo generalmente usato per ottenere i token per accedere alle risorse protette è AcquireTokenSilent, perché le chiamate per richiedere o rinnovare i token vengono effettuate in modo invisibile all'utente.

Alla fine, il AcquireTokenSilent metodo potrebbe non riuscire. ad esempio perché l'utente ha effettuato la disconnessione o ha modificato la propria password su un altro dispositivo. Se MSAL rileva che il problema può essere risolto richiedendo un'azione interattiva, viene attivata un'eccezione MsalUiRequiredException. L'applicazione può gestire questa eccezione in due modi:

  • Può eseguire immediatamente una chiamata a AcquireTokenInteractive e richiedere così all'utente di eseguire l'accesso. Questo modello viene usato nelle applicazioni online in cui non è disponibile contenuto offline per l'utente. L'esempio generato da questa configurazione segue questo modello, che può essere visualizzato in azione la prima volta che si esegue l'esempio.

  • Dato che nessun utente ha usato l'applicazione, PublicClientApp.Users.FirstOrDefault() contiene un valore Null e viene generata un'eccezione MsalUiRequiredException.

  • Il codice dell'esempio gestisce quindi l'eccezione chiamando AcquireTokenInteractive e richiedendo così all'utente di eseguire l'eccesso.

  • Può presentare un'indicazione visiva per informare gli utenti che è necessario un accesso interattivo e consentire così di scegliere il momento opportuno per accedere. In alternativa, l'applicazione può riprovare a eseguire AcquireTokenSilent in un secondo momento. Questo modello viene spesso usato quando gli utenti possono usare altre funzionalità dell'applicazione senza interruzioni. Ad esempio, quando il contenuto offline è disponibile nell'applicazione. In questo caso, gli utenti possono decidere se vogliono eseguire l'accesso per accedere alla risorsa protetta oppure aggiornare le informazioni obsolete. In alternativa, l'applicazione può decidere di riprovare a eseguire AcquireTokenSilent quando la rete viene ripristinata dopo essere stata temporaneamente non disponibile.

Chiamare l'API Microsoft Graph usando il token appena ottenuto

Aggiungere il nuovo metodo seguente al file MainWindow.xaml.cs. Questo metodo consente di eseguire una richiesta GET all'API Graph usando un'intestazione Authorize:

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

Altre informazioni sull'esecuzione di una chiamata REST a un'API protetta

In questa applicazione di esempio viene usato il metodo GetHttpContentWithToken per eseguire una richiesta HTTP GET a una risorsa protetta che richiede un token e restituire il contenuto al chiamante. Questo metodo aggiunge il token acquisito nell'intestazione di autorizzazione HTTP. Per questo esempio, la risorsa è l'endpoint me dell'API Microsoft Graph, che visualizza le informazioni del profilo dell'utente.

Aggiungere un metodo per disconnettere un utente

Per disconnettere un utente, aggiungere il metodo seguente al file MainWindow.xaml.cs:

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

Altre informazioni sulla disconnessione degli utenti

Il SignOutButton_Click metodo rimuove gli utenti dalla cache dell'utente MSAL, che in effetti indica a MSAL di dimenticare l'utente corrente in modo che una richiesta futura di acquisizione di un token abbia esito positivo solo se viene effettuata per essere interattiva.

Nonostante l'applicazione in questo esempio supporti singoli utenti, MSAL supporta anche scenari con accesso contemporaneo di più account, ad esempio un'applicazione di posta elettronica in cui un utente ha più account.

Visualizzare informazioni di base sui token

Per visualizzare informazioni di base sui token, aggiungere il metodo seguente al file 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;
    }
}

Altre informazioni

Oltre al token di accesso che viene usato per chiamare l'API Microsoft Graph, dopo l'accesso dell'utente, MSAL ottiene anche un token ID. Questo token contiene un piccolo subset di informazioni pertinenti agli utenti. Il metodo DisplayBasicTokenInfo visualizza le informazioni di base contenute nel token. Ad esempio, visualizza l'ID e il nome visualizzato dell'utente, la data di scadenza del token e la stringa che rappresenta il token di accesso. Premendo più volte il pulsante Call Microsoft Graph API (Chiama API Microsoft Graph), è possibile vedere che lo stesso token è stato usato per più richieste successive. È anche possibile visualizzare la data di scadenza estesa quando MSAL decide che è il momento di rinnovare il token.

Eseguire test del codice

Per eseguire il progetto, in Visual Studio selezionare F5. Viene visualizzata l'applicazione MainWindow, come mostrato di seguito:

Test your application.

Alla prima esecuzione dell'applicazione, quando si seleziona il pulsante Call Microsoft Graph API (Chiama API Microsoft Graph), viene richiesto l'accesso. Usare un account Microsoft Entra (account aziendale o dell'istituto di istruzione) o un account Microsoft (live.com, outlook.com) per testarlo.

Sign in to the application.

Al primo accesso all'applicazione viene richiesto anche di specificare il consenso per permettere all'applicazione di accedere al profilo e di completare l'accesso per l'utente, come mostrato di seguito:

Provide your consent for application access.

Visualizzare i risultati dell'applicazione

Dopo l'accesso dovrebbero essere visualizzate le informazioni sul profilo utente restituite dalla chiamata all'API Microsoft Graph . I risultati vengono visualizzati nella casella API Call Results (Risultati della chiamata API). Le informazioni di base sul token acquisito tramite la chiamata a AcquireTokenInteractive o AcquireTokenSilent dovrebbero essere visibili nella casella Token Info (Info sul token). I risultati contengono le proprietà seguenti:

Proprietà Formato Descrizione
Nome utente user@domain.com Nome utente usato per identificare l'utente.
Token Expires (Scadenza token) Data/Ora Ora in cui scadrà il token. MSAL estende la data di scadenza rinnovando il token in base alla necessità.

Altre informazioni sugli ambiti e sulle autorizzazioni delegate

L'API Microsoft Graph richiede l'ambito user.read per leggere il profilo dell'utente. Per impostazione predefinita, questo ambito viene aggiunto automaticamente in ogni applicazione registrata nel portale di registrazione dell'applicazione. Altre API per Microsoft Graph e le API personalizzate per il server di back-end potrebbero richiedere anche altri ambiti. L'API Microsoft Graph richiede l'ambito Calendars.Read per elencare i calendari dell'utente.

Per accedere ai calendari dell'utente nel contesto di un'applicazione, aggiungere l'autorizzazione delegata Calendars.Read alle informazioni di registrazione dell'applicazione. Aggiungere quindi l'ambito Calendars.Read alla chiamata acquireTokenSilent.

Nota

Con l'aumentare del numero di ambiti è possibile che all'utente venga chiesto di esprimere anche altri tipi di consenso.

Assistenza e supporto

Se è necessaria assistenza, si vuole segnalare un problema o si vogliono ottenere informazioni sulle opzioni di supporto, vedere Assistenza e supporto per gli sviluppatori.

Passaggi successivi

Altre informazioni sulla creazione di app desktop che chiamano API Web protette nella serie di scenari in più parti: