If you ever get this error message, then you are probably working on an ASP.NET application or web service and you are using the CrmImpersonator class from the SDK assemblies.

"The organization id of the user being verified does not match the organization id of the execution context passed to VerifyUser"

What does it mean? Well, you are executing a web service command and have set the CallerId value of the CrmAuthenticationToken to a user id that doesn't exist in the organization you have specified in the Organization. David Yack has an article about it, which may solve your problem. But what's actually wrong with the following code?

protected void Page_Load(object sender, EventArgs e) {

    using (new CrmImpersonator()) {

        CrmService crmService = new CrmService();
        string organization = ParseOrganizationName(this.Context);
        string crmServiceUrl = BuildCrmServiceUrl(this.Context);

        crmService.CrmAuthenticationTokenValue = CrmAuthenticationToken.ExtractCrmAuthenticationToken(this.Context, organization);
        crmService.Url = crmServiceUrl;
        crmService.UseDefaultCredentials = true;

        WhoAmIResponse whoAmI = (WhoAmIResponse) crmService.Execute(new WhoAmIRequest());
    }
}

It threw an exception, but not always. Here are some facts:

To answer my own question, there's nothing wrong with the code. Both methods, ParseOrganizationName and BuildCrmServiceUrl - though not listed - worked fine and returned the proper information. So where's the problem?

It's actually the way how I called this ASPX page. If you have used the Filtered Lookup before, you know that there is some loader code that you have to add to a form in order to load some piece of JavaScript code:

SW_IS_LICENSED_USER = false;

try
{
    var
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
    httpRequest.open("GET", "/isv/stunnware.com/cld4/cld4.aspx?orgname=" + ORG_UNIQUE_NAME, false);
    httpRequest.send(null);
    eval(httpRequest.responseText);
}

catch(e) {
}

On a first sight it seems fine, because the cld4.aspx page is included in /isv/stunnware.com/cld4. And to know the correct organization, I'm simply passing it as a parameter. So all that ParseOrganizationName does is looking for the orgname parameter. Until a week ago the Filtered Lookup implementation used a standard web service reference instead of the SDK assemblies. This has historical reasons, because I had a solution for Dynamics CRM 3.0 as well and of course I haven't started from scratch when building the 4.0 version. However, I have now included IFD support and I changed my implementation to use the SDK assemblies instead, because the CrmImpersonator class was needed to get it running in a hosted environment.

I was very pleased as I had the Filtered Lookup working at my provider and was heading for a beer to celebrate, but to be safe I decided to install the new version in my on-premise environment and it failed with the above error.

The virtual path provider (authentication)

When calling an ASPX or ASMX page, the CRM server uses the request URL to find the corresponding CRM user. In an on-premise installation, each web page is addressed like this:

    http(s)://server:port/organization/...

As an example, in my environment it's http://dc:5555/sw/... Of course there is no directory named "sw" in the CRM web. The virtual path provider looks at the request URL and extracts the virtual directory name "sw". Now CRM knows which organization we are working with and removes "sw" from the request URL, resulting in a file that can be found on disk.

So my problem was that I haven't had included the organization name in the call to the open method of the httpRequest object. CRM looks at the request URL and doesn't find a match in the list of organization names. It decides that we want to use the default organization and looks for a record matching my credentials. And of course it found a user record in the default organization. It then creates a CrmAuthenticationToken and sets the CallerId value to the systemuserid of my user record in the default organization. As I had used a parameter specifying the organization name, I had a mismatch: the organization name was correct, but the user id contained in the authentication token extracted by the call to ExtractCrmAuthenticationToken was from a different (the default) organization.

Thanks to Jagan Peri from Microsoft, I found the problem and changed the JavaScript loader code to this:

SW_IS_LICENSED_USER = false;

try
{
    var
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
    httpRequest.open("GET", "/" + ORG_UNIQUE_NAME + "/isv/stunnware.com/cld4/cld4.aspx?orgname=" + ORG_UNIQUE_NAME, false);
    httpRequest.send(null);
    eval(httpRequest.responseText);
}

catch(e) {
}

This did the trick and everything was working fine in my on-premise environment. The virtual path provider has the information needed to find the correct organization and delivers the correct user id in the authentication token.

While again heading for a beer to celebrate, I thought that I may do a final test in the hosted environment. And guess what? It failed.

Different paths in hosted environments

When accessing a hosted environment, you won't find a URL like http(s)://server.domain.com/organization. Instead it will be http(s)://organization.domain.com. My JavaScript code now added the organization name to the request URL, resulting in /sw/isv/stunnware.com/cld4/cld4.aspx. In an on-premise installation, the virtual path provider would extract "sw" and remove it from the URL. In a hosted environment it does not, because it gets the organization name from the host header. So the request URL still is /sw/isv/stunnware.com/cld4/cld4.aspx and this doesn't exist.

So how do you know if you have to add the organization name or not? While thinking about it, I found that Microsoft must have the same problem, because they are opening pages and executing web service requests all over the place. So I looked at the CRM files and found the following function:

function prependOrgName(sUrl) {

    var sNewUrl = sUrl;

    if (IS_PATHBASEDURLS && ORG_UNIQUE_NAME.length > 0) {
        sNewUrl = "/" + ORG_UNIQUE_NAME + sUrl;
    }

    return sNewUrl;
}

So finally I got my code working in on-premise and hosted environments by changing the JavaScript code to this:

SW_IS_LICENSED_USER = false;

try
{
    var
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
    httpRequest.open("GET", prependOrgName("/isv/stunnware.com/cld4/cld4.aspx?orgname=" + ORG_UNIQUE_NAME), false);
    httpRequest.send(null);
    eval(httpRequest.responseText);
}

catch(e) {
}

The only problem is that neither IS_PATHBASEDURLS nor prependOrgName is contained in the SDK documentation, so when being picky, it is unsupported. I hope to see it contained in the future though, because it is definitely needed to write code running in all environments.

One note to the Filtered Lookup users: the next release of the Filtered Lookup will support Internet Facing Deployments (IFD) and when installing this version, you have to change the loader code appropriately. I'm sorry about this, but it won't be too much work, as it's only pasting the updated loader script into each form using the lookup. I'm adding a big red alert to this version though.

And a tip for JavaScript (and C#, C, Java) in general: the prependOrgName function can be written in a single line of code:

function prependOrgName(sUrl) {
    return (IS_PATHBASEDURLS && ORG_UNIQUE_NAME.length > 0) ? ("/" + ORG_UNIQUE_NAME + sUrl) : sUrl;
}

Besides smaller code, the biggest difference is that no memory is allocated for the sNewUrl variable unless needed. Not a big one though.