API Azure AD Auth, Angular и .NET с Graph: исправление MSAL.UiRequiredException?

Я столкнулся со следующей ситуацией: у меня есть две регистрации приложений в Azure AD — веб-приложение Angular 17 и REST API с .NET 6. Я настроил аутентификацию/авторизацию через Azure AD. Приложение Angular вызывает API с токеном доступа через MSAL, и все работает как задумано.

Недавно я расширил свой API клиентом Graph, чтобы составить электронное письмо Outlook и вернуть ссылку, которую Angular может использовать для открытия Outlook и отображения электронного письма.

Затем пользователь может просмотреть, вручную настроить и отправить электронное письмо. Для этого я расширил регистрацию приложения API в Azure, добавив область «Mail.ReadWrite» и реализовав ее на C# следующим образом:

  string clientId = _configuration["AzureAd:ClientId"];
  string clientSecret = _configuration["AzureAd:ClientSecret"];
  string tenantId = _configuration["AzureAd:TenantId"];

  string[] scopes = { "Mail.ReadWrite" };

  var options = new OnBehalfOfCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud };
  var onBehalfOfCredential = new OnBehalfOfCredential(tenantId, clientId, clientSecret, userAccessToken, options);

      var graphClient = new GraphServiceClient(onBehalfOfCredential, scopes); 
    
      var requestBody = new Message
      {
          Subject = $"Test",
          Body = new ItemBody
          {
              ContentType = BodyType.Html,
              Content = "Sehr geehrte Damen und Herren,<br/><br/>" +
                        "Test"
          },
          ToRecipients = new List<Recipient>
          {
              new() { EmailAddress = new EmailAddress { Address = sendDocumentsRequest.Receiver } }
          },
          From = new Recipient { EmailAddress = new  EmailAddress { Address = sendDocumentsRequest.Sender } }
      };
    
      // 1. Step: Create E-Mail in drafts folder
      var resultMessage = await graphClient.Me.Messages.PostAsync(requestBody);

и это отлично работает для меня.

Однако все остальные пользователи получают следующее сообщение об ошибке:

