Call the Microsoft Graph API from a Universal Windows Platform application (XAML)

This guide explains how a native Universal Windows Platform (UWP) application can request an access token. The application then calls the Microsoft Graph API. The guide also applies to other APIs that require access tokens from the Microsoft identity platform endpoint.

At the end of this guide, your application calls a protected API by using personal accounts. Examples are outlook.com, live.com, and others. Your application also calls work and school accounts from any company or organization that has Azure Active Directory (Azure AD).

Note

This guide requires Visual Studio with Universal Windows Platform development installed. For instructions to download and configure Visual Studio to develop Universal Windows Platform apps, see Get set up.

Note

If you're new to the Microsoft identity platform, start with the Call the Microsoft Graph API from a Universal Windows Platform (UWP) application quickstart.

How this guide works

Shows how the sample app generated by this tutorial works

This guide creates a sample UWP application that queries the Microsoft Graph API. For this scenario, a token is added to HTTP requests by using the Authorization header. Microsoft Authentication Library handles token acquisitions and renewals.

NuGet packages

This guide uses the following NuGet package:

Library Description
Microsoft.Identity.Client Microsoft Authentication Library
Microsoft.Graph Microsoft Graph Client Library

Set up your project

This section provides step-by-step instructions to integrate a Windows Desktop .NET application (XAML) with sign-in with Microsoft. Then the application can query web APIs that require a token, such as the Microsoft Graph API.

This guide creates an application that displays a button that queries the Microsoft Graph API and a button to sign out. It also displays text boxes that contain the results of the calls.

Note

Do you want to download this sample's Visual Studio project instead of creating it? Download a project, and skip to the application registration step to configure the code sample before it runs.

Create your application

  1. Open Visual Studio, and select Create a new project.

  2. In Create a new project, choose Blank App (Universal Windows) for C# and select Next.

  3. In Configure your new project, name the app, and select Create.

  4. If prompted, in New Universal Windows Platform Project, select any version for Target and Minimum versions, and select OK.

    Minimum and Target versions

Add Microsoft Authentication Library to your project

  1. In Visual Studio, select Tools > NuGet Package Manager > Package Manager Console.

  2. Copy and paste the following commands in the Package Manager Console window:

    Install-Package Microsoft.Identity.Client
    Install-Package Microsoft.Graph
    

    Note

    The first command installs Microsoft Authentication Library (MSAL.NET). MSAL.NET acquires, caches, and refreshes user tokens that access APIs that are protected by the Microsoft identity platform. The second command installs Microsoft Graph .NET Client Library to authenticate requests to Microsoft Graph and make calls to the service.

Create your application's UI

Visual Studio creates MainPage.xaml as a part of your project template. Open this file, and then replace your application's Grid node with the following code:

<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>
        <TextBlock Text="API Call Results" Margin="2,0,0,-5" FontFamily="Segoe Ui" />
        <TextBox x:Name="ResultText" TextWrapping="Wrap" MinHeight="120" Margin="5" FontFamily="Segoe Ui"/>
        <TextBlock Text="Token Info" Margin="2,0,0,-5" FontFamily="Segoe Ui" />
        <TextBox x:Name="TokenInfoText" TextWrapping="Wrap" MinHeight="70" Margin="5" FontFamily="Segoe Ui"/>
    </StackPanel>
</Grid>

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

