Why does CRM Online return 401 unauthorized in my scenario?
Asked Answered
I

1

7

I am trying to implement server-to-server integration with Dynamics CRM Online 2016 and BizTalk 2013 R2. I am using the WebHttpBinding to call the CRM web API, which requires a bearer token supplied as an http header:

Authorization: Bearer [base64string]

I have written a client message inspector which calls Azure AD using ADAL to acquire an access token. This is secured with a client assertion certificate, which is assigned to the registered app in our AD tenant:

var token = context.AcquireTokenAsync(this.ResourceUri, assertionCert).Result;

  • ResourceUri is https://[myorganisation].crm4.dynamics.com
  • assertionCert is a ClientAssertionCertificate created using the app registration application ID and an x509 certificate in the machine certificate store that is registered to the app as a KeyCredential

This 'works' in that it returns a token and I can decode this token to inspect the claims - there are a fair number of them, I have no way of telling whether this is the set of claims that CRM requires.

The AD app registration is configured with delegated permissions to the CRM instance.

I have set the application ID in the CRM local user to that of the app registration.

Upon calling the webAPI and supplying this token, CRM responds with 401 unauthorized.

I have repeated the same process in a powershell script and in PostMan, all of which appear to show the same behaviour.

What else am I supposed to do to make CRM accept my access token?

edit #1: Tried hardcoding the authority URI to https://login.windows.net/[my-tenant-id]/oauth2/token rather than what comes out of dynamically acquiring the authority through AuthenticationParameters - this is the same value except ending with /authorization instead of /token. This makes zero difference.

edit #2: An administrator I am working with pointed out to me that the application user I am expecting to use had no user roles assigned - this has been amended to have a role which should allow API access, but this also made no difference.

edit #3: Set oauth2AllowImplicitFlow to true in the manifest for the app registration. This doesn't make any difference.

edit #4: Made some progress by creating a new app registration, this time as a Native app rather than a web app. I managed to get a token using a client secret, and this was accepted - BUT when assigning a certificate to the app, and presenting a ClientAssertionCertificate as before, I get the response from the authority:

Error validating credentials. AADSTS50012: Client is public so a client_assertion' should not be presented.

WHY? What does 'Client is public' mean? Just work!

Isopropanol answered 13/4, 2017 at 10:23 Comment(11)
This looks useful (I know nothing about biztalk, can't the CRM SDK somehow be involved ? it would make things easier) social.technet.microsoft.com/wiki/contents/articles/… (or code.msdn.microsoft.com/windowsdesktop/… )Feudality
Shouldn't have mentioned BizTalk - it's not really anything to do with the problem at this point. I have reproduced the authentication failure using a powershell script, which doesn't touch BizTalk at all. Furthermore this link concerns the Organization service, not the web API.Isopropanol
Then my suggestion is to grab Xrm.Tooling.Connector from NuGet, and solve the issue in one line of code (it already takes care of auth). Unless I'm missing something else (could very well be)Feudality
I can't see how using a different tool would fix configuration that is either in Azure or in CRM. And in any case, I need a solution I can use from BizTalk, although my current repro is using a different route.Isopropanol
As a comment to your 4th edit, Public Clients by definition cannot have client secrets. The error you are getting is the token service saying that you are trying to pass a client secret while authenticating with a public client, and that should just not be the case.Azarcon
@ShawnTabrizi the issue is that I don't understand what 'public' means and why I can't use either authentication mode with either type of application. What makes a native app 'public' and a web app not?Isopropanol
@ShawnTabrizi alternatively, if you can tell me what I have to do to make an access token issued by a web app registration be accepted by CRM, I'm happy to use a web app registration instead. I have to say I have not found this topic to be very clearly documented.Isopropanol
Here is the OAuth 2 definition of client types. Native Clients (which are a subset of public clients) cannot hold a secret because their code runs on a machine controlled by the user. With respect to your follow up question, the error Unauthorized implies that your token is missing the correct permissions to call the API. Can you share the endpoint you are trying to call, and also the claims that appear in your token (removing personal/sensitive material).Azarcon
@TomW Have you fixed this issue now?Vaccaro
@FeiXue-MSFT I have not had a chance to look; the required client certificate is on a work machine and it is a public holiday in the UK. Thanks for looking though - I am starting to narrow down conceptually what is wrong. It would be nice if the CRM documentation specified exactly what it expects in claims from an access token.Isopropanol
@TomW The error indicates the issue was relative to the type of app you register. As Shawn Tabrizi explained the there is no secret with the native clients. If you want to acquire the access token using the client credentials flow, you need to register an confident app(web app). And if you have any feedback about the document of Microsoft, you should be able to submit the feedback through the bottom of the document page.Vaccaro
I
0

Hrrrmph!

Turns out that the original situation I had tried and failed with, now works.

  • Web application registration with delegated permissions to CRM Online
  • Install a client certificate on the client machine, and register this same certificate to the app using New-AzureADApplicationKeyCredential
  • Link the app registration to a CRM Application User created for this purpose (they are fundamentally different to interactive users) - n.b. this screen is not easy to find
  • Call AcquireTokenAsync() from ADAL
  • Just works

I am at a loss to explain why this didn't work the first time I tried it, as CRM doesn't supply any information as to why token validation failed.

Isopropanol answered 21/4, 2017 at 8:40 Comment(3)
I am using the exact steps. These are now listed as well on learn.microsoft.com/en-us/powerapps/developer/… but still the web api end point is not accepting my generated access token. What did you do to make this work?Stapleton
@Stapleton I'm not sure what else I can say in addition to that documentation. This version is clearer than what I was using at the time. The only part that I can't explain is that everything mentioned here seems to talk about AAD delegated permissions and not app permissions. S2S applications are supposed to be assigned app permissions. The only other thing I can think of is that AAD may be waiting for admin consentIsopropanol
Even i think that it is related to admin consent but if you look at the documentation for "Connect as an app" it doesn't require the API permissions.. ):Stapleton

© 2022 - 2024 — McMap. All rights reserved.