Проверка подлинности с помощью Dynamics 365 из функции Azure - PullRequest
0 голосов
/ 27 апреля 2018

Сценарий

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

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

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

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

using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    var jsonContent = await req.Content.ReadAsStringAsync();

    log.Info(jsonContent);

    return req.CreateResponse(HttpStatusCode.OK);
}

Вопрос

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

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

  1. Xrm Tooling

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

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

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

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

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

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

Ответы [ 4 ]

0 голосов
/ 10 сентября 2018

Используя 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);
0 голосов
/ 27 апреля 2018

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

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

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

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

0 голосов
/ 11 мая 2018

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

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

  1. Не берите идентификатор приложения, который я буду называть позже appId.
  2. В разделе «Доступ к API - необходимые разрешения» добавьте Dynamics CRM Online (Microsoft.CRM) и NOT Dynamics 365.
  3. В разделе «Доступ к API - ключи» создайте ключ с соответствующим сроком действия. Вы можете создать несколько ключей, если у вас есть несколько функций / приложений, подключающихся обратно, как это «приложение». Я буду называть этот ключ "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 organization.svc и использование Microsoft.Xrm.Sdk.WebServiceClient.OrganizationWebProxyClient вместо Tooling:

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, еще не пробовал, но просто удостоверьтесь, что в заголовке авторизации обычно есть токен-носитель, и два приведенных выше примера вводят его по-разному, поэтому вам следует быть в состоянии соединить то, что нужно отсюда.

0 голосов
/ 27 апреля 2018

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

Для вашего второго варианта вы зарегистрировали приложение и дали согласие в целевом 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, объект, которому требуется доступ, должен быть представлен субъектом безопасности. Это верно как для пользователей (принципал пользователя), так и для приложений (принципал службы). Участник безопасности определяет политику доступа и разрешения для пользователя / приложения в этом клиенте. Это включает основные функции, такие как аутентификация пользователя / приложения при входе в систему и авторизация при доступе к ресурсам.

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

НТН

-Крис

...