This section shows how to use Microsoft Authentication Library to get a token for the Microsoft Graph API. Make changes to the MainPage.xaml.cs file.

  1. In MainPage.xaml.cs, add the following references:

    using Microsoft.Identity.Client;
    using Microsoft.Graph;
    using System.Diagnostics;
    using System.Threading.Tasks;
    
  2. Replace your MainPage class with the following code:

    public sealed partial class MainPage : Page
    {
    
        //Set the scope for API call to user.read
        private string[] scopes = new string[] { "user.read" };
    
        // 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
        private const string ClientId = "[Application Id pasted from the application registration portal]";
    
        private const string Tenant = "common"; // Alternatively "[Enter your tenant, as obtained from the Azure portal, e.g. kko365.onmicrosoft.com]"
        private const string Authority = "https://login.microsoftonline.com/" + Tenant;
    
        // The MSAL Public client app
        private static IPublicClientApplication PublicClientApp;
    
        private static string MSGraphURL = "https://graph.microsoft.com/v1.0/";
        private static AuthenticationResult authResult;
    
        public MainPage()
        {
            this.InitializeComponent();
        }
    
        /// <summary>
        /// Call AcquireTokenAsync - to acquire a token requiring user to sign in
        /// </summary>
        private async void CallGraphButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                // Sign in user using MSAL and obtain an access token for Microsoft Graph
                GraphServiceClient graphClient = await SignInAndInitializeGraphServiceClient(scopes);
    
                // Call the /me endpoint of Graph
                User graphUser = await graphClient.Me.Request().GetAsync();
    
                // Go back to the UI thread to make changes to the UI
                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    ResultText.Text = "Display Name: " + graphUser.DisplayName + "\nBusiness Phone: " + graphUser.BusinessPhones.FirstOrDefault()
                                      + "\nGiven Name: " + graphUser.GivenName + "\nid: " + graphUser.Id
                                      + "\nUser Principal Name: " + graphUser.UserPrincipalName;
                    DisplayBasicTokenInfo(authResult);
                    this.SignOutButton.Visibility = Visibility.Visible;
                });
            }
            catch (MsalException msalEx)
            {
                await DisplayMessageAsync($"Error Acquiring Token:{System.Environment.NewLine}{msalEx}");
            }
            catch (Exception ex)
            {
                await DisplayMessageAsync($"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}");
                return;
            }
        }
                /// <summary>
        /// Signs in the user and obtains an access token for Microsoft Graph
        /// </summary>
        /// <param name="scopes"></param>
        /// <returns> Access Token</returns>
        private static async Task<string> SignInUserAndGetTokenUsingMSAL(string[] scopes)
        {
            // Initialize the MSAL library by building a public client application
            PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
                .WithAuthority(Authority)
                .WithUseCorporateNetwork(false)
                .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
                 .WithLogging((level, message, containsPii) =>
                 {
                     Debug.WriteLine($"MSAL: {level} {message} ");
                 }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
                .Build();
    
            // It's good practice to not do work on the UI thread, so use ConfigureAwait(false) whenever possible.
            IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(false);
            IAccount firstAccount = accounts.FirstOrDefault();
    
            try
            {
                authResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
                                                  .ExecuteAsync();
            }
            catch (MsalUiRequiredException ex)
            {
                // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token
                Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
    
                authResult = await PublicClientApp.AcquireTokenInteractive(scopes)
                                                  .ExecuteAsync()
                                                  .ConfigureAwait(false);
    
            }
            return authResult.AccessToken;
        }
    }
    

Get a user token interactively

The AcquireTokenInteractive method results in a window that prompts users to sign in. Applications usually require users to sign in interactively the first time to access a protected resource. They might also need to sign in when a silent operation to acquire a token fails. An example is when a user's password has expired.

Get a user token silently

The AcquireTokenSilent method handles token acquisitions and renewals without any user interaction. After AcquireTokenInteractive runs for the first time and prompts the user for credentials, use the AcquireTokenSilent method to request tokens for later calls. That method acquires tokens silently. Microsoft Authentication Library handles token cache and renewal.

Eventually, the AcquireTokenSilent method fails. Reasons for failure include a user that signed out or changed their password on another device. When Microsoft Authentication Library detects that the issue requires an interactive action, it throws an MsalUiRequiredException exception. Your application can handle this exception in two ways:

  • Your application calls AcquireTokenInteractive immediately. This call results in prompting the user to sign in. Normally, use this approach for online applications where there's no available offline content for the user. The sample generated by this guided setup follows the pattern. You see it in action the first time you run the sample.

    Because no user has used the application, accounts.FirstOrDefault() contains a null value, and throws an MsalUiRequiredException exception.

    The code in the sample then handles the exception by calling AcquireTokenInteractive. This call results in prompting the user to sign in.

  • Your application presents a visual indication to users that they need to sign in. Then they can select the right time to sign in. The application can retry AcquireTokenSilent later. Use this approach when users can use other application functionality without disruption. An example is when offline content is available in the application. In this case, users can decide when they want to sign in. The application can retry AcquireTokenSilent after the network was temporarily unavailable.

