Using ADAL C# as Confidential User /Daemon Server /Server-to-Server - 401 Unauthorized

后端 未结 3 1839
眼角桃花
眼角桃花 2020-11-30 10:59

Refering to not answered Questions:

401- Unauthorized authentication using REST API Dynamics CRM with Azure AD

and

Dynamics CRM Online 2016 - Daemon

3条回答
  •  难免孤独
    2020-11-30 11:10

    I finally found a solution. Provided by Joao R. in this Post:

    https://community.dynamics.com/crm/f/117/t/193506

    First of all: FORGET ADAL

    My problem was the whole time that I was using "wrong" URLS as it seems you need other adresses when not using Adal (or more general: user-redirect).


    Solution

    Construct following HTTP-Reqest for the Token:

    URL: https://login.windows.net/MyCompanyTenant.onmicrosoft.com/oauth2/token

    Header:

    • Cache-Control: no-cache
    • Content-Type: application/x-www-form-urlencoded

    Body:

    • client_id: YourClientIdFromAzureAd
    • resource: https://myCompanyTenant.crm.dynamics.com
    • username: yourServiceUser@myCompanyTenant.onmicrosoft.com
    • password: yourServiceUserPassword
    • grant_type: password
    • client_secret: YourClientSecretFromAzureAd

    Construct the following HTTP-Request for the access to WebApi:

    URL: https://MyCompanyTenant.api.crm.dynamics.com/api/data/v8.0/accounts

    Header:

    • Cache-Control: no-cache
    • Accept: application/json
    • OData-Version: 4.0
    • Authorization: Bearer TokenRetrievedFomRequestAbove

    Node.js Solution (Module for getting the Token)

    var https = require("https");
    var querystring = require("querystring");
    var config = require("../config/configuration.js");
    var q = require("q");
    
    var authHost = config.oauth.host;
    var authPath = config.oauth.path;
    var clientId = config.app.clientId;
    var resourceId = config.crm.resourceId;
    var username = config.crm.serviceUser.name;
    var password = config.crm.serviceUser.password;
    var clientSecret =config.app.clientSecret;
    
    function retrieveToken() {
        var deferred = q.defer();   
        var bodyDataString = querystring.stringify({
            grant_type: "password",
            client_id:  clientId, 
            resource: resourceId,
            username: username,
            password: password,        
            client_secret: clientSecret
        });
        var options = {
            host: authHost,
            path: authPath,
            method: 'POST',
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
                "Cache-Control": "no-cache"
            }
        };      
        var request = https.request(options, function(response){
            // Continuously update stream with data
            var body = '';
            response.on('data', function(d) {
                body += d;
            });
            response.on('end', function() {
                var parsed = JSON.parse(body); //todo: try/catch
                deferred.resolve(parsed.access_token);
            });               
        });
    
        request.on('error', function(e) {
            console.log(e.message);
            deferred.reject("authProvider.retrieveToken: Error retrieving the authToken: \r\n"+e.message);
        });
    
       request.end(bodyDataString);
       return deferred.promise;    
    }
    
    module.exports = {retrieveToken: retrieveToken};
    

    C#-Solution (Getting and using the Token)

      public class AuthenticationResponse
      {
        public string token_type { get; set; }
        public string scope { get; set; }
        public int expires_in { get; set; }
        public int expires_on { get; set; }
        public int not_before { get; set; }
        public string resource { get; set; }
        public string access_token { get; set; }
        public string refresh_token { get; set; }
        public string id_token { get; set; }
      }
    

    private static async Task GetAuthenticationResponse()
    {
      List> vals = new List>();
    
      vals.Add(new KeyValuePair("client_id", ClientId));
      vals.Add(new KeyValuePair("resource", ResourceId));
      vals.Add(new KeyValuePair("username", "yxcyxc@xyxc.onmicrosoft.com"));
      vals.Add(new KeyValuePair("password", "yxcycx"));
      vals.Add(new KeyValuePair("grant_type", "password"));
      vals.Add(new KeyValuePair("client_secret", Password));
    
    
      string url = string.Format("https://login.windows.net/{0}/oauth2/token", Tenant);
    
      using (HttpClient httpClient = new HttpClient())
      {
        httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
        HttpContent content = new FormUrlEncodedContent(vals);
        HttpResponseMessage hrm = httpClient.PostAsync(url, content).Result;
    
        AuthenticationResponse authenticationResponse = null;
        if (hrm.IsSuccessStatusCode)
        {
          Stream data = await hrm.Content.ReadAsStreamAsync();
          DataContractJsonSerializer serializer = new
        DataContractJsonSerializer(typeof(AuthenticationResponse));
          authenticationResponse = (AuthenticationResponse)serializer.ReadObject(data);
        }
        return authenticationResponse;
      }
    }
    
    private static async Task DataOperations(AuthenticationResponse authResult)
    {    
      using (HttpClient httpClient = new HttpClient())
      {
        httpClient.BaseAddress = new Uri(ResourceApiId);
        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("Cache-Control", "no-cache");
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.access_token);
    
        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 });
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "api/data/v8.0/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
        {
          throw new Exception(String.Format("Failed to create account '{0}', reason is '{1}'."
            , account.name
            , response.ReasonPhrase));
        }
    (...)
    

提交回复
热议问题