Intereting Posts
Могу ли я ждать чего-то, если нет метода GetAwaiter? Производительность URL-адреса ASP.NET MVC «Значение не может быть нулевым. Имя параметра: ошибка экземпляра при попытке открыть отчет telerik C #: публичная новая строка ToString () VS public override string ToString () Профилировщик MV MV: отображает время профиля для каждого статического ресурса Запуск консольного приложения C # в качестве службы Windows Возможно ли выполнить несколько SSH-команд из одного сеанса входа в систему с помощью SSH.NET? C # Строка переключателя, начиная с TextBlock размером с заглавную букву (игнорируя заставку / descender шрифта) MongoDB: обновление только определенных полей динамически создавать несколько текстовых полей C # Отображение целого числа с цветом RGB в C # polymorphism для свойств, заданных интерфейсами Сохранить адрес электронной почты при отправке в Outlook Add-In Ожидание async с обработчиком событий

Аутентификация с помощью Dynamics 365 от функции Azure

сценарий

У меня есть организация Dynamics 365 v9, размещенная в Интернете. У меня есть набор функций Azure, размещенных в приложении Azure Function, для другого арендатора для моей организации Dynamics.

Я создал веб-крючки с помощью инструмента регистрации плагинов Dynamics, который при определенных событиях (например, когда контакт создается в Dynamics), данные POST для моих функций Azure через их конечные URL-адреса.

Аутентификация между Dynamics 365 и моими функциями Azure достигается передачей значения x-functions-key в аутентификации HTTP-запроса HttpHeader.

Функции Azure получают данные из события в Dynamics в форме RemoteExecutionContext, который я могу читать, используя следующий код:

 using System.Net; public static async Task Run(HttpRequestMessage req, TraceWriter log) { var jsonContent = await req.Content.ReadAsStringAsync(); log.Info(jsonContent); return req.CreateResponse(HttpStatusCode.OK); } 

Вопрос

Как функция Azure может аутентифицироваться обратно в организации Dynamics 365 для чтения и записи данных?

Что я пробовал

  1. Инструмент Xrm

Самый простой способ аутентификации – использовать CrmServiceClient из Microsoft.Xrm.Tooling.Connector.dll. Однако я не обязательно должен иметь имя пользователя и пароль для создания конструктора CrmServiceClient. Возможно, учетные данные могут быть безопасно переданы через HTTP-запрос POST?

  1. Пользователь приложения

Я пробовал зарегистрировать пользователя приложения в динамике. Я предоставляю клиентский идентификатор и клиентский секрет своим Azure-функциям, но проверка подлинности не выполняется, потому что пользователь находится в другом арендаторе для моих Azure-функций.

Рассмотренные решения

Один объект полученной строки jsonContent называется ParentContext . Возможно, это можно повторно использовать для аутентификации с вызывающей организацией Dynamics.

Марк Швайгерт рекомендовал использовать S2S и предоставил образец для своего репозитория AzureFunctionApp . Если я смогу использовать этот подход для работы, я отправлю решение здесь.

Я бы не подумал, что вы можете разумно использовать «настоящие» учетные данные пользователей для подключения к CRM.

Я бы использовал учетную запись службы для подключения к CRM. Создайте нового пользователя CRM специально для этой цели, если вы сделаете пользователя неинтерактивным, вы не должны использовать лицензию. Затем вы можете использовать учетные данные этой учетной записи службы для подключения к CRM с помощью CrmServiceClient . В качестве альтернативы можно посмотреть аутентификацию Server to Server .

Если вы можете предоставить идентификатор пользователя в своем функциональном приложении, вы используете учетную запись службы, чтобы олицетворять «реальных» пользователей через веб-службы CRM.

Чтобы олицетворять пользователя, перед вызовом веб-методов службы установите свойство CallerId на экземпляр OrganizationServiceProxy.

Мне тоже это интересно, но у меня не было возможности поэкспериментировать с этим.

Для вашего второго варианта вы зарегистрировали заявку и дали согласие в целевой AAD?

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/use-multi-tenant-server-server-authentication

Когда они дадут согласие, ваше зарегистрированное приложение будет добавлено в список приложений Azure AD Enterprise, и оно будет доступно пользователям арендатора Azure AD.

Только после того, как администратор предоставил согласие, вы должны затем создать пользователя приложения в арендаторе Dynamics 365 .

Я полагаю, что корень проблемы доступа связан с Основным объектом службы приложения (объект, локальный для целевого Арендатора)

Основной объект службы

https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-objects#service-principal-object

Для доступа к ресурсам, которые обеспечены арендатором Azure AD, объект, который требует доступа, должен быть представлен руководителем безопасности. Это справедливо как для пользователей (пользователей), так и для приложений (директор службы). Принцип безопасности определяет политику доступа и разрешения для пользователя / приложения в этом арендаторе. Это позволяет использовать основные функции, такие как аутентификация пользователя / приложения во время входа в систему и авторизация во время доступа к ресурсам.

