The remote server returned an error: (401) Unauthorized.

Here is the scenario:

  • .NET Web Api running on an IIS Server (8.5 – although the problem was recreated on 7.5 too)
  • v4.0 Integrated Application Pool with a domain account identity.
  • Only Windows Authentication is enabled. No anonymous access is allowed.
  • Making requests to the Web Api  works in the following configuration:
    • Using a different domain account to the one used by the Application Pool AND
    • From a different machine and using a browser or the Invoke-RestMethod PowerShell command
  • Making requests to the Web Api does not work in the this configuration and results in a 401:Unauthorized error being displayed.
    • Using a different domain account from the same server OR
    • Using the same domain account to the one used by the Application Pool AND
    • From either the same or different machine, using a browser or the Invole-RestMethod PowerShell command

The problem was that a Schedueld Job, created as a PowerShell script, was being run from the same server and the Invoke-RestMethod was generating the 401:Unauthorized error.

The solution was to set the relevant Service Principle Names for the IIS Server, but instead of doing it for the specific domain user account it was configured for the server. This included SPN’s for the computers NetBIOS name and the FQDN as well as the host name of the Web Api.

Accessing secure web services – invalid certificate errors

Calling a secure web service from within an ASP.NET application was generating the following exception:

The remote certificate is invalid according to the validation procedure.

Digging further down into the exception provided a little more detail:

Could not establish trust relationship for the SSL/TLS secure channel with authority 'companyaddress.com'

But, this still didn’t give me too many clues as to the problem. I then came across this very useful MSDN blog post that gave me some very valuable steps to follow to resolve the issue. My first problem was that there were a couple of intermediate certificates involved that needed to be installed to the relevant locations on the server where my ASP.NET app was running; but this didn’t solve the problem so I set up the tracing file and ran the request again. This time the error was a bit more helpful:

Certificate name mismatch

It turns out that my problem was the endpoint address in my web.config file did not match the address in the certificate. My endpoint address was entered as companyaddress.com but the certificate was issued to www.companyaddress.com. The final part of the solution was to change the endpoint address in the web.config to match that in the certificate.

CRM 2011 Workflow Registration: Access Denied

During a deployment of a custom workflow to a CRM 2011 server I started to encounter errors in the plugin registration process. The Workflow Assembly would register with CRM but the Workflow activity registration would fail. I would get the following error

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Exception retrieving custom activity info - Could not load file or assembly 'Microsoft.Crm, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. Access is denied.

The CRM server I was deploying to was shared with a number of other developers and this particular server had been experiencing other issues during the day. Once the server had been stabilised and the organisations were working once more I found I was still encountering the same issues. I was using the Plugin registration tool, was connected as the CRM Administrator to perform my deployments but it was still telling me that my access was denied.

It turns out that the Server/bin folder on the CRM server was missing some permissions which was why I was getting the Access Denied message. In particular, the CRMServer\User group was missing and once I had reinstated it my Workflows would register the assembly and custom activities successfully.

Using HtmlHelper.GenerateLink

Since the documentation for HtmlHelper.GenearteLink is a bit limited I thought I would document some of my understanding of its usage. The application we are developing uses a static class, called HtmlExtensions, that contains a number of methods to build MvcHtmlString’s. One of those methods was used to generate an anchor tag containing a Controller, Action and Id; for example, Id. Using the MvCHtmlString.Create method the first version of this helper method was written as

return MvcHtmlString.Create(string.Format("<a class="my-link" href="Controller/Action/{0}">{0}</a>", myNumber));

This worked fine in the development environment (VS 2012 with IIS Express 8) but when it was deployed to a virtual directory on the Test server we found that the links would not work because the paths were not being resolved correctly. With experience comes knowledge and I found people who had identified similar problems but using the GenerateLink method; I worked on converting the code above to use that method.

public static MvcHtmlString MyLink(this HtmlHelper helper, string myNumber)
{
    string anchor = HtmlHelper.GenerateLink(helper.ViewContext.RequestContext, 
                        helper.RouteCollection, myNumber, null, "Action", "Controller", 
                        new System.Web.Routing.RouteValueDictionary() { {"Id", myNumber } }, 
                        new System.Collections.Generic.Dictionary<string, object>() { { "class", "my-link" } });
    return MvcHtmlString.Create(anchor);
}

The GenerateLink method listed above produces virtually the same return string value as the initial implementation but this time it takes into account the RequestContext to generate the correct href attribute for the anchor. The other parameters worth noting are the RouteValueDictionary and the htmlAttributes Dictionary. The RouteValueDictionary allows us to define the final part of the Route Url, which in our case was the Id – {controller}/{action}/{id}. The htmlAttributes Dictionary is just a simple Key/Value pair of additional attributes required by the anchor tag. For the code example above to work you would need to replace the “Action” and “Controller” parameters with an actual Action name and Controller name, but other than that this provides a more robust solution to the problem.

MVC Sites using Windows Authentication trying to redirect to a login page

One of the issues I have encountered whilst developing MVC apps that require Windows Authentication was the problem that it keeps wanting to redirect to the login page when there isn’t one. The problem manifested itself when I was using IIS to Browse to my application running as a virtual directory under my default web site; it kept wanting to redirect to ‘~/Index’ and since my default route was not configured to find an Index Controller it would return a Page Not Found error.

After doing some digging I realised that the web.config file contained the following entry:

<authentication mode="Windows">
    <forms loginUrl="~/Index"/>
</authentication>

Replacing the loginUrl attribute with “~/Home/Index” solved the problem as it matched my default Route; but I knew that I wasn’t using forms authentication and had no login page. After doing some more research I came across a few sites which recommended adding the following keys to the <appSettings> section:

<add key="autoFormsAuthentication" value="false" />
<add key="enableSimpleMembership" value="false"/>

Having made the additions and removed the <forms> tag the site functioned as expected and the requests for the default route were handled correctly.