How to authenticate in WCF services in BasicHttpBinding?

nRk picture nRk · Nov 24, 2009 · Viewed 16.2k times · Source

I am developing WCF services with basicHttpBinding, these services should be accessible using .net 1.1 & .net 2.0, for this purpose I am using basicHttpBinding.
In old ASMX web services I assed one Soap Header (AuthHeader) to authenticate the user every request.

How Can I authenticate in WCF using basicHttpBinding? Any sample Or tutorial will helpfull.


nRk

Answer

fspirit picture fspirit · Dec 18, 2009

You can use AuthHeader as you did before switching to WCF. Maybe it will be more convinient for you, cause the princples will remain the same. The bad thing i see in this solution is a plain text password transfer. Anyway, it's just another option and you can encrypt/decrypt the password somehow.

In this case you should implement your own your IDispatchMessageInspector & IClientMessageInspector, like

[AttributeUsage(AttributeTargets.Class)]
public class CredentialsExtractorBehaviorAttribute : Attribute, IContractBehavior, IDispatchMessageInspector
{
    #region IContractBehavior implementation.

    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint,
                                      DispatchRuntime dispatchRuntime)
    {
        dispatchRuntime.MessageInspectors.Add(this);
    }

    ... empty interface methods impl skipped ...

    #endregion

    #region IDispatchMessageInspector implementation.

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        int i = request.Headers.FindHeader("username", "sec");
        if (-1 != i)
        {
            string username = request.Headers.GetHeader<string>("username", "sec");
            ... do smth ...
        }
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        return;
    }

    #endregion
}

In a sample i placed to header only username, but you can implement your a class containing username and password and use it instead of string. On the client:

internal class CredentialsInserter : IContractBehavior, IClientMessageInspector
{
    private string m_username;

    public CredentialsInserter(string username)
    {
        m_username = username;
    }

    #region IContractBehavior implementation.

    ... empty interface methods impl skipped ...

    public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint,
                                    ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(this);
    }

    #endregion

    #region IClientMessageInspector implementation.

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        MessageHeader<string> mh = new MessageHeader<string>(m_username);
        request.Headers.Add(mh.GetUntypedHeader("username", "sec"));
        return null;
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        return;
    }

    #endregion
}

Then you should place attribute CredentialsExtractorBehaviorAttribute on your service implementation class.

[CredentialsExtractorBehavior]
public class DummyService : IDummyService
{
   ... impl ...
}

And on the client side you should do the following:

        using (DummyServiceClient c = new DummyServiceClient("TcpEndpoint"))
        {
            c.ChannelFactory.Endpoint.Contract.Behaviors.Add(
                new CredentialsInserter("_username_"));
            c.DummyMethod();
        }