23
votes

Server:

<system.serviceModel>
    <services>
        <service name="Service" behaviorConfiguration="md">
            <!-- Service Endpoints -->
            <endpoint address="SslService" binding="basicHttpBinding" bindingConfiguration="security" contract="IService"/>
            <host>
                <baseAddresses>
                    <add baseAddress="https://pc:8080/Service.svc"/>
                </baseAddresses>
            </host>
        </service>
    </services>
    <bindings>
        <basicHttpBinding>
            <binding name="security">
                <security mode="Transport">
                    <transport clientCredentialType="Basic"/>
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <behaviors>
        <serviceBehaviors>
            <behavior name="md">
      <serviceCredentials>
        <userNameAuthentication
          userNamePasswordValidationMode="Custom"
          customUserNamePasswordValidatorType="ClassLibrary1.CustomUserNameValidator, ClassLibrary1" />
      </serviceCredentials>
                <serviceMetadata httpsGetEnabled="true"/>
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

ClassLibrary1.CustomUserNameValidato:

public class CustomUserNameValidator : System.IdentityModel.Selectors.UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            if (userName != "111" || password != "111")
            {

                throw new System.ServiceModel.FaultException("Unknown username or incorrect password");
            }
        }
    }

Client:

<system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpBinding_IService" closeTimeout="00:01:00"
                openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                useDefaultWebProxy="true">
                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                    maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                <security mode="Transport">
                    <transport clientCredentialType="Basic" proxyCredentialType="Basic" realm="">
                        <extendedProtectionPolicy policyEnforcement="Never" />
                    </transport>
                    <message clientCredentialType="UserName" algorithmSuite="Default" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <client>
        <endpoint address="https://pc:8080/Service.svc/SslService" binding="basicHttpBinding"
            bindingConfiguration="BasicHttpBinding_IService" contract="ServiceReference1.IService"
            name="BasicHttpBinding_IService" />
    </client>
</system.serviceModel>

ServiceReference1.ServiceClient s = new WindowsFormsApplication1.ServiceReference1.ServiceClient();

s.ClientCredentials.UserName.UserName = "111";
s.ClientCredentials.UserName.UserName = "111";
MessageBox.Show(s.GetData(3)); // <---- ERROR

The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Basic realm="pc"'.

6

6 Answers

17
votes

I had created a client like this:

using (var client = new Client())
    {

    client.ClientCredentials.UserName.UserName = <username>;
    client.ClientCredentials.UserName.Password = **<WRONG_PASSWORD>**;
...
    }

The security section of my binding looked like this:

          <security mode="Transport">
            <transport clientCredentialType="Basic" proxyCredentialType="Basic" realm="" />
          </security>

And I saw this error come back. Once I corrected the password, everything worked.

10
votes

I'm assuming you are hosting your servicehost on the IIS. Then the problem is that the IIS intercepts the https request and performs IIS-level authentication before the WCF framework and your custom validator has a chance to kick in.

In your example, the IIS will actually look for a local user '111' with password '111' on the server running the IIS. Try creating this user on the server, and you will probably get a different result.

One solution is to host your WCF servicehost somewhere else, for example in a Windows Service. Another solution is to change your security scheme to TransportWithMessageCredential. Finally, you could check this OSS http module out: Custom Basic Authentication for IIS - seems to do the trick we need.

4
votes

Try to send username and password not in http with basic authentication (this can embarrass IIS), but only in soap-message headers with following scheme:

<binding name="...">
        <security mode="TransportWithMessageCredential" >
           <message clientCredentialType="UserName" />
        </security>
</binding>

How to: Use Transport Security and Message Credentials

Maybe you also need to additionally specify <transport clientCredentialType="None">

3
votes

I posted an answer here: Can not call web service with basic authentication using WCF transport clientcredentialType is TransportCredentialOnly

1
votes

Looks like you set the user name twice instead of the user name and password.

When you have basic authentication and you do not send the username and password with the request you get a challenge response back.

0
votes

I applied all the things which are above mentioned but my problem is not solved.

In my case this is happening because of proxy server. Then I removed all the proxy and run my web service. Then it is working fine.

Hope this will you!!!!!!