Basic authentication in selfhosted mono service

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:

  1. The client sends a request
  2. The server responds with a HTTP401 (Unauthorized) and the accepted authentication methods in the header [WWW-Authenticate: Basic realm=””]
  3. The client resends the request with the Authorization header containing the username and password [Authorization: Basic VGVzdDpQYXNzd29yZA==]
  4. 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. fiddler screenprint of failing basic authentication on mono 2.10.8
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.

About erictummers

Working in a DevOps team is the best thing that happened to me. I like challenges and sharing the solutions with others. On my blog I’ll mostly post about my work, but expect an occasional home project, productivity tip and tooling review.
This entry was posted in Development and tagged , , , , , , , , , . Bookmark the permalink.

6 Responses to Basic authentication in selfhosted mono service

  1. erictummers says:
    // solution by adding ServiceCredentials behavior
    var cred = new ServiceCredentials();
    cred.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
    cred.UserNameAuthentication.CustomUserNamePasswordValidator = UserNamePasswordValidator.None;
    host.Description.Behaviors.Add(cred);
    
  2. 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.

    • erictummers says:

      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.

  3. Vitor Moscon says:

    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.

Leave a Reply to erictummers Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.