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

6

13

I'm trying to access a Dynamics CRM Online REST API with Azure AD oAuth 2 Authentication. In order to do so I followed these steps:

- I've registered a web application and/or web api in Azure
- Configured the permissions to Dynamics CRM to have Delegated permissions "Access CRM Online as organization user"
- And created a Key with a 1 year expiration and kept the Client ID generated.

After the web app was configured on Azure I have created a Console application in .NET/C# that uses ADAL to make a simple request, in this case to retrieve a list of accounts:

    class Program
{
    private static string ApiBaseUrl = "https://xxxxx.api.crm4.dynamics.com/";
    private static string ApiUrl = "https://xxxxx.api.crm4.dynamics.com/api/data/v8.1/";
    private static string ClientId = "2a5dcdaf-2036-4391-a3e5-9d0852ffe3f2";
    private static string AppKey = "symCaAYpYqhiMK2Gh+E1LUlfxbMy5X1sJ0/ugzM+ur0=";

    static void Main(string[] args)
    {
        AuthenticationParameters ap = AuthenticationParameters.CreateFromResourceUrlAsync(new Uri(ApiUrl)).Result;

        var clientCredential = new ClientCredential(ClientId, AppKey);

        var authenticationContext = new AuthenticationContext(ap.Authority);
        var authenticationResult = authenticationContext.AcquireToken(ApiBaseUrl, clientCredential);

        var httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);

        var result = httpClient.GetAsync(Path.Combine(ApiUrl, "accounts")).Result;         
    }
}

I retrieve an access token successfully but when I try to do a httprequest to CRM I always get a 401 - Unauthorized status code. What am I missing?

Deceleron answered 13/5, 2016 at 17:11 Comment(10)
I believe its incorrect permissions on the token. What you are doing is using Application credentials to obtain contain. The permissions you selected when you added CRM were Delegated PErmissions. Both are different things. What happens if you use User Creds of a user in the same directory to authenticate and access ?Twentyfour
I've created a native client application before, with which I successfully integrated with CRM but I had to provide user credentials in order to obtain a token from Azure. My goal is to avoid using user credentials at all. For that purpose I'm trying to get a token through key exchange (the method that I explained in the post) or through certificate.Lalalalage
Did you check this out ? msdn.microsoft.com/en-us/library/gg327838.aspxTwentyfour
Yes.But that tutorial only works to create native client applications, for which you always have to provide user credentials to retrieve a token. I'm trying to use Key or Certificate authentication to obtain a token, like explained in this site at 3 and 7:Lalalalage
Did you figure something out yet?Daystar
No, I'm still looking for a solution...Lalalalage
Please see the solution to my similar question: #37455039Daystar
Are you sure this is just simply "Unauthorized", dont you get some kind of error message, some kind of user consent error? Did you send an interactive auth request for the app that you created before you try to call Web API?Altostratus
@AndreCavaca, did you find a solution to your issue? I'm in the same scenario. I may have to get the token with UserPasswordCredential rather than a ClientCredential if I can't get it to work.Doublefaced
Seems, that you forget to add an application user. Also you need to assign an custom created CRM user role for this app user. See this link: msdn.microsoft.com/en-us/library/…Com
D
6

1 year and 2 months later, this same code works perfectly. As many have stated, Dynamics 365 started to support Server-to-Server (S2S) authentication in the meantime. The only step that I had to make that I didn't back then was to create an application user. For more information about how to make this authentication work check this website: https://msdn.microsoft.com/en-us/library/mt790170.aspx

Deceleron answered 21/7, 2017 at 15:36 Comment(1)
Can you please share some more details on how you made it to work ? I am in the same boat and trying to use client_credentials grant type to access d365 web apis ?Thyrotoxicosis
H
10

Thanks everyone for your answers. I finally managed to access Dynamics CRM OData API using ADAL 3.

Since many people are still having problems doing this, see the following steps:

