Chiamare l'API Microsoft Graph da un'app Windows Desktop

Questa guida dimostra come un'applicazione .NET per Windows Desktop (XAML) nativa può ottenere un token di accesso e chiamare l'API Microsoft Graph o altre API che richiedono token di accesso dall'endpoint di Azure Active Directory v2.

Al termine di questa guida, l'applicazione sarà in grado di chiamare un'API protetta usando sia account personali (ad esempio, outlook.com, live.com e altri) sia account aziendali o di istituti di istruzione di proprietà di aziende o organizzazioni con Azure Active Directory.

Questa guida richiede Visual Studio 2015 Update 3 o Visual Studio 2017. Se non lo si ha, è possibile scaricare Visual Studio 2017 gratuitamente

Come interpretare questa guida

Come interpretare questa guida

L'applicazione di esempio creata in questa guida consente a un'applicazione per Windows Desktop di eseguire query nell'API Microsoft Graph o in un'API Web che accetta token dall'endpoint di Azure Active Directory v2. Per questo scenario, viene aggiunto un token a richieste HTTP tramite l'intestazione di autorizzazione. L'acquisizione e il rinnovo del token vengono gestiti da Microsoft Authentication Library (MSAL).

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

Dopo che l'utente ha eseguito l'autenticazione, l'applicazione di esempio riceve un token che può essere usato per eseguire query nell'API Microsoft Graph o in un'API Web protetta da Microsoft Azure Active Directory v2.

API come Microsoft Graph richiedono un token di accesso per consentire l'accesso a risorse specifiche, ad esempio per leggere un profilo utente, accedere al calendario dell'utente o inviare un messaggio di posta elettronica. L'applicazione può richiedere un token di accesso usando la libreria MSAL per accedere alle risorse tramite la definizione di ambiti API. Il token di accesso ottenuto viene quindi aggiunto all'intestazione di autorizzazione HTTP per ogni chiamata effettuata alla risorsa protetta.

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

Pacchetti NuGet

Questa guida usa i pacchetti NuGet seguenti:

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

Configurare il progetto

Questa sezione fornisce istruzioni dettagliate su come creare un nuovo progetto per illustrare come integrare un'applicazione .NET per Windows Desktop (XAML) con Accedi con Microsoft in modo da poter eseguire query su API Web che richiedono un token.

L'applicazione creata in questa guida include un pulsante che consente di generare un grafico dei risultati da visualizzare sullo schermo, oltre a un pulsante di disconnessione.

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

Creare l'applicazione

  1. In Visual Studio: File > New > Project
  2. In Modelli selezionare Visual C#
  3. Selezionare WPF App (o Applicazione WPF, a seconda della versione di Visual Studio)

Aggiungere Microsoft Authentication Library (MSAL) al progetto

  1. In Visual Studio: Tools > Nuget Package Manager > Package Manager Console
  2. Nella finestra Console di Gestione pacchetti copiare e incollare il codice seguente:
Install-Package Microsoft.Identity.Client -Pre

Questo pacchetto consente di installare Microsoft Authentication Library (MSAL), che gestisce l'acquisizione, la memorizzazione nella cache e l'aggiornamento dei token utente usati per accedere ad API protette da Azure Active Directory v2.

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 e aggiungere alla classe il riferimento relativo alla libreria MSAL:
using Microsoft.Identity.Client;
  1. Aggiornare la classe App con il codice seguente:
public partial class App : Application
{
    //Below is the clientId of your app registration. 
    //You have to replace the below with the Application Id for your app registration
    private static string ClientId = "your_client_id_here";

    public static PublicClientApplication PublicClientApp = new PublicClientApplication(ClientId);

}

Creare l'interfaccia utente dell'applicazione

La sezione seguente illustra come un'applicazione può eseguire una query su un server back-end protetto come Microsoft Graph. Nell'ambito del modello di progetto viene automaticamente creato un file MainWindow.xaml. Aprire il file e seguire le istruzioni seguenti:

Sostituire il valore <Grid> dell'applicazione con il 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 Microsoft Authentication Library (MSAL) per ottenere un token per l'API Microsoft Graph

Questa sezione illustra come usare MSAL per ottenere un token per l'API Microsoft Graph.

  1. In MainWindow.xaml.cs aggiungere alla classe il riferimento della libreria MSAL:
