Руководство по входу пользователей и вызову Microsoft Graph в классическом приложении Windows Presentation Foundation (WPF)

В этом руководстве описано, как создать собственное приложение windows Desktop .NET (XAML), которое входит в систему пользователей и получает маркер доступа для вызова API Microsoft Graph.

После выполнения руководства приложение сможет вызвать защищенный API, использующий личная учетная запись (включая outlook.com, live.com и другие). Приложение также будет использовать рабочие и учебные учетные записи из любой компании или организации, которая использует идентификатор Microsoft Entra.

В этом руководстве рассматриваются следующие темы:

  • создание проекта Windows Presentation Foundation (WPF) в Visual Studio;
  • установка библиотеки аутентификации Майкрософт (MSAL) для .NET;
  • Регистрация приложения
  • добавление кода для поддержки входа и выхода пользователей;
  • добавление кода для вызова API Microsoft Graph;
  • Тестирование приложения

Необходимые компоненты

Как работает пример приложения, созданный в этом руководстве

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

Пример приложения, создаваемого с помощью этого руководства, включает классическое приложение Windows, которое запрашивает API Microsoft Graph или веб-API, принимающий маркеры из конечной точки платформа удостоверений Майкрософт. В этом сценарии маркер добавляется в запросы HTTP с помощью заголовка авторизации. Получение маркера и его обновление выполняется с помощью библиотеки проверки подлинности Майкрософт (MSAL).

Обработка получения токена для доступа к защищенным веб-API

Когда пользователь пройдет аутентификацию, пример приложения получит токен, который может использоваться для запрашивания API Microsoft Graph или веб-API, защищенного с помощью платформы удостоверений Майкрософт.

Программным интерфейсам, таким как Microsoft Graph, для доступа к определенным ресурсам требуется маркер. Например, маркер требуется для чтения профиля пользователя, доступа к календарю пользователя или отправки электронной почты. Приложение может запросить маркер доступа с помощью MSAL, чтобы получить доступ к этим ресурсам, указав определенные области API. Затем этот маркер доступа добавляется в заголовок авторизации HTTP для каждого вызова к защищенному ресурсу.

MSAL управляет кэшированием и обновлением маркеров доступа, поэтому вашему приложению не нужно этого делать.

Пакеты NuGet для Application Insights

В этом руководстве используются следующие пакеты NuGet:

Библиотека Description
Microsoft.Identity.Client Библиотека аутентификации Майкрософт (MSAL)

Настройка проекта

В этом разделе вы создадите новый проект, чтобы продемонстрировать, как интегрировать классическое приложение .NET Для Windows (XAML) с помощью входа в Корпорацию Майкрософт , чтобы приложение могли запрашивать веб-API, требующие маркера.

Приложение, которое вы создадите, отображает кнопку, которая вызовет API Microsoft Graph, область для отображения результатов и кнопку выхода.

Примечание.

Предпочитаете скачать этот пример проекта Visual Studio? Скачайте проект и перейдите к настройке, чтобы настроить пример кода перед его выполнением.

Создайте приложение, выполнив следующие действия:

  1. Запустите Visual Studio
  2. В окне запуска выберите Создание нового проекта.
  3. В раскрывающемся списке "Все языки " выберите C#.
  4. Найдите и выберите шаблон приложения WPF (платформа .NET Framework), а затем нажмите кнопку "Далее".
  5. В поле "Имя проекта" введите имя, например Win-App-calling-MsGraph.
  6. Выберите расположение проекта или примите параметр по умолчанию.
  7. В Платформе выберите платформа .NET Framework 4.8.
  8. Нажмите кнопку создания.

Добавление MSAL в проект

  1. В Visual Studio выберите Сервис>Диспетчер пакетов NuGet>Консоль диспетчера пакетов.

  2. В окне консоли диспетчера пакетов вставьте следующую команду Azure PowerShell:

    Install-Package Microsoft.Identity.Client -Pre
    

Регистрация приложения

Совет

Действия, описанные в этой статье, могут немного отличаться на портале, с который вы начинаете работу.

