Sunday, May 18, 2008

Generating the CrmService over an IFD Connection

I've been working on an application that needs to connect using the Internet Connector license over an IFD connection. I have users who need to submit data into CRM that are not CRM users and thus have no login credentials. So I started researching using the GUID of a CRM user to authenticate. This is possible with plgins and is utilized for impersonation. However, after a few days of fighting code, authentication errors and discussing options with the MS Support SDK team, we found that authentication tghrough the IFD is not possible by using just a users GUID. There still has to be some level of authentication with Active Directory that must occur and a CRM user's GUID does not contain sufficient information to pass the credential challenge AD requires. IFD requires a username and password in order to instantiate the CRM service.

From what MS Support said, either not many are doing this yet, or I'm one of the few who hadn't figured this out yet.

Either way, I thought I'd share my CRM Service generation code that is used for connecting over an IFD connection Hopefully this helps someone else out, enjoy.


public CrmService IFDConnection(string organization, string server, string domain, string username, string password)
{
// A CrmService reference.
CrmService CrmService = null;
// URL of the Web application.
string WebApplicationUrl = String.Empty;
// GUID of the user's organization.
Guid OrganizationId = Guid.Empty;
//Remove any trailing forward slash from the end of the server URL.
server = server.TrimEnd(new char[] { '/' });
// Initialize an instance of the CrmDiscoveryService Web service proxy.
CrmDiscoveryService disco = new CrmDiscoveryService();
disco.Url = "http://" + server + "/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx";
//Retrieve a list of available organizations.
RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
orgRequest.UserId = domain + "\\" + username;
orgRequest.Password = password;
RetrieveOrganizationsResponse orgResponse = (RetrieveOrganizationsResponse)disco.Execute(orgRequest);
//Find the desired organization.
foreach (OrganizationDetail orgdetail in orgResponse.OrganizationDetails)
{
if (orgdetail.OrganizationName.ToLower() == organization.ToLower())
{
//Retrieve the ticket.
RetrieveCrmTicketRequest ticketRequest = new RetrieveCrmTicketRequest();
ticketRequest.OrganizationName = organization;
ticketRequest.UserId = domain + "\\" + username;
ticketRequest.Password = password;
RetrieveCrmTicketResponse ticketResponse = (RetrieveCrmTicketResponse)disco.Execute(ticketRequest);
//Create the CrmService Web service proxy.
CrmAuthenticationToken sdktoken = new CrmAuthenticationToken();
sdktoken.AuthenticationType = 2;
sdktoken.OrganizationName = organization;
sdktoken.CrmTicket = ticketResponse.CrmTicket;
CrmService = new CrmService();
CrmService.CrmAuthenticationTokenValue = sdktoken;
CrmService.Url = orgdetail.CrmServiceUrl;
WebApplicationUrl = orgdetail.WebApplicationUrl;
OrganizationId = orgdetail.OrganizationId;
break;
}
}
return CrmService;
}

David Fronk
Dynamic Methods Inc.

28 comments:

Anonymous said...

Thanks for the information. We have snapins in CRM(urls to site map) and trying to do IFD. What user credentials are you passing to IFDConnection method. If forms, how can we retrieve this information.

thanks,Bhasker

Anonymous said...

Thanks for this. The code is also in the help file for the SDK.
However I am having problems just after similar code (which runs OK) when I make the first request to the service where I get an authentication error even though a ticket was issues sucessfully. Still investigating ....

Anonymous said...

Hi,

thanks, but it doesn't work for me. When I try access CrmDiscoveryService, I get "The request failed with HTTP status 401: Unauthorized.".
What should be the problem?

Thanks,
Tomas

Dynamic Methods said...

Bhasker,

According to Microsoft Suppor there is no way to get the user credentials over IFD, at least not initially. You need to hard code some username and password of a user that can authenticate to CRM so that it can get through the initial layer of security. From there you might be able to get the user's computer login and compare with the CRM user list, get the GUID and then impersonate using that GUID. But you MUST hard code some username and password in order to get through the initial layer of security.

David Fronk
Dynamic Methods Inc.

Dynamic Methods said...

redware,

You may be running into a similar problem as Bhasker, you may need to use a hard coded user to get through initially. See what more you can come up with.

David Fronk
Dynamic Methods Inc.

Dynamic Methods said...

Tomas,

Make sure your login information is hard coded correctly (username, password, and especially the Organization Name). That would be my guess.

