Call the Microsoft Graph API from a Windows Desktop app

This guide demonstrates how a native Windows Desktop .NET (XAML) application can get an access token and call Microsoft Graph API or other APIs that require access tokens from Azure Active Directory v2 endpoint.

At the end of this guide, your application will be able to call a protected API using personal accounts (including outlook.com, live.com, and others) as well as work and school accounts from any company or organization that has Azure Active Directory.

This guide requires Visual Studio 2015 Update 3 or Visual Studio 2017. Don’t have it? Download Visual Studio 2017 for free

How this guide works

How this guide works

The sample application created by this guide enables a Windows Desktop Application to query Microsoft Graph API or a Web API that accepts tokens from Azure Active Directory v2 endpoint. For this scenario, a token is added to HTTP requests via the Authorization header. Token acquisition and renewal is handled by the Microsoft Authentication Library (MSAL).

Handling token acquisition for accessing protected Web APIs

After the user authenticates, the sample application receives a token that can be used to query Microsoft Graph API or a Web API secured by Microsoft Azure Active Directory v2.

APIs such as Microsoft Graph require an access token to allow accessing specific resources – for example, to read a user’s profile, access user’s calendar or send an email. Your application can request an access token using MSAL to access these resources by specifying API scopes. This access token is then added to the HTTP Authorization header for every call made against the protected resource.

MSAL manages caching and refreshing access tokens for you, so your application doesn't need to.

NuGet Packages

This guide uses the following NuGet packages:

Library Description
Microsoft.Identity.Client Microsoft Authentication Library (MSAL)

Set up your project

This section provides step-by-step instructions for how to create a new project to demonstrate how to integrate a Windows Desktop .NET application (XAML) with Sign-In with Microsoft so it can query Web APIs that requires a token.

The application created by this guide exposes a button to graph and show results on screen and a sign-out button.

Prefer to download this sample's Visual Studio project instead? Download a project and skip to the Configuration step to configure the code sample before executing.

Create your application

  1. In Visual Studio: File > New > Project
  2. Under Templates, select Visual C#
  3. Select WPF App (or WPF Application depending on the version of your Visual Studio)

Add the Microsoft Authentication Library (MSAL) to your project

  1. In Visual Studio: Tools > Nuget Package Manager > Package Manager Console
  2. Copy/paste the following in the Package Manager Console window:
Install-Package Microsoft.Identity.Client -Pre

The package above installs the Microsoft Authentication Library (MSAL). MSAL handles acquiring, caching and refreshing user toskens used to access APIs protected by Azure Active Directory v2.

Add the code to initialize MSAL

This step will help you create a class to handle interaction with MSAL Library, such as handling of tokens.

  1. Open the App.xaml.cs file and add the reference for MSAL library to the class:
using Microsoft.Identity.Client;
  1. Update the App class to the following:
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);

}

Create your application’s UI

The section below shows how an application can query a protected backend server like Microsoft Graph. A MainWindow.xaml file should automatically be created as a part of your project template. Open this file this file and then follow the instructions below:

Replace your application’s <Grid> with be the following:

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

Use the Microsoft Authentication Library (MSAL) to get a token for the Microsoft Graph API

This section shows how to use MSAL to get a token the Microsoft Graph API.

  1. In MainWindow.xaml.cs, add the reference for MSAL library to the class:
using Microsoft.Identity.Client;
  1. Replace MainWindow class code with:
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;
        }
    }
}

More Information

Getting a user token interactive

Calling the AcquireTokenAsync method results in a window prompting the user to sign in. Applications usually require a user to sign in interactively the first time they need to access a protected resource, or when a silent operation to acquire a token fails (e.g. the user’s password expired).

Getting a user token silently

AcquireTokenSilentAsync handles token acquisitions and renewal without any user interaction. After AcquireTokenAsync is executed for the first time, AcquireTokenSilentAsync is the usual method used to obtain tokens used to access protected resources for subsequent calls - as calls to request or renew tokens are made silently. Eventually, AcquireTokenSilentAsync will fail – e.g. the user has signed out, or has changed their password on another device. When MSAL detects that the issue can be resolved by requiring an interactive action, it fires an MsalUiRequiredException. Your application can handle this exception in two ways:

  1. Make a call against AcquireTokenAsync immediately, which results in prompting the user to sign-in. This pattern is usually used in online applications where there is no offline content in the application available for the user. The sample generated by this guided setup uses this pattern: you can see it in action the first time you execute the sample: because no user ever used the application, PublicClientApp.Users.FirstOrDefault() will contain a null value, and an MsalUiRequiredException exception will be thrown. The code in the sample then handles the exception by calling AcquireTokenAsync resulting in prompting the user to sign-in.

  2. Applications can also make a visual indication to the user that an interactive sign-in is required, so the user can select the right time to sign in, or the application can retry AcquireTokenSilentAsync at a later time. This is usually used when the user can use other functionality of the application without being disrupted - for example, there is offline content available in the application. In this case, the user can decide when they want to sign in to access the protected resource, or to refresh the outdated information, or your application can decide to retry AcquireTokenSilentAsync when network is restored after being unavailable temporarily.