App registration

  1. Log into portal.azure.com using your Office 365 administrator user of your Dynamics CRM subscription.

  2. Go to Azure Active Director\App registrations and add New application registrations

  3. Enter "Name" and "Sign-on URL", the URL could be anything (https://localhost for example)

  4. Select the registered app you just created, go to Settings\Keys

  5. Enter a key description, click Save and copy the Value (and keep it since you will need it later). Also copy the Application ID of the registered app.

  6. Go to "Required Permissions", click Add, select "Dynamics CRM Online" then tick "Access CRM Online as organisation users".

These steps enable a client application to access Dynamics CRM by using the Application ID and the client secret you created in step 5. Your client application is now able to be authenticated against Azure AD with permission to access CRM Online. However, CRM Online does not know about this "client application" or "user". CRM API will response a 401 if you try to access it.

Add CRM application user

To let CRM know about the "client application" or "user", you'll need to add an application user.

  1. Go to CRM\Security Roles, create a new Security Role or just copy the "System Administrator" Role

  2. Go to CRM\Settings\Security\Users, create a new User, change the form to "Application User"

  3. Enter the required fields with the Application ID that you have in previous step. Once saved, CRM will auto populate the Azure AD Object ID and the URI.

  4. Add the user to the Security Role created from previous step.

Now you should be able to access CRM API using HttpClient and ADAL using the sample code below:

var ap = await AuthenticationParameters.CreateFromResourceUrlAsync(
                new Uri("https://*****.api.crm6.dynamics.com/api/data/v9.0/"));

String authorityUrl = ap.Authority;
String resourceUrl = ap.Resource;

var authContext = new AuthenticationContext(authorityUrl);
var clientCred = new ClientCredential("Application ID", "Client Secret");
var test = await authContext.AcquireTokenAsync(resourceUrl, clientCred);

Console.WriteLine(test.AccessToken);

using (var client = new HttpClient())
{
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", test.AccessToken);

    var response = await client.GetAsync("https://*****.api.crm6.dynamics.com/api/data/v9.0/contacts");
    var contacts = await response.Content.ReadAsStringAsync();

    Console.WriteLine(contacts);
}
Heartsease answered 1/2, 2018 at 3:24 Comment(2)
VERY helpful, your comment fixed my problem instantly. Thank you Stefan for getting me out of this rut.Shaffer
How to use Application Id in java?Starstarboard
D
6

1 year and 2 months later, this same code works perfectly. As many have stated, Dynamics 365 started to support Server-to-Server (S2S) authentication in the meantime. The only step that I had to make that I didn't back then was to create an application user. For more information about how to make this authentication work check this website: https://msdn.microsoft.com/en-us/library/mt790170.aspx

Deceleron answered 21/7, 2017 at 15:36 Comment(1)
Can you please share some more details on how you made it to work ? I am in the same boat and trying to use client_credentials grant type to access d365 web apis ?Thyrotoxicosis
C
3

I'd advise you to have a look at Server-to-Server (S2S) authentication, which was added to Dynamics 365 in the latest release.

By using S2S you do not need a paid Dynamics 365 license. Rather than user credentials, the application is authenticated based on a service principal identified by an Azure AD Object ID value which is stored in the Dynamics 365 application user record.

More information can be found here: https://msdn.microsoft.com/en-us/library/mt790168.aspx https://msdn.microsoft.com/en-us/library/mt790170.aspx

Catchpole answered 2/2, 2017 at 9:15 Comment(0)
M
0

Your ClientId, AppKey from Azure, so ap.Authority should be https://login.microsoftonline.com/tenantid in var authenticationContext = new AuthenticationContext(ap.Authority);

Matriarchate answered 19/5, 2016 at 3:9 Comment(2)
I tried to use the URL you proposed but I'm still getting Unauthorized. The requested message generated was this one, where the token was replaced by TOKEN: {Method: GET, RequestUri: 'xxxxx.api.crm4.dynamics.com/api/data/v8.1/accounts', Version: 1.1, Content: <null>, Headers: { Authorization: Bearer TOKEN }}Lalalalage
Did you try to download Manifest and open the JSON file and locate the line: "oauth2AllowImplicitFlow": false, and change false to true and upload the file.Matriarchate
E
0

I don't think you'll be able to get around providing user credentials to at least some kind of "integration account". You can avoid a more traditional popup/redirect OAUTH flow with the following:

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.IO;
using System.Net;

namespace ConsoleApplication2
{
    class Program
    {
        private static string API_BASE_URL = "https://<CRM DOMAIN>/";
        private static string API_URL = "https://<CRM DOMAIN>/api/data/v8.1/";
        private static string CLIENT_ID = "<CLIENT ID>";

        static void Main(string[] args)
        {
            var userCredential = new UserCredential("<USERNAME>", "<PASSWORD>");
            var authContext = new AuthenticationContext("https://login.windows.net/common", false);
            var result = authContext.AcquireToken(API_BASE_URL, CLIENT_ID, userCredential);

            var httpClient = HttpWebRequest.CreateHttp(Path.Combine(API_URL, "accounts"));
            httpClient.Headers.Add(HttpRequestHeader.Authorization, "Bearer:" + result.AccessToken);
            using (var sr = new StreamReader(httpClient.GetResponse().GetResponseStream()))
            {
                Console.WriteLine(sr.ReadToEnd());
            }

            Console.ReadLine();
        }
    }
}

Note: I'm using an older version of ADAL (2.19.208020213) as it appears the password parameter has been taken out of the UserCredential constructor.

EDIT: CRM now supports Server to Server Authentication which allows you to create an application user.

Electrolyze answered 11/6, 2016 at 3:21 Comment(0)
B
0

You may need an Application User set-up inside CRM to match with your Azure Application: https://msdn.microsoft.com/en-us/library/mt790170.aspx

Whilst you can get your Bearer Token setup in C#, the web request to the CRM resource may fail due to CRM level permissions.

Blackwood answered 26/1, 2018 at 22:50 Comment(1)
I did setup an application user with the same app id and appropriate security role. I can create a bearer token using the oauth client credentials grant but when I try to use it I get a 401 unauthorized. Totally not sure what am I doing wrong.Thyrotoxicosis

© 2022 - 2024 — McMap. All rights reserved.