Рассмотрите объект приложения как глобальное представление вашего приложения для использования для всех арендаторов и принципала службы как локальное представление для использования в конкретном арендаторе.

НТН

-Крис

Недавно я сделал что-то подобное, но не полагался на функцию аутентификации подписки Azure для подключения к D365. В моем случае звонки приходили к функциям Azure из других мест, но связь не изменилась. В любом из этих случаев аутентификация НЕ проходит. Если пользователь AAD аутентифицируется в вашем приложении Function, вам все равно необходимо подключиться к D365 с помощью пользователя приложения, а затем выдавать себя за вызывающего вас пользователя.

Во-первых, убедитесь, что приложение, зарегистрированное в Azure AD при регистрации приложений, имеет тип «Веб-приложение / API», а не «Родной». Отредактируйте настройки зарегистрированного приложения и убедитесь в следующем:

  1. Не принимайте ID приложения, о котором я расскажу позже как appId.
  2. В разделе «Доступ к API – требуемые разрешения» добавьте Dynamics CRM Online (Microsoft.CRM) и NOT Dynamics 365.
  3. В разделе «API Access – Keys» создайте ключ с соответствующим истечением срока действия. Вы можете создавать несколько ключей, если у вас есть несколько функций / приложений, которые соединяются обратно как это «приложение». Я буду ссылаться на этот ключ как «clientSecret» позже.

Если опция «Ключи» недоступна, вы зарегистрировали собственное приложение.

Я сохранил appId и clientSecret в разделе конфигурации приложения в приложении Function и получил доступ к ним, используя обычную коллекцию System.Configuration.ConfigurationManager.AppSettings.

В приведенных ниже примерах используется вызов AuthenticationParameters для поиска URL-адресов полномочий и ресурсов, но вы можете так же легко создавать эти URL-адреса вручную, используя бесчисленные примеры онлайн. Я считаю, что это просто обновит себя, если они когда-нибудь изменятся, поэтому меньше работы позже.

Это простые примеры, и я замалчиваю необходимость обновлять токены и все эти вещи.

Затем для доступа к D365 с использованием OData:

 string odataUrl = "https://org.crm6.dynamics.com/api/data/v8.2/"; // trailing slash actually matters string appId = "some-guid"; string clientSecret = "some key"; AuthenticationParameters authArg = AuthenticationParameters.CreateFromResourceUrlAsync(new Uri(odataUrl)).Result; AuthenticationContext authCtx = new AuthenticationContext(authArg.Authority); AuthenticationResult authRes = authCtx.AcquireTokenAsync(authArg.Resource, new ClientCredential(appId, clientSecret)).Result; using (HttpClient client = new HttpClient()) { client.TimeOut = TimeSpan.FromMinutes (2); client.DefaultRequestHeaders.Add("Authorization", authRes.CreateAuthorizationHeader ()); using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, $"{odataUrl}accounts?$select=name&$top=10")) { using (HttpResponseMessage res = client.SendAsync(req).Result) { if (res.IsSuccessStatusCode) { Console.WriteLine(res.Content.ReadAsStringAsync().Result); } else { // cry } } } } 

Если вы хотите получить доступ к D365 с помощью службы «Организация» и LINQ, используйте следующее. Две основные части, на которые мне потребовалось некоторое время, – это формат этого странного URL-адреса организации.svc и использование Microsoft.Xrm.Sdk.WebServiceClient.OrganizationWebProxyClient вместо инструмента:

 string odataUrl = "https://org.crm6.dynamics.com/xrmservices/2011/organization.svc/web?SdkClientVersion=8.2"; // don't question the url, just accept it. string appId = "some-guid"; string clientSecret = "some key"; AuthenticationParameters authArg = AuthenticationParameters.CreateFromResourceUrlAsync(new Uri(odataUrl)).Result; AuthenticationContext authCtx = new AuthenticationContext(authArg.Authority); AuthenticationResult authRes = authCtx.AcquireTokenAsync(authArg.Resource, new ClientCredential(appId, clientSecret)).Result; using (OrganizationWebProxyClient webProxyClient = new OrganizationWebProxyClient(new Uri(orgSvcUrl), false)) { webProxyClient.HeaderToken = authRes.AccessToken; using (OrganizationServiceContext ctx = new OrganizationServiceContext((IOrganizationService)webProxyClient)) { var accounts = (from i in ctx.CreateQuery("account") orderby i["name"] select i).Take(10); foreach (var account in accounts) Console.WriteLine(account["name"]); } } 

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

Используя S2S, вы можете использовать AcquireToken для извлечения носителя

  var clientcred = new ClientCredential(clientId, clientSecret); AuthenticationContext authContext = new AuthenticationContext(aadInstance, false); AuthenticationResult result = authContext.AcquireToken(organizationUrl, clientcred); token = result.AccessToken; ExpireDate = result.ExpiresOn.DateTime; client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);