David Fronk
Dynamic Methods Inc.

Unknown said...

Hi,
I am using CRm 4.0 with IFD.
I have one custom web page which is working fine when CRM is accessed over internet.

But i get Unauthorised error when i tried to access it in intranet.
Actually with ordinary webservice calls the page is working fine from my devlopment environment.when i deploy it in server by creating a virtual directory it is giving error.
I have also tired to create my wbpage under a different web site still i get error...

Dynamic Methods said...

Gowri,

My guess would be that you are trying to reference your call to the webservices the exact same way in your code for when accessing your code over the internet versus when accessing it from your intranet. Check the SDK document titled "Web Form (IFD) Authentication" and even the "Web Service Authentication and Impersonation" page that should lead you to an article that will walk you through each authentication scenario. Those should help you out a bit. I can make guesses but my experience is that unless I get waist or even neck deep into someone else's code troubleshooting gets difficult.

Hope this helps you at least a little.

David Fronk
Dynamic Methods Inc.

Anonymous said...

Hi,
Thanks for the authentication method that you have provided.

But I am stuck up with adifferent problem.
The code works well when I run the code from another machine or system.
But if I try deploying the code on the server I am not able to run it.

Please help me out.

Dynamic Methods said...

Susan,

I guess I didn't state it clearly enough, but this code for generating the CrmService over an IFD connection ONLY works from outside your network. If your code needs to work both internally and externally, then you need to set up the logic, or proper authentication calls within your code to make that work. This code will never work internally as it is supposed to be coming in over your IFD connection, which can never be your network that is internal to the CRM server. In order to get your code to work internally you will need to employ the AD credential authentication type for custom code.

I hope this helps to at least point you in the right direction.

David Fronk
Dynamic Methods Inc.

Bohnnie said...

Hi,
I am using the same code as in SDK to invoke an IFD service and have hard coded the credentials.

I am still getting "http: 401 Unauthorised error" on exceuting the following line of code:
"RetrieveOrganizationsRequest orgResponse = (RetrieveOrganizationsResponse) disco.Execute(orgRequest);"

Please help!

Bohnnie said...

Hi,
I have used the same code as given in the SDK to invoke an IFD connection.
I have hard coded the credentials and the org name. I am still getting the "http: 401 Unauthorised Access Error" on executing the following line of code:
"RetrieveOrganizationsResponse orgResponse = (RetrieveOrganizationsResponse)disco.Execute(orgRequest); "
Please Help!!

Unknown said...

Hi,
I have used the same code as given in the SDK to invoke an IFD connection.

I have hard coded the credentials and the org name. I am still getting the "http: 401 Unauthorised Access Error" on executing the following line of code:
"RetrieveOrganizationsResponse orgResponse = (RetrieveOrganizationsResponse)disco.Execute(orgRequest); "

Please Help!!

Dynamic Methods said...

bohnnie and shilpa,

I would need to see what you are putting into your orgRequest variable. Are you including the username and password? Do you actually have IFD set up on your implementation? If you don't have IFD set up then your authentication through the web services will be different.

Also, looking through the SDK, I saw the RetrieveOrganizations Message article and the Web Form (IFD) Authentication and if you'll notice they are different. The RetrieveOrganizations Message (which is the call that you posted) will pull back the Organizations that have been implemented in the given instance of MSCRM. But if you just want to connect over IFD then the Web Form (IFD) Authentication article is what you want to be using.

Hopefully this helps a bit, otherwise let me know how else I can help. Posting some more of your code (obviously pulling out usernames and passwords) would be helpful as well.

Thanks,

David Fronk
Dynamic Methods Inc.

Anonymous said...

Hi All,
I am facing the same issue.
I dont want to give the credentials for disco explicitly.
So I am using CrmImpersonator and ExtractCrmAuthenticationToken method with DefaultCredentials. CrmService object is created but with CrmTicket as null. So i face 401 unauthorized exception when i use the service object.

The context object that i use is System.Web.HttpContext.Current.

Any help on this!!

Lochan

Dynamic Methods said...

Lochan,

If you are connecting over IFD then you will have to include a username and password. I spoke to MS Support about this at length and the way that the web service is set up is that it has to have a username and password in order to authenticate. Typically, when I have issues with getting the CrmTicket it is due to the Organization name being entered incorrectly. There should be NO spaces in the Organization Name. So, if Dynamic Methods were my organization, it would actually be "DynamicMethods" in CRM. If there is a space, then the ticket retrieve will fail. You could double check the Organization Name in Deployment Manager on the CRM server just to be sure.

