Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my self-hosted WCF service returning 403 Forbidden, rather than 401 Unauthorized?

Tags:

c#

wcf

I've been trying to create a relatively straightforward self-hosted RESTful service with WCF, but I wanted to add custom authentication. To that end I tried overriding the UserNamePasswordValidator. Unfortunately, although this is being called to validate a username/password combination, if the username/password don't pass, the server returns a 403 Forbidden, rather than a 401 Unauthorized, as I'd expect. This will cause a big problem because if a user fails to authenticate the first time, they won't be prompted to enter credentials again unless they restart the browser. So, what am I doing wrong?

This is what I have so far :

(The actual ServiceContract contains a single method which returns a string)

class Program
{
    static void Main(string[] args)
    {
        WebServiceHost host = null;
        try
        {
            host = new WebServiceHost(typeof (MyService));
            const string uri = "http://127.0.0.1/MyService";
            var wb = new WebHttpBinding
                            {
                                Security =
                                    {
                                        Mode = WebHttpSecurityMode.TransportCredentialOnly,
                                        Transport = {ClientCredentialType = HttpClientCredentialType.Basic}
                                    }
                            };
            var ep = host.AddServiceEndpoint(typeof (IMyService), wb, uri);
            host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
            host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new PasswordValidator();
            host.Open();

            Console.WriteLine("Press any key to terminate");
            Console.ReadKey();
        }
        finally
        {
            if (host != null) host.Close();
        }
    }
}

public class PasswordValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        if (null == userName || null == password)
            throw new ArgumentNullException();

        if (userName == "Test" && password == "Password") return;
        throw new WebFaultException(HttpStatusCode.Unauthorized);
    }
}

I've already taken note of this other, similar question, but none of the answers posted there actually work. I'm hoping a more fully defined version of the question will elicit some better responses.

like image 302
adhocgeek Avatar asked Sep 14 '12 08:09

adhocgeek


1 Answers

Try this:

public override void Validate(string userName, string password) 
{ 
    if (null == userName || null == password)             
        throw new ArgumentNullException(); 
    if (userName == "Test" || password == "Password") return; 

    WebFaultException rejectEx = new WebFaultException(HttpStatusCode.Unauthorized);
    rejectEx.Data.Add("HttpStatusCode", rejectEx.StatusCode);
    throw rejectEx;
} 

As far as I know this is an undocumented technique which relies on how the WCF stack manages exceptions internally. There ought to be a documented way but I haven't found one.

like image 139
Chris Dickson Avatar answered Oct 03 '22 01:10

Chris Dickson