Instantiate the Microsoft Graph Service Client by obtaining the token from the SignInUserAndGetTokenUsingMSAL method

Add the following new method to MainPage.xaml.cs:

      /// <summary>
     /// Sign in user using MSAL and obtain a token for Microsoft Graph
     /// </summary>
     /// <returns>GraphServiceClient</returns>
     private async static Task<GraphServiceClient> SignInAndInitializeGraphServiceClient(string[] scopes)
     {
         GraphServiceClient graphClient = new GraphServiceClient(MSGraphURL,
             new DelegateAuthenticationProvider(async (requestMessage) =>
             {
                 requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", await SignInUserAndGetTokenUsingMSAL(scopes));
             }));

         return await Task.FromResult(graphClient);
     }

More information on making a REST call against a protected API

In this sample application, the GetGraphServiceClient method instantiates GraphServiceClient by using an access token. Then, GraphServiceClient is used to get the user's profile information from the me endpoint.

Add a method to sign out the user

To sign out the user, add the following method to MainPage.xaml.cs:

/// <summary>
/// Sign out the current user
/// </summary>
private async void SignOutButton_Click(object sender, RoutedEventArgs e)
{
    IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(false);
    IAccount firstAccount = accounts.FirstOrDefault();

    try
    {
        await PublicClientApp.RemoveAsync(firstAccount).ConfigureAwait(false);
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            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}";
        }
    }

Note

MSAL.NET uses asynchronous methods to acquire tokens or manipulate accounts. You need to support UI actions in the UI thread. This is the reason for the Dispatcher.RunAsync call and the precautions to call ConfigureAwait(false).

More information about signing out

The SignOutButton_Click method removes the user from the Microsoft Authentication Library user cache. This method effectively tells Microsoft Authentication Library to forget the current user. A future request to acquire a token succeeds only if it's interactive.

The application in this sample supports a single user. Microsoft Authentication Library supports scenarios where the user can sign in on more than one account. An example is an email application where a user has several accounts.

Display basic token information

Add the following method to MainPage.xaml.cs to display basic information about the token:

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

More information

ID tokens acquired by using OpenID Connect also contain a small subset of information pertinent to the user. DisplayBasicTokenInfo displays basic information contained in the token. This information includes the user's display name and ID. It also includes the expiration date of the token and the string that represents the access token itself. If you select the Call Microsoft Graph API button several times, you'll see that the same token was reused for later requests. You can also see the expiration date extended when Microsoft Authentication Library decides it's time to renew the token.

Display message

Add the following new method to MainPage.xaml.cs:

/// <summary>
/// Displays a message in the ResultText. Can be called from any thread.
/// </summary>
private async Task DisplayMessageAsync(string message)
{
     await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
         () =>
         {
             ResultText.Text = message;
         });
     }

Register your application

Now you need to register your application:

  1. Sign in to the Azure portal.
  2. Select Azure Active Directory > App registrations.
  3. Select New registration. Enter a meaningful application name that will be displayed to users of the app, for example, UWP-App-calling-MSGraph.
  4. Under Supported account types, select Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox). Then select Register to continue.
  5. On the overview page, find the Application (client) ID value and copy it. Go back to Visual Studio, open MainPage.xaml.cs, and replace the value of ClientId with this value.

Configure authentication for your application:

  1. Back in the Azure portal, under Manage, select Authentication.
  2. In the Redirect URIs | Suggested Redirect URIs for public clients (mobile, desktop) section, check https://login.microsoftonline.com/common/oauth2/nativeclient.
  3. Select Save.

Configure API permissions for your application:

  1. Under Manage, select API permissions.
  2. Select Add a permission, and then make sure that you've selected Microsoft APIs.
  3. Select Microsoft Graph.
  4. Select Delegated permissions, search for User.Read, and verify that User.Read is selected.
  5. If you made any changes, select Add permissions to save them.

Enable integrated authentication on federated domains (optional)

To enable Integrated Windows authentication when it's used with a federated Azure AD domain, the application manifest must enable additional capabilities. Go back to your application in Visual Studio.

  1. Open Package.appxmanifest.

  2. Select Capabilities, and enable the following settings:

    • Enterprise Authentication
    • Private Networks (Client & Server)
    • Shared User Certificates

Important

