Обработка ошибок и исключений в MSAL.NET

В этой статье представлен обзор различных типов ошибок и рекомендаций по обработке распространенных ошибок входа.

Основы обработки ошибок MSAL

Исключения в Microsoft Authentication Library (MSAL) предназначены для разработчиков приложений для устранения неполадок, а не для отображения конечным пользователям. Сообщения об исключениях не локализованы.

При обработке исключений и ошибок можно использовать сам тип исключения и код ошибки для различения исключений. Список кодов ошибок см. в разделе Microsoft Entra коды ошибок проверки подлинности и авторизации.

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

В следующем разделе содержатся дополнительные сведения об обработке ошибок для приложения.

Обработка ошибок в MSAL.NET

Типы исключений

MsalClientException возникает, когда сама библиотека обнаруживает состояние ошибки, например плохую конфигурацию.

MsalServiceException возникает, когда поставщик удостоверений (Microsoft Entra ID) возвращает ошибку. Это перевод ошибки сервера.

MsalUIRequiredException является типом MsalServiceException и указывает, что взаимодействие с пользователем требуется. Например, если требуется многофакторная проверка подлинности (MFA) или когда пользователь изменяет пароль и маркер не может быть получен автоматически.

Обработка исключений

При обработке исключений .NET можно использовать сам тип исключения и ErrorCode член для различения исключений. ErrorCode значения — константы типа MsalError.

Вы также можете просмотреть поля MsalClientException, MsalServiceException и MsalUIRequiredException.

Если возникает ошибка MsalServiceException , попробуйте использовать коды ошибок проверки подлинности и авторизации , чтобы узнать, указан ли этот код.

Если вызывается исключение MsalUIRequiredException, это означает, что необходимо выполнить интерактивный поток, чтобы пользователь мог устранить проблему. В клиентских приложениях общего доступа, таких как настольное и мобильное приложение, эта проблема решается вызовом AcquireTokenInteractive, при котором открывается браузер. В конфиденциальных клиентских приложениях веб-приложения должны перенаправить пользователя на страницу авторизации, а веб-API должны возвращать код состояния HTTP и заголовок, указывающий на сбой проверки подлинности (401 Неавторизованный и заголовок WWW-Authenticate).

Распространенные исключения .NET

Ниже приведены распространённые исключения, которые могут возникать, и некоторые возможные способы их предотвращения или устранения.

Исключение Код ошибки Смягчение последствий
MsalUiRequiredException AADSTS65001: пользователь или администратор не предоставил согласие на использование приложения с идентификатором "{appId}" с именем "{appName}". Отправьте интерактивный запрос авторизации для этого пользователя и ресурса. Сначала получите согласие пользователя. Если вы не используете .NET Core (который не имеет веб-интерфейса), вызовите AcquireTokenInteractive только один раз. Если вы используете .NET Core или не хотите выполнять AcquireTokenInteractive, пользователь может перейти по URL-адресу, чтобы дать согласие: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read для вызова AcquireTokenInteractive: app.AcquireTokenInteractive(scopes).WithAccount(account).WithClaims(ex.Claims).ExecuteAsync();
MsalUiRequiredException AADSTS50079. Пользователю требуется использовать многофакторную проверку подлинности (MFA). Нет никаких мер по устранению рисков. Если MFA настроена для вашего клиента и Microsoft Entra ID решает применить его, вернитесь к интерактивному потоку, напримерAcquireTokenInteractive.
MsalServiceException AADSTS90010. Тип предоставления не поддерживается для конечных точек /common или /consumers . Используйте конечную точку /organizations или конечную точку для конкретного клиента. Вы использовали /common. Как описано в сообщении из Microsoft Entra ID, центр должен иметь клиент или в противном случае /организации.
MsalServiceException AADSTS70002: текст запроса должен содержать следующий параметр: client_secret or client_assertion Это исключение может возникать, если ваше приложение не было зарегистрировано в Microsoft Entra ID как общедоступное клиентское приложение. В центре администрирования Microsoft Entra измените манифест для вашего приложения и установите для allowPublicClient значение true.
MsalClientException unknown_user Message: не удалось определить пользователя, вошедшего в систему Библиотеке не удалось получить сведения о текущем вошедшем в систему пользователе Windows, либо этот пользователь не присоединён к Active Directory или Microsoft Entra (пользователи, присоединённые к рабочему месту, не поддерживаются). Способ устранения: реализуйте собственную логику, чтобы получать имя пользователя (например, john@contoso.com), и используйте вариант AcquireTokenByIntegratedWindowsAuth, который принимает имя пользователя.
MsalClientException интегрированная_проверка_подлинности_windows_не_поддерживается_для_управляемого_пользователя Этот метод использует протокол, предоставляемый Active Directory (AD). Если пользователь был создан в Microsoft Entra ID без привязки к AD («управляемый» пользователь), выполнение этого метода завершается ошибкой. Пользователи, созданные в AD и поддерживаемые Microsoft Entra ID (федеративные пользователи), могут воспользоваться этим неинтерактивным методом проверки подлинности. Устранение рисков. Используйте интерактивную проверку подлинности.

