title: Azure to Dynamics CRM 2011: how to implement AD authentication?
body: <p>In order to have a better display of data in our CRM installation, we have an Azure website which basically reads the CRM data we want to process (work schedule hours for an employee) and converts them into an easier to read format using Telerik (Scheduler using recurring appointments). The Telerik appointments are never converted back, so it's a read-only connection to CRM.</p>
  
  <p>We have gotten the product working just fine using a fixed user for authentication, but when we want to authenticate using the currently logged in user, we hit a snag involving AD. the currentUserId in the code below is passed through querystring from the form.</p>
  
  <pre><code>private OrganizationServiceProxy CreateOrganizationService(String serverAddress, String organizationName, Guid currentUserId)
  {
      var discoveryServiceUri = serverAddress.Contains("http") ? new Uri(String.Format("{0}/XRMServices/2011/Discovery.svc", serverAddress)) : new Uri(String.Format("http://{0}/XRMServices/2011/Discovery.svc", serverAddress));
      System.Diagnostics.Trace.TraceInformation("current discoveryUri: " + discoveryServiceUri);
      var credentials = new ClientCredentials();
      // Get the user's logon credentials.
      credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
      System.Diagnostics.Trace.TraceInformation("current user: " + CredentialCache.DefaultNetworkCredentials.UserName);
      // Get the target organization.
      var organizationUri = GetOrganizationAddress(organizationName, discoveryServiceUri, credentials, null);
      System.Diagnostics.Trace.TraceInformation("current organization: " + organizationUri);
      var serviceProxy = new OrganizationServiceProxy(organizationUri, null, credentials, null);
      // This statement is required to enable early-bound type support.
      serviceProxy.EnableProxyTypes();
      if (currentUserId == Guid.Empty)
      {
         currentUserId = GetUserId(serviceProxy);
      }
      if (currentUserId != Guid.Empty)
         serviceProxy.CallerId = currentUserId;
      return serviceProxy;
  }
  </code></pre>
  
  <p>The above code is used to create the CRM connection on the server side.</p>
  
  <pre><code>private Uri GetOrganizationAddress(String organizationName, Uri discoveryServiceUri, ClientCredentials credentials, ClientCredentials deviceCredentials)
      {
          if (discoveryServiceUri == null)
              throw new Exception("DiscoveryServiceUri is null. Please specify a valid configuration details to connect crm server.");
          using (var serviceProxy = new DiscoveryServiceProxy(discoveryServiceUri, null, credentials, deviceCredentials))
          {
              // Obtain organization information from the Discovery service.
              {
                  // Obtain information about the organizations that the system user belongs to.
                  System.Diagnostics.Trace.TraceInformation("getting organizationss now");
                  OrganizationDetailCollection orgs = DiscoverOrganizations(serviceProxy);
                  System.Diagnostics.Trace.TraceInformation(orgs.Count + " organizations discovered");
                  if (orgs.Count &gt; 0)
                  {
                      int orgNumber = 0;
                      for (int n = 0; n &lt; orgs.Count; n++)
                      {
                          if (orgs[n].UniqueName == organizationName || orgs[n].FriendlyName == organizationName)
                          {
                              orgNumber = n + 1;
                              System.Diagnostics.Trace.TraceInformation("orgs[n].UniqueName: " + orgs[n].UniqueName);
                              System.Diagnostics.Trace.TraceInformation("orgs[n].friendlyName: " + orgs[n].FriendlyName);
                              break;
                          }
                      }
                      if (orgNumber &gt; 0 &amp;&amp; orgNumber &lt;= orgs.Count)
                      {
                          Version version = new Version(orgs[orgNumber - 1].OrganizationVersion);
                          System.Diagnostics.Trace.TraceInformation("version: " + version.Build);
                          // Return the organization Uri.
                          Uri uri =  new Uri(orgs[orgNumber - 1].Endpoints[EndpointType.OrganizationService]);
                          System.Diagnostics.Trace.TraceInformation("uri:" + uri);
                          return uri;
                      }
                      throw new Exception(string.Format("The specified organization '{0}' does not exist.", organizationName));
                  }
                  throw new Exception(string.Format("You do not belong to any organizations on the specified server. DiscoveryServiceUri '{0}'", discoveryServiceUri.AbsoluteUri));
              }
          }
      }
  </code></pre>
  
  <p>the below code is where it goes wrong:</p>
  
  <pre><code>private OrganizationDetailCollection DiscoverOrganizations(IDiscoveryService service)
      {
          var orgRequest = new RetrieveOrganizationsRequest();
          System.Diagnostics.Trace.TraceInformation("orgrequest made");
          var orgResponse = (RetrieveOrganizationsResponse)service.Execute(orgRequest);
          System.Diagnostics.Trace.TraceInformation("orgresponse retrieved: " + orgResponse);
  
          System.Diagnostics.Trace.TraceInformation("orgResponse.details: " + orgResponse.Details);
          return orgResponse.Details;
      }
  </code></pre>
  
  <p>It goes wrong on the Execute(orgRequest) command. I have tracked this down to an issue with AD authentication, namely that my credentials appear to be wrong. I want to pass along the AD credentials used by the AD user account who is currently using CRM (not the CRM user account) to the credentials object in the first method..</p>
  
  <p>The intent is that this app will only be used from inside Dynamics CRM 2011, but it should theoretically also work outside the platform for testing purposes.</p>
  
  <p>edit: just realized I forgot my stacktrace:</p>
  
  <pre><code>System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
     at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
     at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)The caller was not authenticated by the service.
  Server stack trace: 
     at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
     at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout)
     at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
     at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
     at System.ServiceModel.Security.CommunicationObjectSecurityTokenProvider.Open(TimeSpan timeout)
     at System.ServiceModel.Security.SecurityProtocol.OnOpen(TimeSpan timeout)
     at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
     at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
     at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.OnOpen(TimeSpan timeout)
     at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
     at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
     at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
     at System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
     at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
     at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
     at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
     at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
  </code></pre>