Integrated Windows authentication isn't configured by default for this sample. Applications that request Enterprise Authentication or Shared User Certificates capabilities require a higher level of verification by the Windows Store. Also, not all developers want to perform the higher level of verification. Enable this setting only if you need Integrated Windows authentication with a federated Azure AD domain.

Alternate approach to using WithDefaultRedirectURI()

In the current sample, the WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient") method is used. To use WithDefaultRedirectURI(), complete these steps:

  1. In MainPage.XAML.cs, replace WithRedirectUri with WithDefaultRedirectUri:

    Current code

    
    PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
        .WithAuthority(Authority)
        .WithUseCorporateNetwork(false)
        .WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
        .WithLogging((level, message, containsPii) =>
         {
             Debug.WriteLine($"MSAL: {level} {message} ");
         }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
        .Build();
    
    

    Updated code

    
    PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
        .WithAuthority("https://login.microsoftonline.com/common")
        .WithUseCorporateNetwork(false)
        .WithDefaultRedirectUri()
        .WithLogging((level, message, containsPii) =>
         {
             Debug.WriteLine($"MSAL: {level} {message} ");
         }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
        .Build();
    
  2. Find the callback URI for your app by adding the redirectURI field in MainPage.xaml.cs and setting a breakpoint on it:

    
    public sealed partial class MainPage : Page
    {
            ...
    
            string redirectURI = Windows.Security.Authentication.Web.WebAuthenticationBroker
                                .GetCurrentApplicationCallbackUri().ToString();
            public MainPage()
            {
                ...
            }
           ...
    }
    
    

    Run the app, and then copy the value of redirectUri when the breakpoint is hit. The value should look something similar to the following value:
    ms-app://s-1-15-2-1352796503-54529114-405753024-3540103335-3203256200-511895534-1429095407/

    You can then remove the line of code because it's required only once, to fetch the value.

  3. In the app registration portal, add the returned value in RedirectUri in the Authentication pane.

Test your code

To test your application, select the F5 key to run your project in Visual Studio. Your main window appears:

Application's user interface

When you're ready to test, select Call Microsoft Graph API. Then use an Azure AD organizational account or a Microsoft account, such as live.com or outlook.com, to sign in. The first time a user runs this test, the application displays a window asking the user to sign in.

The first time you sign in to your application, a consent screen appears similar to the following image. Select Yes to explicitly consent to access:

Access consent screen

Expected results

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

API Call Results screen

You also see basic information about the token acquired via AcquireTokenInteractive or AcquireTokenSilent in the Token Info box:

Property Format Description
Username user@domain.com The username that identifies the user.
Token Expires DateTime The time when the token expires. Microsoft Authentication Library extends the expiration date by renewing the token as necessary.

More information about scopes and delegated permissions

The Microsoft Graph API requires the user.read scope to read a user's profile. This scope is added by default in every application that's registered in the Application Registration Portal. Other APIs for Microsoft Graph and custom APIs for your back-end server might require additional scopes. For instance, the Microsoft Graph API requires the Calendars.Read scope to list the user's calendars.

To access the user's calendars in the context of an application, add the Calendars.Read delegated permission to the application registration information. Then add the Calendars.Read scope to the acquireTokenSilent call.

Note

Users might be prompted for additional consents as you increase the number of scopes.

Known issues

Issue 1

You receive one of the following error messages when you sign in on your application on a federated Azure AD domain:

  • "No valid client certificate found in the request."
  • "No valid certificates found in the user's certificate store."
  • "Try again choosing a different authentication method."

Cause: Enterprise and certificate capabilities aren't enabled.

Solution: Follow the steps in Enable integrated authentication on federated domains (optional).

Issue 2

You enable integrated authentication on federated domains and try to use Windows Hello on a Windows 10 computer to sign in to an environment with multifactor authentication configured. The list of certificates appears. If you choose to use your PIN, the PIN window never appears.

Cause: This issue is a known limitation of the web authentication broker in UWP applications that run on Windows 10 desktops. It works fine on Windows 10 Mobile.

Workaround: Select Sign in with other options. Then select Sign in with a username and password. Select Provide your password. Then go through the phone authentication process.

Help and support

If you need help, want to report an issue, or would like to learn about your support options, see Help and support for developers.