MsalUiRequiredException

Одним из распространённых кодов состояния, возвращаемых MSAL.NET при вызове AcquireTokenSilent(), является MsalError.InvalidGrantError. Этот код состояния означает, что приложение должно снова вызывать библиотеку проверки подлинности, но в интерактивном режиме (AcquireTokenInteractive или AcquireTokenByDeviceCodeFlow для общедоступных клиентских приложений возникает проблема в веб-приложениях). Это связано с тем, что прежде чем можно будет выдать токен аутентификации, требуется дополнительное взаимодействие с пользователем.

В большинстве случаев сбой AcquireTokenSilent происходит из-за того, что в кэше токенов отсутствуют токены, соответствующие вашему запросу. Срок действия токена доступа истекает через 1 час, и AcquireTokenSilent пытается получить новый, используя токен обновления (в терминологии OAuth2 это поток "Refresh Token"). Этот поток также может завершиться с ошибкой по различным причинам, например, если администратор арендатора настраивает более строгие политики входа в систему.

Взаимодействие нацелено на то, чтобы пользователь делал действие. Некоторые из этих проблем пользователи могут легко устранить (например, принять условия использования одним щелчком мыши), а некоторые невозможно устранить при текущей конфигурации (например, соответствующий компьютер нужно подключить к определённой корпоративной сети). Некоторые помогут пользователю настроить многофакторную проверку подлинности или установить Microsoft Authenticator на своем устройстве.

MsalUiRequiredException перечисление классификации

MSAL предоставляет Classification поле, которое можно прочитать, чтобы обеспечить лучший пользовательский интерфейс. Например, чтобы сообщить пользователю, что срок действия пароля истек или что он должен предоставить согласие на использование некоторых ресурсов. Поддерживаемые значения входят в состав перечисления UiRequiredExceptionClassification:

Classification Значение Рекомендуемая обработка
BasicAction Условие может быть разрешено взаимодействием пользователя во время интерактивного потока проверки подлинности. Вызовите AcquireTokenInteractively().
Дополнительное действие Условие можно устранить путем дополнительного взаимодействия с системой за пределами интерактивного потока проверки подлинности. Вызовите AcquireTokenInteractively(), чтобы отобразить сообщение, объясняющее исправление действия. Вызывающее приложение может скрыть потоки, требующие additional_action, если пользователь вряд ли выполнит корректирующее действие.
Только сообщения Условие не может быть разрешено в настоящее время. Запуск интерактивного потока проверки подлинности отобразит сообщение, объясняющее условие. Вызовите AcquireTokenInteractively(), чтобы отобразить сообщение, объясняющее условие. AcquireTokenInteractively() возвращает ошибку UserCanceled после того, как пользователь считывает сообщение и закрывает окно. Вызывающее приложение может скрывать сценарии, в результате которых используется message_only, если сообщение вряд ли будет полезно пользователю.
Требуется согласие Согласие пользователя отсутствует или было отменено. Вызовите AcquireTokenInteractively(), чтобы пользователь предоставил согласие.
Срок действия пароля пользователя истёк Срок действия пароля пользователя истек. Вызовите AcquireTokenInteractively(), чтобы пользователь смог сбросить пароль.
PromptNeverFailed Интерактивная аутентификация была вызвана с параметром prompt=never, что вынуждает MSAL полагаться на файлы cookie браузера и не отображать окно браузера. Это не удалось. Вызовите AcquireTokenInteractively() без использования Prompt.None
AcquireTokenSilentFailed SDK MSAL не хватает данных, чтобы получить токен из кэша. Это может быть связано с тем, что в кэше нет токенов или учетная запись не была найдена. Сообщение об ошибке содержит дополнительные сведения. Вызовите AcquireTokenInteractively().
Нет Дополнительные сведения не предоставляются. Условие может быть разрешено взаимодействием пользователя во время интерактивного потока проверки подлинности. Вызовите AcquireTokenInteractively().

пример кода .NET