using Microsoft.Identity.Client;
  1. Sostituire il codice della classe MainWindow con:
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 AcquireTokenAsync - to acquire a token requiring user to sign-in
    /// </summary>
    private async void CallGraphButton_Click(object sender, RoutedEventArgs e)
    {
        AuthenticationResult authResult = null;

        try
        {
            authResult = await App.PublicClientApp.AcquireTokenSilentAsync(_scopes, App.PublicClientApp.Users.FirstOrDefault());
        }
        catch (MsalUiRequiredException ex)
        {
            // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token
            System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");

            try
            {
                authResult = await App.PublicClientApp.AcquireTokenAsync(_scopes);
            }
            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

Acquisizione di un token utente in modo interattivo

Se si chiama il metodo AcquireTokenAsync, viene visualizzata una finestra in cui si chiede all'utente di eseguire l'accesso. In genere, le applicazioni chiedono agli utenti di accedere in modo interattivo la prima volta che devono accedere a una risorsa protetta o quando un'operazione invisibile di acquisizione di un token ha esito negativo (ad esempio, perché la password dell'utente è scaduta).

Acquisizione di un token utente in modo invisibile

AcquireTokenSilentAsync gestisce le acquisizioni e i rinnovi dei token senza alcuna interazione da parte dell'utente. Dopo aver eseguito AcquireTokenAsync la prima volta, per le chiamate successive il metodo generalmente usato per ottenere token per accedere a risorse protette è AcquireTokenSilentAsync: le chiamate per richiedere o rinnovare token vengono effettuate in modo invisibile per l'utente. Alla fine, tuttavia, AcquireTokenSilentAsync avrà esito negativo perché, ad esempio, l'utente si sarà disconnesso o avrà modificato la password in un altro dispositivo. Se MSAL rileva che il problema può essere risolto richiedendo un'azione interattiva, viene attivata una MsalUiRequiredException. L'applicazione può gestire questa eccezione in due modi:

  1. Effettuare subito una chiamata a AcquireTokenAsync, in modo da chiedere all'utente di eseguire l'accesso. Questo criterio viene usato in genere nelle applicazioni online in cui non sono disponibili contenuti offline per l'utente. L'esempio generato in questa installazione guidata usa questo criterio, che è possibile vedere in pratica la prima volta che si esegue l'esempio: poiché nessun utente ha mai usato l'applicazione, PublicClientApp.Users.FirstOrDefault() conterrà un valore null e verrà generata un'eccezione MsalUiRequiredException. Il codice dell'esempio gestirà quindi l'eccezione chiamando AcquireTokenAsync, ovvero chiedendo all'utente di eseguire l'eccesso.

  2. Le applicazioni possono anche generare un'indicazione visiva per informare l'utente che è necessario un accesso interattivo, in modo da consentire di scegliere il momento più opportuno per accedere. In alternativa, l'applicazione riproverà a eseguire AcquireTokenSilentAsync in un secondo momento. Questo metodo viene usato in genere quando l'utente può accedere ad altre funzionalità dell'applicazione senza essere interrotto, ad esempio quando nell'applicazione sono disponibili contenuti offline. In questo caso, l'utente può decidere quando eseguire l'accesso per accedere alla risorsa protetta o per aggiornare informazioni obsolete. In alternativa, l'applicazione può decidere di riprovare a eseguire AcquireTokenSilentAsync se la rete viene ripristinata dopo essere stata temporaneamente non disponibile.

Chiamare l'API Microsoft Graph usando il token appena ottenuto

  1. Aggiungere il nuovo metodo seguente al file MainWindow.xaml.cs. Questo metodo consente di eseguire una richiesta GET all'API Graph usando un'intestazione di autorizzazione:
/// <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 restituisce 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 consente di visualizzare informazioni sul profilo dell'utente.

Aggiungere un metodo per disconnettere l'utente

  1. Aggiungere il metodo seguente al file MainWindow.xaml.cs per disconnettere l'utente:
/// <summary>
/// Sign out the current user
/// </summary>
private void SignOutButton_Click(object sender, RoutedEventArgs e)
{
    if (App.PublicClientApp.Users.Any())
    {
        try
        {
            App.PublicClientApp.Remove(App.PublicClientApp.Users.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

L'oggetto SignOutButton_Click rimuove l'utente dalla cache utente di MSAL: in questo modo, MSAL dimenticherà l'utente corrente e un'eventuale richiesta futura di acquisizione di un token riuscirà solo effettuata in modo interattivo. Anche se l'applicazione in questo esempio supporta un unico utente, MSAL supporta anche scenari in cui è possibile eseguire contemporaneamente l'accesso di più account, come nel caso di un'applicazione di posta elettronica in cui un utente dispone di più account.

Visualizzare informazioni di base sul token

  1. Aggiungere il metodo seguente al file MainWindow.xaml.cs per visualizzare informazioni di base sul token:
/// <summary>
/// Display basic information contained in the token
/// </summary>
private void DisplayBasicTokenInfo(AuthenticationResult authResult)
{
    TokenInfoText.Text = "";
    if (authResult != null)
    {
        TokenInfoText.Text += $"Name: {authResult.User.Name}" + Environment.NewLine;
        TokenInfoText.Text += $"Username: {authResult.User.DisplayableId}" + Environment.NewLine;
        TokenInfoText.Text += $"Token Expires: {authResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
        TokenInfoText.Text += $"Access Token: {authResult.AccessToken}" + Environment.NewLine;
    }
}

Altre informazioni

I token acquisiti tramite OpenID Connect contengono anche un piccolo subset di informazioni relative all'utente. DisplayBasicTokenInfo consente di visualizzare le informazioni di base contenute nel token, quali l'ID e il nome visualizzato dell'utente, la data di scadenza del token e la stringa che rappresenta il token di accesso. Queste informazioni possono essere visualizzate dall'utente. Premendo più volte il pulsante Call Microsoft Graph API (Chiama API Microsoft Graph), è possibile vedere che lo stesso token è stato usato per richieste successive. Se MSAL decide che è il momento di rinnovare il token, è possibile anche vedere la data di scadenza estesa.

Creare un'applicazione (Rapida)

È ora necessario registrare l'applicazione nel portale di registrazione delle applicazioni Microsoft:

  1. Registrare l'applicazione tramite il portale di registrazione delle applicazioni Microsoft
  2. Immettere un nome per l'applicazione e l'indirizzo di posta elettronica
  3. Assicurarsi che l'opzione per l'installazione guidata sia selezionata
  4. Seguire le istruzioni per ottenere l'ID dell'applicazione e incollarlo nel codice

Aggiungere le informazioni di registrazione dell'applicazione alla soluzione (Avanzata)

È ora necessario registrare l'applicazione nel portale di registrazione delle applicazioni Microsoft:

  1. Passare al portale di registrazione delle applicazioni Microsoft per registrare un'applicazione
  2. Immettere un nome per l'applicazione e l'indirizzo di posta elettronica
  3. Assicurarsi che l'opzione per l'installazione guidata sia deselezionata
  4. Fare clic su Add Platform, selezionare Native Application e quindi fare clic su Salva
  5. Copiare il GUID nell'ID applicazione, tornare a Visual Studio, aprire App.xaml.cs e sostituire your_client_id_here con l'ID applicazione appena registrato:
private static string ClientId = "your_application_id_here";

Testare il codice

Per testare l'applicazione, premere F5 per eseguire il progetto in Visual Studio. Verrà visualizzata la finestra principale:

Schermata di esempio

Quando si è pronti per eseguire il test, fare clic su Call Microsoft Graph API (Chiama API Microsoft Graph) e usare un account Microsoft Azure Active Directory (account aziendale) o un account Microsoft (live.com, outlook.com) per accedere. Se è la prima volta, verrà visualizzata una finestra in cui si chiede all'utente di eseguire l'accesso:

Accesso

La prima volta che si accede all'applicazione, viene visualizzata una schermata di consenso, simile alla figura seguente, in cui è necessario accettare in modo esplicito:

Schermata di consenso

Risultati previsti

Nella schermata API Call Results (Risultati chiamata API) vengono visualizzate le informazioni sul profilo utente restituite dalla chiamata all'API Microsoft Graph.

Nella casella Token Info (Informazioni sul token) vengono visualizzate anche informazioni di base sul token acquisite tramite AcquireTokenAsync o AcquireTokenSilentAsync:

Proprietà Format Descrizione
Nome {Nome utente completo} Nome e cognome dell'utente
Username user@domain.com Nome utente usato per identificare l'utente
Token Expires (Scadenza token) {DateTime} Ora in cui scadrà il token. MSAL estende automaticamente la data di scadenza rinnovando il token ogni volta che è necessario
Token di accesso {Stringa} Stringa di token che verrà inviata alle richieste HTTP che richiedono un'intestazione di autorizzazione

Altre informazioni sugli ambiti e sulle autorizzazioni delegate

L'API Graph richiede l'ambito user.read per leggere il profilo utente. Per impostazione predefinita, questo ambito viene aggiunto automaticamente in ogni applicazione registrata nel portale di registrazione. Altre API Graph e alcune API personalizzate per il server back-end richiedono anche altri ambiti. Graph, ad esempio, richiede l'ambito Calendars.Read per elencare i calendari degli utenti. Per poter accedere al calendario dell'utente nel contesto di un'applicazione, è necessario aggiungere le informazioni di registrazione dell'applicazione delegata Calendars.Read e aggiungere Calendars.Read alla chiamata AcquireTokenAsync. Con l'aumentare del numero di ambiti è possibile che all'utente venga chiesto di esprimere anche altri tipi di consenso.