Call the Microsoft Graph API using the token you just obtained

  1. Add the new method below to your MainWindow.xaml.cs. The method is used to make a GET request against Graph API using an Authorize header:
/// <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();
    }
}

More information on making a REST call against a protected API

In this sample application, the GetHttpContentWithToken method is used to make an HTTP GET request against a protected resource that requires a token and then return the content to the caller. This method adds the acquired token in the HTTP Authorization header. For this sample, the resource is the Microsoft Graph API me endpoint – which displays the user's profile information.

Add a method to sign out the user

  1. Add the following method to your MainWindow.xaml.cs to sign out the user:
/// <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}";
        }
    }
}

More info on Sign-Out

SignOutButton_Click removes the user from MSAL user cache – this will effectively tell MSAL to forget the current user so a future request to acquire a token will only succeed if it is made to be interactive. Although the application in this sample supports a single user, MSAL supports scenarios where multiple accounts can be signed-in at the same time – an example is an email application where a user has multiple accounts.

Display Basic Token Information

  1. Add the following method to to your MainWindow.xaml.cs to display basic information about the 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;
    }
}

More Information

Tokens acquired via OpenID Connect also contain a small subset of information pertinent to the user. DisplayBasicTokenInfo displays basic information contained in the token: for example, the user's display name and ID, as well as the token expiration date and the string representing the access token itself. This information is displayed for you to see. You can hit the Call Microsoft Graph API button multiple times and see that the same token was reused for subsequent requests. You can also see the expiration date being extended when MSAL decides it is time to renew the token.

Create an application (Express)

Now you need to register your application in the Microsoft Application Registration Portal:

  1. Register your application via the Microsoft Application Registration Portal
  2. Enter a name for your application and your email
  3. Make sure the option for Guided Setup is checked
  4. Follow the instructions to obtain the application ID and paste it into your code

Add your application registration information to your solution (Advanced)

Now you need to register your application in the Microsoft Application Registration Portal:

  1. Go to the Microsoft Application Registration Portal to register an application
  2. Enter a name for your application and your email
  3. Make sure the option for Guided Setup is unchecked
  4. Click Add Platform, then select Native Application and hit Save
  5. Copy the GUID in Application ID, go back to Visual Studio, open App.xaml.cs and replace your_client_id_here with the Application ID you just registered:
private static string ClientId = "your_application_id_here";

Test your code

In order to test your application, press F5 to run your project in Visual Studio. Your Main Window should appear:

Sample screen shot

When you're ready to test, click Call Microsoft Graph API and use a Microsoft Azure Active Directory (organizational account) or a Microsoft Account (live.com, outlook.com) account to sign in. It it is the first time, you will see a window asking user to sign in:

Sign-in

The first time you sign in to your application, you will be presented with a consent screen similar to the below, where you need to explicitly accept:

Consent Screen

Expected results

You should see user profile information returned by the Microsoft Graph API call on the API Call Results screen.

You should also see basic information about the token acquired via AcquireTokenAsync or AcquireTokenSilentAsync in the Token Info box:

Property Format Description
Name {User Full name} The user’s first and last name
Username user@domain.com The username used to identify the user
Token Expires {DateTime} The time on which the token expires. MSAL will extend the expiration date for you by renewing the token when necessary
Access token {String} The token string sent that will be sent to HTTP requests that require an authorization header

More information about scopes and delegated permissions

Graph API requires the user.read scope to read user profile. This scope is automatically added by default in every application being registered on our registration portal. Some other Graph APIs as well as custom APIs for your backend server require additional scopes. For example, for Graph, Calendars.Read is required to list user’s calendars. In order to access the user’s calendar in a context of an application, you need to add Calendars.Read delegated application registration’s information and then add Calendars.Read to the AcquireTokenAsync call. User may be prompted for additional consents as you increase the number of scopes.