未回答の質問への言及:
401- Azure AD で REST API Dynamics CRM を使用した不正な認証
と
Dynamics CRM Online 2016 - デーモン/サーバー アプリケーション Web Api への Azure AD 認証エラー
と
Dynamics CRM 2016 Online Rest API とクライアント資格情報 OAuth フロー
Azure クラウドの Web サービスと Dynamics CRM Online 2016 の間で、ログイン画面なしで通信する必要があります。このサービスには、CRM で CRUD 操作をトリガーする REST API があります (認証も実装します)。
これは「Confidential Client」または「Daemon Server」または単に「Server-to-Server」と呼ばれていると思います
Azure AD でサービスを適切に設定しました (「委任権限 = 組織ユーザーとしてオンラインでダイナミクスにアクセス」、他にオプションはありません)
VS で ASP.NET WEB API プロジェクトを作成し、Azure で WebService を作成し、CRM の Azure AD 内の「アプリケーション」のエントリも作成しました。
私のコードは次のようになります (エンティティタイプと returnValue は無視してください):
public class WolfController : ApiController
{
private static readonly string Tenant = "xxxxx.onmicrosoft.com";
private static readonly string ClientId = "dxxx53-42xx-43bc-b14e-c1e84b62752d";
private static readonly string Password = "j+t/DXjn4PMVAHSvZGd5sptGxxxxxxxxxr5Ki8KU="; // client secret, valid for one or two years
private static readonly string ResourceId = "https://tenantname-naospreview.crm.dynamics.com/";
public static async Task<AuthenticationResult> AcquireAuthentificationToken()
{
AuthenticationContext authenticationContext = new AuthenticationContext("https://login.windows.net/"+ Tenant);
ClientCredential clientCredentials = new ClientCredential(ClientId, Password);
return await authenticationContext.AcquireTokenAsync(ResourceId, clientCredentials);
}
// GET: just for calling the DataOperations-method via a GET, ignore the return
public async Task<IEnumerable<Wolf>> Get()
{
AuthenticationResult result = await AcquireAuthentificationToken();
await DataOperations(result);
return new Wolf[] { new Wolf() };
}
private static async Task DataOperations(AuthenticationResult authResult)
{
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(ResourceId);
httpClient.Timeout = new TimeSpan(0, 2, 0); //2 minutes
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
Account account = new Account();
account.name = "Test Account";
account.telephone1 = "555-555";
string content = String.Empty;
content = JsonConvert.SerializeObject(account, new JsonSerializerSettings() {DefaultValueHandling = DefaultValueHandling.Ignore});
//Create Entity/////////////////////////////////////////////////////////////////////////////////////
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "api/data/v8.1/accounts");
request.Content = new StringContent(content);
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
HttpResponseMessage response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Account '{0}' created.", account.name);
}
else //Getting Unauthorized here
{
throw new Exception(String.Format("Failed to create account '{0}', reason is '{1}'.",account.name, response.ReasonPhrase));
} ... and more code
GET リクエストを呼び出すと、AccessToken を取得して送信しても 401 Unauthorized が返されます。
何か案は?
編集:このブログでアドバイスされているコードも試しました(問題を解決したように見えるソースのみで、どちらも機能しませんでした):
このコードで:
public class WolfController : ApiController
{
private static readonly string Tenant = System.Configuration.ConfigurationManager.AppSettings["ida:Tenant"];
private static readonly string TenantGuid = System.Configuration.ConfigurationManager.AppSettings["ida:TenantGuid"];
private static readonly string ClientId = System.Configuration.ConfigurationManager.AppSettings["ida:ClientID"];
private static readonly string Password = System.Configuration.ConfigurationManager.AppSettings["ida:Password"]; // client secret, valid for one or two years
private static readonly string ResourceId = System.Configuration.ConfigurationManager.AppSettings["ida:ResourceID"];
// GET: api/Wolf
public async Task<IEnumerable<Wolf>> Get()
{
AuthenticationResponse authenticationResponse = await GetAuthenticationResponse();
String result = await DoSomeDataOperations(authenticationResponse);
return new Wolf[]
{
new Wolf()
{
Id = 1,
Name = result
}
};
}
private static async Task<AuthenticationResponse> GetAuthenticationResponse()
{
//https://samlman.wordpress.com/2015/06/04/getting-an-azure-access-token-for-a-web-application-entirely-in-code/
//create the collection of values to send to the POST
List<KeyValuePair<string, string>> vals = new List<KeyValuePair<string, string>>();
vals.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
vals.Add(new KeyValuePair<string, string>("resource", ResourceId));
vals.Add(new KeyValuePair<string, string>("client_id", ClientId));
vals.Add(new KeyValuePair<string, string>("client_secret", Password));
vals.Add(new KeyValuePair<string, string>("username", "someUser@someTenant.onmicrosoft.com"));
vals.Add(new KeyValuePair<string, string>("password", "xxxxxx"));
//create the post Url
string url = string.Format("https://login.microsoftonline.com/{0}/oauth2/token", TenantGuid);
//make the request
HttpClient hc = new HttpClient();
//form encode the data we’re going to POST
HttpContent content = new FormUrlEncodedContent(vals);
//plug in the post body
HttpResponseMessage hrm = hc.PostAsync(url, content).Result;
AuthenticationResponse authenticationResponse = null;
if (hrm.IsSuccessStatusCode)
{
//get the stream
Stream data = await hrm.Content.ReadAsStreamAsync();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof (AuthenticationResponse));
authenticationResponse = (AuthenticationResponse) serializer.ReadObject(data);
}
else
{
authenticationResponse = new AuthenticationResponse() {ErrorMessage = hrm.StatusCode +" "+hrm.RequestMessage};
}
return authenticationResponse;
}
private static async Task<String> DoSomeDataOperations(AuthenticationResponse authResult)
{
if (authResult.ErrorMessage != null)
{
return "problem getting AuthToken: " + authResult.ErrorMessage;
}
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(ResourceId);
httpClient.Timeout = new TimeSpan(0, 2, 0); //2 minutes
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.access_token);
//Retreive Entity/////////////////////////////////////////////////////////////////////////////////////
var retrieveResponse = await httpClient.GetAsync("/api/data/v8.0/feedback?$select=title,rating&$top=10");
//var retrieveResponse = await httpClient.GetAsync("/api/data/v8.0/$metadata");
if (!retrieveResponse.IsSuccessStatusCode)
{
return retrieveResponse.ReasonPhrase;
}
return "it worked!";
}
}