Аутентификация Dynamics 365 Web Api с вошедшим в систему пользователем приложения-функции Azure

У меня есть приложение-функция Azure (функция триггера HTTP), которое защищено проверкой подлинности Azure AD.

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

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

Часть, с которой я борюсь, - это получение токена доступа для вызова Dynamics Web API, который соответствует зарегистрированному пользователю.

Первая попытка

Затем я попытался использовать токен текущего зарегистрированного пользователя в соответствии с заголовком "X-MS-TOKEN-AAD-ID-TOKEN". Мой код выглядит следующим образом - он не подключается к веб-API Dynamics - я получаю 401 - Несанкционированный ответ:

[FunctionName("TestCrmFunction")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
    var crmUrl = "https://myorganisation.crm11.dynamics.com";

    // Get the token from the HTTP header of the request:
    var token = req.Headers
        .Where(x => x.Key == "X-MS-TOKEN-AAD-ID-TOKEN")
        .SelectMany(x => x.Value)
        .FirstOrDefault();

    using (var httpClient = new HttpClient())
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

        httpClient.BaseAddress = new Uri(crmUrl);

        // set the token here:
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var response = await httpClient.GetAsync("api/data/v8.1/WhoAmI");

        var content = await response.Content.ReadAsStringAsync();

        if (response.IsSuccessStatusCode)
        {
            // I don't get here...
            return req.CreateResponse(HttpStatusCode.OK, content);
        }
        else
        {
            // ... I get here
            return req.CreateResponse(response.StatusCode, $"ERROR: {content}");
        }
    }

}

Похоже, что "X-MS-TOKEN-AAD-ID-TOKEN" не принимается.

Вторая попытка

Я пытаюсь получить новый токен для пользователя, создавая экземпляр "UserAssertion" вместе с ClientCredential. Бит UserAssertion, в котором я действительно не уверен и, вероятно, ошибочен:

var tenantId = "<my organisation tenantID>";
var authority = $"https://login.microsoftonline.com/{tenantId}";
var authenticationContext = new AuthenticationContext(authority, false);

// create the client credential using the clientID and clientSecret:
var clientId = "<my applicationId>";
var clientSecret = "<key for my application>";
var clientCredential = new ClientCredential(clientId, clientSecret);

// create the user assertion using the access token from the authenticated user:
var accessToken = req.Headers
    .Where(x => x.Key == "X-MS-TOKEN-AAD-ACCESS-TOKEN")
    .SelectMany(x => x.Value)
    .FirstOrDefault();

var userAssertion = new UserAssertion(
    accessToken,
    "urn:ietf:params:oauth:grant-type:jwt-bearer",
    ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn)?.Value);

// get a new token - this step is failing
var crmUrl = "https://myorganisation.crm11.dynamics.com";
AuthenticationResult authenticationResult = 
    await authenticationContext.AcquireTokenAsync(crmUrl, clientCredential, userAssertion);

string token = authenticationResult.AccessToken;

Я получаю ответ:

Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException: AADSTS50027: Invalid JWT token. AADSTS50027: Invalid JWT token. Token format not valid.

Обходной путь - не реальное решение

Единственное, что мне удалось получить - это жестко закодировать имя пользователя / пароль пользователя, с которым я вхожу в систему:

var credentials = new UserPasswordCredential("<my username>", "<my password>");

AuthenticationResult authenticationResult = await 
authenticationContext.AcquireTokenAsync(crmUrl, clientId, credentials);

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

2 ответа

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

Пожалуйста, смотрите мой пост ниже, и если это поможет вашей ситуации, надеюсь, вы можете пометить это как ответ.

Аутентификация приложения-функции Azure для подключения к Dynamics 365 CRM онлайн

Это больше альтернатива, но, вероятно, легче достичь.

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

Выдавать себя за другого пользователя

Выдавать себя за пользователя

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

// Retrieve the system user ID of the user to impersonate.
OrganizationServiceContext orgContext = new OrganizationServiceContext(_serviceProxy);
_userId = (from user in orgContext.CreateQuery<SystemUser>()
          where user.FullName == "Something from your token" //perhaps domain name?
          select user.SystemUserId.Value).FirstOrDefault();

// To impersonate another user, set the OrganizationServiceProxy.CallerId
// property to the ID of the other user.
_serviceProxy.CallerId = _userId;