Я пытаюсь подключиться к Postman к Dynamics365 CRM REST API. Хотя я успешно получаю токен без входа в систему, используя учетные данные клиента Grant Type, я получаю ошибку 401 при выполнении образца GET для API. Однако мое консольное приложение работает успешно и не предлагает пользователю войти в систему (я не хочу, чтобы появлялось приглашение для входа).
У меня есть: 1. Зарегистрировал приложение в Azure AD, 2. Создал секрет клиента 3. Создал пользователя приложения в Dynamics и связал через идентификатор приложения с приложением из шага 1.
Я сделал это с двумя разными приложениями и двумя разными пользователями приложений и получил тот же результат в Postman, то есть токен получен, но ошибка 401 при GET.
Консольное приложение ниже работает с теми же учетными данными, будет признателен за любой вклад в то, что отсутствует в конфигурации Postman.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net.Http.Headers;
using System.Net.Http;
using Newtonsoft.Json.Linq;
namespace ConsoleApp8
{
class Program
{
static void Main(string[] args)
{
const string ResourceId = "https://myurldev.crm4.dynamics.com/api/data/v9.1";
Task<AuthenticationResult> t = GetUserOAuthToken();
t.Wait();
string accessToken = t.Result.AccessToken;
Console.WriteLine("ACCESS TOKEN \n\n" + accessToken);
Console.WriteLine("\n\n Please any key to display content of the blob");
//Console.ReadKey();
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(ResourceId);
httpClient.Timeout = new TimeSpan(0, 2, 0); // 2 minutes
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
//Send the Get Incident request to the Web API using a GET request.
var response = httpClient.GetAsync("/incidents",
HttpCompletionOption.ResponseHeadersRead).Result;
if (response.IsSuccessStatusCode)
{
//Get the response content and parse it.
JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
string title = (string)body["title"];
Console.WriteLine("Incident title is : {0}", title);
}
else
{
Console.WriteLine("The request failed with a status of '{0}'",
response.ReasonPhrase);
}
}
/*
// Use the access token to create the storage credentials.
TokenCredential tokenCredential = new TokenCredential(accessToken);
StorageCredentials storageCredentials = new StorageCredentials(tokenCredential);
// Create a block blob using those credentials
CloudBlockBlob blob = new CloudBlockBlob(new Uri("https://placeholderURL/SalesOrder.json"), storageCredentials);
using (var stream = blob.OpenRead())
{
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream)
{
Console.WriteLine(reader.ReadLine());
}
}
}
Console.WriteLine("\n\n Please any key to terminate the program");
Console.ReadKey();*/
}
static async Task<AuthenticationResult> GetUserOAuthToken()
{
const string ResourceId = "https://myurldev.crm4.dynamics.com/";
const string AuthInstance = "https://login.microsoftonline.com/{0}/";
const string TenantId = "XXXX-DYNAMICS_TENANT_ID-XXXXX"; // Tenant or directory ID
// Construct the authority string from the Azure AD OAuth endpoint and the tenant ID.
string authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, AuthInstance, TenantId);
AuthenticationContext authContext = new AuthenticationContext(authority);
ClientCredential cc = new ClientCredential("XXXX_APPLICATION_ID_XXXX", "XXXXX_CLIENT_SECRET_XXXX");
// Acquire an access token from Azure AD.
AuthenticationResult result = await authContext.AcquireTokenAsync(ResourceId, cc);
return result;
}
}
}
Да, я добавил разрешение API олицетворения пользователя в AD. Я также назначил пользовательскую роль безопасности пользователю приложения в Dynamics. Консольное приложение правильно подключается и возвращает данные. Это просто Почтальон терпит неудачу.
Проверьте свой токен в jwt-декодер и убедитесь, что у вас есть соответствующее разрешение, например это.
Спасибо за ваш вклад. Я обновил исходный вопрос снимком экрана с разрешениями и анализом токенов в декодере jwt (хотя я не совсем уверен, как интерпретировать ответ).
Привет @Tony Ju, спасибо за ваш ответ и комментарий. Я считаю, что авторизация делегированных пользователей в Dynamics не является проблемой, поскольку консольное приложение может возвращать результаты с теми же учетными данными. Я также могу вручную создать POST / GET, который получает токен и может извлекать информацию. Я считаю, что проблема в том, что почтальон не отправляет необходимый идентификатор ресурса в запросе токена. Как только я подтвержу, я обновлю вопрос с разрешением. Еще раз спасибо за ваш вклад.
Кажется, что Dynamics CRM поддерживает только делегированное разрешение, которые позволяют приложению получать доступ к Common Data Service выступая в роли пользователей в организации. Это означает, что учетные данные клиента здесь не подходят.
Однако вы сказали, что можете использовать учетные данные клиента в консольном приложении. Вы можете попробовать с запросом ниже, чтобы получить токен доступа.
Спасибо за отзыв. Разрешения делегирования — это единственный доступный в настоящее время вариант, и из-за ограничений вызывающего приложения и желания не показывать всплывающее окно для входа в систему это лучший вариант для нас. Я принял ваше замечание о добавлении ресурса в тело запроса токена, и это сработало, так что большое спасибо за вашу помощь!
После того, как вы зарегистрировали свое приложение, я полагаю, вы дали приложению роли безопасности?? Кроме того, вы используете правильную среду, в которой вы предоставили данные доступа с помощью своего токена доступа.