I want to secure my selfhosted service on mono with basic authentication. This works with an Authorization header in the http request and can be setup with configuration for the binding. The setting BasicHttpSecurityMode.TransportCredentialOnly
only encrypts the header and no SSL certificate is needed. See code below.
[ServiceContract] public interface IPrintable { [OperationContract] void Print(string textToPrint); } // service implementation public class PrintService : IPrintable { public void Print(string textToPrint) { Console.WriteLine(textToPrint); } } // selfhosted static void Main(string[] args) { // address for the service var baseAddress = String.Format("http://{0}:1234", Environment.MachineName); var host = new ServiceHost(typeof(PrintService), new Uri(baseAddress)); // binding with encrypted usename/password in header var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly); binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; // create endpoint for serviceontract var endpoint = host.AddServiceEndpoint(typeof(IPrintable), binding, "/PrintService"); // start the service host.Open(); // wait for user to stop service Console.WriteLine("Host opened. Press <CR> to end."); Console.ReadLine(); // stop the service host.Close(); }
Basic authentication works as follows:
- The client sends a request
- The server responds with a HTTP401 (Unauthorized) and the accepted authentication methods in the header [WWW-Authenticate: Basic realm=””]
- The client resends the request with the Authorization header containing the username and password [Authorization: Basic VGVzdDpQYXNzd29yZA==]
- The server processes the Authorization header and services the request, the response is a HTTP200 (OK)
Hosting the service on mono 2.6.7 this works as described. But on mono 2.10.8 this is broken. Inspecting with fiddler showed the server responds with a HTTP401 in step 4 without the accepted authentication methods in the header.
See client code below and try for yourself.
// client static void Main(string[] args) { // address the service is listening on var url = String.Format("http://{0}:1234/PrintService", Environment.MachineName); var address = new EndpointAddress(new Uri(url)); // binding with username/password in header var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly); binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; // contract var factory = new ChannelFactory<IPrintable>(binding, address); // create the proxy with username and password factory.Credentials.UserName.UserName = "Test"; factory.Credentials.UserName.Password = "Password"; var proxy = factory.CreateChannel(); // invoke the print operation proxy.Print("Hello world"); }
Submitted bug 4993 to xamarin.
It may indeed encode your header without using SSL, but it is not safe to use as anyone can easily intercept the message and replay it. Furthermore, the name and password is encode using base64 encoding. You can easily see the password and username by decrypting the encoded string, for example https://www.base64decode.org/ will do this for you.
The “VGVzdDpQYXNzd29yZA==” string is resolved as “Test:Password”.
If you tend to make things secure, a choose different security path by adding Transport security (HTTPS) or using Digest authentication.
You’re right to assume basic authentication must be over https when securing public services.
Using https requires a certificate and some setup, but is not what this post is about.
This post is about getting the header for basic authentication added to the message.
Hi erictummers,
Could you provide project files from the examples you described in this post? I have tried your codes on mono, but I can not make it work on mono.
Complete files MonoBasicAuthenticationWCF.zip
Compile with following lines:
Hi erictummers,
Many thanks,
Vitor Moscon