Чтобы зарегистрировать и настроить приложение, выполните следующие действия.

  1. Войдите в Центр администрирования Microsoft Entra как минимум разработчик приложений.
  2. Если у вас есть доступ к нескольким клиентам, используйте значок Параметры в верхнем меню, чтобы переключиться на клиент, в котором вы хотите зарегистрировать приложение из меню каталогов и подписок.
  3. Перейдите к приложениям> удостоверений>Регистрация приложений.
  4. Выберите Создать регистрацию.
  5. Введите имя приложения, например Win-App-calling-MsGraph. Пользователи приложения могут видеть это имя. Вы можете изменить его позже.
  6. В разделе "Поддерживаемые типы учетных записей" выберите "Учетные записи" в любом каталоге организации (любой каталог Microsoft Entra — Multitenant) и личных учетных записей Майкрософт (например, Skype, Xbox).
  7. Выберите Зарегистрировать.
  8. В разделе Управление выберите Проверка подлинности>Добавить платформу.
  9. Выберите вариант Мобильные и классические приложения.
  10. В разделе URI перенаправления выберите https://login.microsoftonline.com/common/oauth2/nativeclient.
  11. Выберите Настроить.

Добавление кода для инициализации MSAL

На этом шаге вы создадите класс для обработки взаимодействия с MSAL, например обработки маркеров.

  1. Откройте файл App.xaml.cs, а затем добавьте ссылку для MSAL в класс:

    using Microsoft.Identity.Client;
    
  2. Обновите класс app следующим образом:

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

Создание пользовательского интерфейса приложения

В этом разделе описывается, как приложение может запрашивать защищенный внутренний сервер, например Microsoft Graph.

Файл MainWindow.xaml автоматически создается в рамках шаблона проекта. Откройте этот файл и замените узел <Сетка> для своего приложения следующим кодом:

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

Получение маркера для API Microsoft Graph с помощью MSAL

В этом разделе вы получите маркер для API Microsoft Graph с помощью MSAL.

  1. В файле MainWindow.xaml.cs добавьте ссылку для MSAL в класс:

    using Microsoft.Identity.Client;
    
  2. Замените MainWindow код класса следующим кодом:

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

Дополнительные сведения

Интерактивное получение маркера

При вызове метода AcquireTokenInteractive появится окно, в котором пользователю предлагается выполнить вход. Когда пользователь впервые запрашивает доступ к защищенному ресурсу, приложение обычно требует выполнить интерактивный вход. Кроме того, им может потребоваться выполнить вход, когда автоматическая операция получения маркера завершается ошибкой (например, если срок действия пароля пользователя истек).

Автоматическое получение маркера пользователя

Метод AcquireTokenSilent обрабатывает получение и обновление маркера без участия пользователя. После первого выполнения метода AcquireTokenInteractive обычно используется метод AcquireTokenSilent, чтобы получить маркеры для доступа к защищенным ресурсам для последующих вызовов, так как вызовы для запроса или обновления маркеров выполняются автоматически.

В конечном итоге метод может завершиться ошибкой AcquireTokenSilent . Причина может заключаться в том, что пользователь вышел из системы или изменил пароль на другом устройстве. Когда MSAL обнаруживает, что эту проблему можно решить, запросив интерактивное действие, возникает исключение MsalUiRequiredException. Приложение может обработать это исключение двумя способами:

  • Может немедленно вызвать AcquireTokenInteractive. Этот вызов приводит к появлению запроса на вход пользователя. Этот шаблон используется в онлайн-приложениях, где нет доступного автономного содержимого для пользователя. Образец, созданный этой программой установки, следует этому шаблону, который можно увидеть в действии при первом выполнении примера.

  • Так как приложение еще не использовалось, PublicClientApp.Users.FirstOrDefault() будет содержать значение NULL и будет выдано исключение MsalUiRequiredException.

  • Код в примере обработает исключение, вызвав AcquireTokenInteractive, в результате чего пользователю будет предложено войти.

  • Также приложение может визуально уведомить пользователей, что требуется интерактивный вход, чтобы они могли выбрать подходящее время для входа. Либо приложение может попытаться повторно выполнить метод AcquireTokenSilent позже. Этот шаблон часто используется, когда пользователи могут использовать другие функциональные возможности приложений без нарушений. Например, если автономное содержимое доступно в приложении. В этом случае пользователи могут решать, что им нужно сделать после входа — получить доступ к защищенному ресурсу или обновить устаревшие данные. Кроме того, приложение может попытаться повторно выполнить AcquireTokenSilent при восстановлении сети после временной недоступности.

Вызов API Microsoft Graph с помощью полученного маркера

