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