4
votes

I have multiple instances of CRM Online which are hosted in the cloud by Microsoft.
They are all secured with the same Azure Active Directory which itself is managed by ADFS.
My application is registered within Azure AD and therefore has a client ID/key etc and permissions to CRM.

I want to connect to a specific instance of CRM via the Web API to request data.
I can get this working by using CrmServiceClient with a connection string containing a username/password.
This seems to be the commonly documented example but I don't want to do this. The following MSDN article makes it look like I can do it via a token instead: https://msdn.microsoft.com/en-us/library/gg327838.aspx

I can successfully get a token like this (I have obscured some details for security):

var tenantName = @"myorg.onmicrosoft.com";
var authString = string.Format(@"https://login.microsoftonline.com/{0}", tenantName);

var authContext = new AuthenticationContext(authString, false);

// I am in the UK so use crm4
var resource = @"https://myorg.crm4.dynamics.com";
var clientId = @"899ac540-2134-1234-abcd-2d6440046630";
var key = @"nStbgtfe0oybU1P5+/FQ4wFn1oLTEDr5M7Kjrghf5yh=";
var clientCred = new ClientCredential(clientId, key);

var authResult = await authContext.AcquireTokenAsync(resource, clientCred);

Normally I'd pass this token to a HttpRequest as an authentication header but option 3 on this blog suggests it's possible to use the OrganizationWebProxyClient class provided by the SDK instead: http://crmtipoftheday.com/2015/06/24/you-have-oauth-token-now-what/

var orgService = new OrganizationWebProxyClient(
    new Uri("https://myorg.api.crm4.dynamics.com/XRMServices/2011/Organization.svc"), false)
{
    HeaderToken = authResult.AccessToken,
    SdkClientVersion = "8"
};

Lastly I need to add a service reference to the Organization Service provided by Microsoft:

https://myorg.api.crm4.dynamics.com/XRMServices/2011/Organization.svc?singleWSDL&sdkversion=8

This creates a binding in my app.config:

<system.serviceModel>
  <bindings>
    <customBinding>
      <binding name="CustomBinding_IOrganizationService">
        <textMessageEncoding />
        <httpsTransport />
      </binding>
    </customBinding>
  </bindings>
  <client>
    <endpoint address="https://myorg.api.crm4.dynamics.com/XRMServices/2011/Organization.svc"
    binding="customBinding" bindingConfiguration="CustomBinding_IOrganizationService"
    contract="CRMOrgService.IOrganizationService" name="CustomBinding_IOrganizationService" />
  </client>
 </system.serviceModel>

And then I can assign this to the organization service:

var binding = new CustomBinding("CustomBinding_IOrganizationService");
orgService.Endpoint.Binding = binding;

Finally I can call the web service:

var contacts = orgService.RetrieveMultiple(new QueryExpression
{
    EntityName = "contact",
    ColumnSet = new ColumnSet("firstname", "lastname")
})
.Entities
.Select(item => item.ToEntity<Contact>());

However upon calling the organization service I get an error:

An error occurred when verifying security for the message

I'm led to believe this error actually swallows whatever the real error was (thanks Microsoft) so I don't know what's really wrong.
Lots of documentation online says it's client and server time being out of sync but I don't think it's this. I can't prove what time Microsoft's servers are on but my clock is absolutely accurate and in the right time zone for the UK.

Does anyone know what I'm doing wrong?
Alternatively is there a different way to authenticate without a username/password?
Or am I simply barking up the wrong tree and should I be using a username/password after all?

1
1. Have you added application user in your org? 2. Why do you need all this custombinding in config?georged
This question is nearly a year and a half old, I stopped looking at this a long time ago. Sorry.Equalsk
Ah, that would explain it :) Sorry, someone asked me about it and I even didn't look at the dates... S2S was not working 1.5 years ago, it's been supported by CRM only since couple months back. So it's 100% working now.georged
@georged Oh that's good news, thanks for sharing. I'll bear this in mind next time I look at this topic :-)Equalsk

1 Answers

2
votes

It seems that CRM Online doesn't support any kind of anonymous or passive authentication and must always be supplied with a valid username and password.

I've resorted to simply creating a user solely for API access and using these credentials to authenticate with CRM. Although this is not what I wanted it does work fine.

I'd love for someone to prove me wrong but it doesn't seem this is possible right now.