AuthenticationResult res;
try
{
 res = await application.AcquireTokenSilent(scopes, account)
        .ExecuteAsync();
}
catch (MsalUiRequiredException ex) when (ex.ErrorCode == MsalError.InvalidGrantError)
{
 switch (ex.Classification)
 {
  case UiRequiredExceptionClassification.None:
   break;
  case UiRequiredExceptionClassification.MessageOnly:
  // You might want to call AcquireTokenInteractive(). Azure AD will show a message
  // that explains the condition. AcquireTokenInteractively() will return UserCanceled error
  // after the user reads the message and closes the window. The calling application may choose
  // to hide features or data that result in message_only if the user is unlikely to benefit 
  // from the message
  try
  {
      res = await application.AcquireTokenInteractive(scopes).ExecuteAsync();
  }
  catch (MsalClientException ex2) when (ex2.ErrorCode == MsalError.AuthenticationCanceledError)
  {
   // Do nothing. The user has seen the message
  }
  break;

  case UiRequiredExceptionClassification.BasicAction:
  // Call AcquireTokenInteractive() so that the user can, for instance accept terms
  // and conditions

  case UiRequiredExceptionClassification.AdditionalAction:
  // You might want to call AcquireTokenInteractive() to show a message that explains the remedial action. 
  // The calling application may choose to hide flows that require additional_action if the user 
  // is unlikely to complete the remedial action (even if this means a degraded experience)

  case UiRequiredExceptionClassification.ConsentRequired:
  // Call AcquireTokenInteractive() for user to give consent.
  
  case UiRequiredExceptionClassification.UserPasswordExpired:
  // Call AcquireTokenInteractive() so that user can reset their password
  
  case UiRequiredExceptionClassification.PromptNeverFailed:
  // You used WithPrompt(Prompt.Never) and this failed
  
  case UiRequiredExceptionClassification.AcquireTokenSilentFailed:
  default:
  // May be resolved by user interaction during the interactive authentication flow.
  res = await application.AcquireTokenInteractive(scopes)
                         .ExecuteAsync(); break;
 }
}

Проблемы условного доступа и утверждений

При получении токенов в фоновом режиме приложение может получать ошибки, если API, к которому вы пытаетесь получить доступ, требует запрос утверждений Conditional Access, например политику MFA.

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

В некоторых случаях при вызове API, требующего условного доступа, вы можете получить запрос утверждений в сообщении об ошибке API. Например, если политика условного доступа требует, чтобы устройство было управляемым (Intune), ошибка будет примерно такой: AADSTS53000: Ваше устройство должно быть управляемым для доступа к этому ресурсу или аналогичное сообщение. В этом случае можно передать утверждения в вызове получения маркера, чтобы пользователю было предложено выполнить требования соответствующей политики.

При вызове API, требующего условного доступа из MSAL.NET, приложение должно обрабатывать исключения вызовов утверждений. Это отображается как msalServiceException , где свойство Claims не будет пустым.

Чтобы выполнить проверку утверждения, используйте WithClaims(String).

Повторная попытка после ошибок и исключений

При вызове MSAL необходимо реализовать собственные политики повторных попыток. MSAL выполняет http-вызовы к службе Microsoft Entra и иногда могут возникать сбои. Например, сеть может пойти вниз или сервер перегружен.

HTTP 429

Если сервер маркеров службы (STS) перегружен слишком большим количеством запросов, он возвращает ошибку HTTP 429 с указанием времени, пока не сможете повторить попытку в Retry-After поле ответа.

Коды ошибок HTTP 500-600

MSAL.NET реализует простой механизм однократного повтора для ошибок с кодами состояния HTTP 500–600.

MsalServiceException предоставляет System.Net.Http.Headers.HttpResponseHeaders как свойство namedHeaders. Дополнительные сведения из кода ошибки можно использовать для повышения надежности приложений. В описанном случае можно использовать RetryAfter свойство (типа RetryConditionHeaderValue) и вычислить время повтора.

Ниже приведён пример приложения-демона с использованием потока учётных данных клиента. Это можно адаптировать для любого из методов получения токена.


bool retry = false;
do
{
    TimeSpan? delay;
    try
    {
         result = await publicClientApplication.AcquireTokenForClient(scopes, account).ExecuteAsync();
    }
    catch (MsalServiceException serviceException)
    {
         if (serviceException.ErrorCode == "temporarily_unavailable")
         {
             RetryConditionHeaderValue retryAfter = serviceException.Headers.RetryAfter;
             if (retryAfter.Delta.HasValue)
             {
                 delay = retryAfter.Delta;
             }
             else if (retryAfter.Date.HasValue)
             {
                 delay = (retryAfter.Date.Value – DateTimeOffset.Now).TotalMilliseconds;
             }
         }
    }
    // . . .
    if (delay.HasValue)
    {
        Thread.Sleep((int)delay.Value.TotalMilliseconds); // sleep or other
        retry = true;
    }
} while (retry);

Дальнейшие действия

Рассмотрите возможность включения ведения журнала в MSAL.NET для диагностики и отладки проблем.