Hopefully that helps a bit.

David Fronk
Dynamic Methods Inc.

Anonymous said...

FYI: You'll also need a special CRM "Connector" license for the user used to push the data from an external source through the web service.
Every user that uses in any sort of way CRM has to have a license. But since in your case many non mapped users may use CRM through a proxy application, the license for that user is special and usually cost more... Anonymous MSCBSS..
Licenses are based on honor but you may loose more than your honor if you/your company gets caught!

Dynamic Methods said...

Thanks for clarifying on that. Any external connection to CRM, whether over IFD or not, requires the purchase of the Internet Connector license.

David Fronk
Dynamic Methods Inc.

Anonymous said...

I am having a problem where I am not able to call CrmService after IFD. I am calling the CrmService from another web service that is hosted in the Dynamics CRM website. Everytime I try I get 401 unauthorized error. I can only call the webservice after Impersonation but that actually makes the logged in user the one being impersonated not the one who is actually accessing the CRM. My webservice is called from a silverlight control hosted in an IFrame on CRM app.
I don't want impersonation so that I get the correct UserId fromt he WhoAmIResponse. Due to impersonation the userId returned will be of the impersonated user.

Haris Munawar

Dynamic Methods said...

Haris,

When connecting over IFD if your webpage isn't getting the authentication token from the main application then impersonation is the only way to authenticate. Just having your page be hosted within the CRM web page isn't enough. Here's a method that I use over IFD that works just fine:

public CrmService GetCrmService(string orgName, string server, string domain)
{
//Standard CRM Service Setup
ComplaintGeneration.CrmSdk.CrmAuthenticationToken token = new ComplaintGeneration.CrmSdk.CrmAuthenticationToken();
token.AuthenticationType = 0; //AD
token.OrganizationName = orgName;

CrmService service = new CrmService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
//service.Credentials = new System.Net.NetworkCredential(username, password, domain);


service.CrmAuthenticationTokenValue = token;
service.Url = string.Format("http://{0}/mscrmservices/2007/crmservice.asmx", server);

return service;
}

Maybe it will work for you as well.

David Fronk
Dynamic Methods Inc.

Joe K said...

Hi,
If my code needs to work both internally and externally, then how can you tell, programmatically, if you need to set up the internal vs the external connection? Is there a method that tells you which one is active??

-- Joe

Dynamic Methods said...

Joe,

Unfortunately there is no service that tells you which connection to use. However you could attempt to connect assuming internal, and if it fails (throws and error) then attempt to connect using the external connection credentials. Even if you were use a method to check whether you were internal or external the time difference wouldn't be all that much.

David Fronk
Dynamic Methods Inc.

Anonymous said...

I've been able to send the cookie using a cookiecontainer on the CrmService. In IFD this appears to be required.

svcCrm.CookieContainer = new CookieContainer();
Cookie Cook = new Cookie(HttpContext.Current.Request.Cookies[0].Name, HttpContext.Current.Request.Cookies[0].Value);
Cook.Domain = "crm.homeftp.org";

svcCrm.CookieContainer.Add(Cook);

This is for IFD only.

Thanks,
John

Dynamic Methods said...

John,

Thanks for sharing that, this will definitely come in hsndy.

David Fronk
Dynamic Methods Inc.

ronald lemmen said...

Hi Frank,
I would strongly suggest to change this line:

if (orgdetail.OrganizationName == organization)

into this line:

if (orgdetail.OrganizationName.ToLower() == organization.ToLower())

It happens way too often that the organization name is written in capitals. This just saves time looking for issues.

Kind regards,
Ronald Lemmen

Dynamic Methods said...

Ronald,

Thanks for the tip, that's a great suggestion. I typically make sure that the organization is passed in correctly within my code so that everything is correct but this helps to limit the number of possible issues that could occur. Thanks again.

David Fronk
Dynamic Methods Inc.

Kevin said...

This works great but I have not been able to adapt it to get the metadata service also. Any recommendations on this ?

Dynamic Methods said...

Kevin,

All you should need to do is change the service call and the URL that the service call is supposed to use. Also, make sure that the user that you are using has privileges to read metadata and interact with them the way that you need to.

Other than that, look for examples in the SDK, that should help.

David Fronk
Dynamic Methods Inc.

Post a Comment