Добавьте в MainWindow.xaml.cs следующий новый метод. Этот метод используется для выполнения запроса GET к API Graph с помощью заголовка авторизации "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();
    }
}

Дополнительные сведения о вызове REST через защищенный API

В этом примере приложения используется метод GetHttpContentWithToken для выполнения HTTP-запроса GET к защищенному ресурсу, требующему маркер, а затем возвращает содержимое вызывающему объекту. Этот метод добавляет полученный маркер в заголовок авторизации HTTP. В этом примере ресурс — это конечная точка me API Microsoft Graph, которая отображает сведения о профиле пользователя.

Добавление метода для выхода пользователя

Добавьте следующий метод в файл 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}";
        }
    }
}

Дополнительные сведения о выходе пользователя

Метод SignOutButton_Click удаляет пользователей из кэша пользователей MSAL, который фактически сообщает MSAL забыть текущего пользователя, чтобы будущий запрос на получение маркера успешно выполнен только в том случае, если он был создан для интерактивного взаимодействия.

Хотя приложение в этом примере поддерживает одного пользователя, MSAL поддерживает сценарии, в которых несколько пользователей могут войти в систему одновременно. Пример — приложение электронной почты, в котором у пользователя есть несколько учетных записей.

Отображение основных сведений о маркере

Для отображения основных сведений о маркере добавьте следующий метод в файл 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;
    }
}

Дополнительные сведения

После входа пользователя MSAL получает идентификатор маркера в дополнение к маркеру доступа, который используется для вызова Microsoft Graph API. Этот маркер содержит небольшое подмножество сведений, относящихся к пользователям. Метод DisplayBasicTokenInfo отображает основные сведения, содержащиеся в маркере. Например, он показывает отображаемое имя и идентификатор пользователя, а также дату истечения срока действия маркера и строку, представляющую сам маркер доступа. Нажав кнопку вызова API Microsoft Graph несколько раз, вы увидите, что для последующих запросов повторно используется тот же маркер. Вы также можете увидеть дату окончания срока действия, когда MSAL решит, что пришло время продлить маркер.

Тестирование кода

Чтобы запустить проект в Visual Studio, нажмите клавишу F5. Отобразится приложение MainWindow, как показано ниже:

Test your application.

При первом запуске приложения и нажатии кнопки Call Microsoft Graph API (Вызов API Graph Microsoft) появится запрос на вход. Используйте учетную запись Microsoft Entra (рабочую или учебную учетную запись) или учетную запись Майкрософт (live.com, outlook.com), чтобы протестировать ее.

Sign in to the application.

Войдя в приложение первый раз, необходимо будет предоставить ему разрешение на использование данных вашего профиля для входа, как показано ниже:

Provide your consent for application access.

Просмотр результатов приложения

После входа вы увидите сведения о профиле пользователя, который возвращается вызовом Microsoft Graph API. Результаты отображаются в поле API Call Results (Результаты вызова API). Основные сведения о токене, полученном при вызове AcquireTokenInteractive или AcquireTokenSilent, должны отображаться в поле Token Info (Сведения о токене). Результаты содержат следующие свойства:

Свойство Формат Description
Username user@domain.com Имя пользователя, которое используется для идентификации пользователя.
Истечение срока действия маркера Дата/время Время окончания срока действия маркера. MSAL продлевает срок действия, по мере необходимости обновляя маркер.

Дополнительные сведения об областях и делегированных разрешениях

Для чтения профиля пользователя API Microsoft Graph требуется область user.read. По умолчанию эта область автоматически добавляется в каждое приложение, зарегистрированное на портале регистрации приложений. Для других API Microsoft Graph, а также для пользовательских API вашего внутреннего сервера, могут потребоваться дополнительные области. Для отображения списка календарей пользователя API Microsoft Graph требуется область Calendars.Read.

Чтобы из контекста приложения получить доступ к календарям пользователя, добавьте делегированное разрешение Calendars.Read в сведения о регистрации приложения. Затем добавьте область Calendars.Read в вызов acquireTokenSilent.

Примечание.

При увеличении количества областей от пользователя могут потребоваться дополнительные согласия.

Справка и поддержка

Если вам нужна помощь, если вы хотите сообщить о проблеме или узнать о доступных вариантах поддержки, воспользуйтесь статьей Возможности получения поддержки и справки для разработчиков.

Следующие шаги

Из нашей серии сценариев узнайте, как создавать классические приложения, вызывающие защищенные веб-API.