Issue with HTTPS, Basic Authentication and IIS7

Topics: Web Api
Feb 1, 2012 at 2:03 PM
Edited Feb 1, 2012 at 2:06 PM

Inspired by http://pfelix.wordpress.com/2011/04/21/wcf-web-api-self-hosting-https-and-http-basic-authentication/#comment-232 from Pedro Felix, we've put together a working IIS7 implementation with Basic Authentication and HTTPS.

It's mostly working as expected, except one very strange issue in our HttpServiceHostFactory mapping. When we switch to HTTPS (HttpBindingSecurityMode.Transport), the following error message shows up on the screen. 

The provided URI scheme 'http' is invalid; expected 'https'.
Parameter name: context.ListenUriBaseAddress

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ArgumentException: The provided URI scheme 'http' is invalid; expected 'https'.
Parameter name: context.ListenUriBaseAddress

If we manually alter (hacks) the baseURL before creating the EndPoint, everything works like a charm. See sample code below for creating a single endpoint using the URL-hack.

This is of course not very sustainable, and hopefully there is way to configure the framework to handle this automatically.

public class MyServiceHostFactory : HttpServiceHostFactory {

protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) {       

var host = base.CreateServiceHost(serviceType, baseAddresses) as HttpServiceHost;

var binding = new HttpBinding(HttpBindingSecurityMode.Transport);       
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;       
//var baseurl = baseAddresses[0];  /When using this option, mapping error occurs       
var baseurl = new Uri(baseAddresses[0].OriginalString.Replace("http", "https").Replace(":7000",""));                   
var ep = host.AddServiceEndpoint(serviceType, binding, baseurl);       
ep.Behaviors.Add((new HttpBehavior { HelpEnabled = true, TestClientEnabled = true, OperationHandlerFactory = new MyOperationHandlerFactory() }));

return host; }}

 

Feb 1, 2012 at 3:28 PM

This is from my HttpConfiguration but it demonstrates what you need to do:

Security = (uri, bindingSecurity) => bindingSecurity.Mode = uri.Scheme.ToLower() ==
"https" ? HttpBindingSecurityMode.Transport : HttpBindingSecurityMode 
.None,

Feb 1, 2012 at 4:47 PM

Thanks David

This works well with RouteCollection.MapServiceRoute since it has a HttpConfiguration parameter.

I'm currently doing the following when creating/adding my Route with the above factory.

MyServiceHostFactory sf = new MyServiceHostFactory()
routes.Add(new ServiceRoute("api/users", sf, typeof(UserApi)));

How can I combine my approach with your feedback above.

Feb 2, 2012 at 6:14 AM
larsm wrote:

Thanks David

This works well with RouteCollection.MapServiceRoute since it has a HttpConfiguration parameter.

I'm currently doing the following when creating/adding my Route with the above factory.

MyServiceHostFactory sf = new MyServiceHostFactory()
routes.Add(new ServiceRoute("api/users", sf, typeof(UserApi)));

How can I combine my approach with your feedback above.


I obviously couldn't test this but here's what I would do given the source you supplied:

	public class MyServiceHostFactory : HttpServiceHostFactory
	{
		protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
		{
			var host = base.CreateServiceHost(serviceType, baseAddresses) as HttpServiceHost;

			foreach (Uri baseAddress in baseAddresses)
			{
				HttpBinding binding;
				if (baseAddress.Scheme.ToLower() == "https")
				{
					binding = new HttpBinding(HttpBindingSecurityMode.Transport);
					binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
				}
				else
				{
					binding = new HttpBinding(HttpBindingSecurityMode.None);
				}

				ServiceEndpoint endpoint = host.AddServiceEndpoint(serviceType, binding, baseAddress);
				endpoint.Behaviors.Add((new HttpBehavior { HelpEnabled = true, TestClientEnabled = true, OperationHandlerFactory = new MyOperationHandlerFactory() }));
			}

			return host;
		}
	}

Hope that helps.

Feb 2, 2012 at 3:08 PM

Thanks,  works like a charm.

/LM