2024-03-20 08:28:50.578 +01:00 [ERR] Ошибка при обработке документы с идентификатором сеанса XXXXX Azure.Identity.AuthenticationFailedException: OnBehalfOfCredential аутентификация не удалась: AADTS65001: Пользователь или администратор не выполнил согласился использовать приложение с идентификатором «XXXXXX» и именем «API_DEV». Отправьте интерактивный запрос авторизации для этого пользователя и ресурса. ---> MSAL.NetCore.4.59.0.0.MsalUiRequiredException: ErrorCode:valid_grant Microsoft.Identity.Client.MsalUiRequiredException: AADTS65001: Пользователь или администратор не дал согласия на использование приложение с идентификатором «XXXX» и именем «XXXX». Отправить интерактивный запрос авторизации для этого пользователя и ресурса. Microsoft.Identity.Client.Internal.Requests.RequestBase.HandleTokenRefreshErrorAsync(MsalServiceException) е, MsalAccessTokenCacheItem кэшированныйAccessTokenItem) в Microsoft.Identity.Client.Internal.Requests.OnBehalfOfRequest.ExecuteAsync(CancellationToken отменуToken) на Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken отменуToken) на Microsoft.Identity.Client.ApiConfig.Executors.ConfidentialClientExecutor.ExecuteAsync(AcquireTokenCommonParameters CommonParameters, AcquireTokenOnBehalfOfParameters onBehalfOfParameters, CancellationToken cancelToken) в Azure.Identity.AbstractAcquireTokenParameterBuilderExtensions.ExecuteAsync[T](AbstractAcquireTokenParameterBuilder`1 строитель, Boolean async, CancellationToken cancelToken) в Azure.Identity.MsalConfidentialClient.AcquireTokenOnBehalfOfCoreAsync(String[] области, строка tenantId, UserAssertion userAssertionValue, логическое значение EnableCae, Boolean async, CancellationToken cancelToken) в Azure.Identity.MsalConfidentialClient.AcquireTokenOnBehalfOfAsync(String[] области, строка tenantId, UserAssertion userAssertionValue, логическое значение EnableCae, Boolean async, CancellationToken cancelToken) в Azure.Identity.OnBehalfOfCredential.GetTokenInternalAsync(TokenRequestContext requestContext, Boolean async, CancellationToken cancelToken) Код статуса: 400

Они не получают запрос на согласие. Я не уверен, подтвердил ли я это согласие в модульном тесте. Пользователи, использующие приложение, распределяются по группам, связанным с приложением. Похоже, проблема связана с MSAL.NetCore.4.59.0.0.MsalUiRequiredException. Как я могу запрограммировать звонок по-другому? Как мне решить эту проблему?

=> AdminConsentRequired = нет => Документация Microsoft Learn.microsoft.com/de-de/graph/permissions-reference

Не могли бы вы включить API permissions изображение портала ваших приложений, отредактировав свой вопрос?

Sridevi 20.03.2024 09:52

Проверьте, помогает ли это stackoverflow.com/questions/72419373/…

Sridevi 20.03.2024 10:08

@Sridevi Спасибо за ответ. Я добавил разрешения API.

Enis 20.03.2024 10:17
Learn.microsoft.com/de-de/graph/permissions-reference В документации Microsoft указано, что для «Mail.ReadWrite» «AdminConsentRequired» с делегированием — «Нет». Почему я должен его менять? Я хочу, чтобы пользователь это подтвердил.
Enis 20.03.2024 10:24
They don't get a consent prompt. -> Это связано с добавлением нового разрешения API. Запрос согласия пользователя появится, когда пользователь, не соглашающийся, попытается войти в ваше приложение. Боюсь, вам следует попросить пользователей, у которых возникла эта проблема, выйти из системы и войти снова и посмотреть, появилось ли приглашение или нет. По сути, после того, как проблемные пользователи дадут согласие на использование приложения, они смогут нормально работать.
Tiny Wang 20.03.2024 10:32

Если вы пытаетесь отправить почту, разрешение «Mail.ReadWrite» не будет работать. Вам необходимо назначить Mail.Send разрешение.

Sridevi 20.03.2024 10:32

@sridevi: «Недавно я расширил свой API клиентом Graph, чтобы составить черновое электронное письмо Outlook и вернуть ссылку, которую Angular может использовать для открытия Outlook и отображения электронного письма. Затем пользователь может просмотреть, вручную настроить и отправить электронное письмо». => Область правильная и работает для меня как пользователя, но, как уже упоминалось, для других она не работает.

Enis 20.03.2024 10:37

Не могли бы вы подтвердить, есть ли у вас какие-либо роли администратора в рамках арендатора?

Sridevi 20.03.2024 10:38

@tiny wang: Вы уверены, что диалоговое окно отображается, хотя я ничего не менял в клиенте Angular, а вызов GraphClient происходит в API?"

Enis 20.03.2024 10:39

в любом случае нам нужно попробовать, потому что вы не желаете давать согласие администратора, верно? А пока можем ли мы вызвать Graph API внутри приложения Angular?

Tiny Wang 20.03.2024 10:41

@TinyWang Да, в моем случае согласие администратора давать не следует... Потому что пользователь должен вводить согласие индивидуально... И, по мнению Microsoft, это также должно быть по умолчанию (см. изображение, которое я добавил к запросу) = > Я предполагаю, что проблема в том, что этот диалог не может автоматически отображаться в Angular?

Enis 20.03.2024 10:52

@TinyWang Я могу редактировать регистрацию приложения и иметь права администратора для нее, но не для организации.

Enis 20.03.2024 10:53

Не могли бы вы подтвердить, включена ли эта опция в вашем клиенте i.imgur.com/qnkIZEt.png?

Sridevi 20.03.2024 10:56

прежде всего, согласие пользователя должно работать в приложении Angular, однако, поскольку у вас разные регистрации приложений (mail.readwrite), разрешение API добавляется для приложения API, но не для входа в Angular, так что я' Я не уверен, повлияет ли это на запрос на получение гранта. Если вы уже протестировали это, но безуспешно, то, боюсь, у нас это не сработает. Возможно, мы можем попытаться использовать только одну регистрацию приложения, если согласие администратора для нас не вариант.

Tiny Wang 20.03.2024 11:00

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

Tiny Wang 20.03.2024 11:01

@Sridevi да, он включен + Разрешить согласие владельца группы для всех владельцев групп. Все владельцы групп могут разрешать приложениям доступ к данным групп, которыми они владеют. Включено.

Enis 20.03.2024 11:17

@Sridevi и tinyWang Спасибо за вашу поддержку. Теперь мы решили авторизовать всех пользователей через нашего администратора Azure, что сократило этот процесс. Впоследствии приложение Angular может открывать Outlook на основе вошедших в систему пользователей.

Enis 21.03.2024 09:14
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
17
202
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Теперь мы решили авторизовать всех пользователей через администратора, что сократило процесс. Впоследствии приложение может открывать Outlook на основе вошедших в систему пользователей.

Ответ принят как подходящий

Ошибка обычно возникает, если вы пропустили предоставление согласия на добавленное разрешение в API или клиентских приложениях.

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

Чтобы устранить ошибку, обязательно дайте согласие на добавленные разрешения либо в App registration, либо в Enterprise application, где администратор проверит разрешения и предоставит их соответствующим образом.

В моем случае я предоставил согласие администратора на добавление разрешения в приложении API следующим образом:

Когда я запустил приведенный ниже код в своей среде после предоставления согласия, я получил такой ответ:

using Azure.Identity;
using Microsoft.Graph;
using Microsoft.Graph.Me.SendMail;
using Microsoft.Graph.Models;

namespace GraphSendMail
{
    class Program
    {

        static async Task Main(string[] args)
        {
            try
            {
                string clientId = "appId";
                string clientSecret = "secret";
                string tenantId = "tenantId";
                string userAccessToken = "token"; // Provide user access token here

                string[] scopes = { "Mail.ReadWrite" };

                var options = new OnBehalfOfCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud };
                var onBehalfOfCredential = new OnBehalfOfCredential(tenantId, clientId, clientSecret, userAccessToken, options);

                var graphClient = new GraphServiceClient(onBehalfOfCredential, scopes);

                var requestBody = new SendMailPostRequestBody
                {
                    Message = new Message
                    {
                        Subject = "Test",
                        Body = new ItemBody
                        {
                            ContentType = BodyType.Html,
                            Content = "Sehr geehrte Damen und Herren,<br/><br/>" +
                                      "Test"
                        },
                        ToRecipients = new List<Recipient>
                        {
                            new Recipient
                            {
                                EmailAddress = new EmailAddress
                                {
                                    Address = "[email protected]" // Provide recipient email here
                                }
                            }
                        },
                        CcRecipients = new List<Recipient>
                        {
                            new Recipient
                            {
                                EmailAddress = new EmailAddress
                                {
                                    Address = "[email protected]" // Provide cc email here
                                }
                            }
                        }
                                            },
                    SaveToSentItems = true
                };

                await graphClient.Me.SendMail.PostAsync(requestBody);

                Console.WriteLine("Email successfully sent.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An error occurred: {ex.Message}");
            }
        }
    }
}

Ответ:

Чтобы убедиться в этом, я проверил то же самое в Outlook пользователя, где почта была успешно отправлена ​​следующим образом:

Если вы предпочитаете, чтобы администратор авторизовал всех пользователей, проверив разрешения, вы можете проверить это Enterprise application следующим образом:

Другие вопросы по теме