Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#3.5

WCF Client For Web Services that Require Authorization by HTTP Header

4.17/5 (11 votes)
24 Dec 2023CPOL2 min read 41.8K  
Using OutgoingMessageProperties and OperationContextScope to implement authorization by http header for WCF client that communicates with non WCF-based web services.
This document navigates WCF-based client connections, demonstrating adaptation for third-party web services demanding custom HTTP header-based authorization, enriching interconnectivity methods.

Introduction

Assume you are communicating with a WCF-based service using WCF-based client.

Due to security reasons, client connection usually requires authorization, so the client should provide some credentials for web service.

Basic Authentication mechanism for WCF is described here.

As you can see after creating a WCF client cc for WCF service "Calculator"...

C#
CalculatorClient cc = new CalculatorClient(myBinding, ea);

...it's possible to pass credentials like this:

C#
cc.ClientCredentials.UserName.UserName = "MyUserName";
cc.ClientCredentials.UserName.Password = "MyPassword";

It's quite simple and works fine if client communicates with web service that provides the same authentication mechanism.

However, sometimes you need to connect to third-party web services which provide their own authentication. Besides that, third-party web services can be non WCF-based at all.

In that case, you can still use WCF-based client, but you may need to write some additional code.

Assume the third-party web service requires authorization by http header.

As per this link, it means that client should provide the Authorization header that looks like:

Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l

where QWxhZGRpbjpPcGVuU2VzYW1l is Base64-encoded string that contains credentials - username and password formatted as "username:password" before ones were Base64-encoded.

Basic Authentication mechanism for WCF will not work in that case because WCF client data is encapsulated inside the http request.

That is WCF client data in XML format (envelop) is located in the "data" part of http request, so no one http header can be modified by that data.

The solution to a problem is using OutgoingMessageProperties and OperationContextScope.

Using the Code

Assume you already have WCF-based client cc which communicates with a third-party service Calculator that requires authorization by http header and you want to call method Add mentioned here.

Use this code to do it:

C#
using (new CalculatorOperationContextScope(cc.InnerChannel).ClientChannel(basicHttpAuth))
{
     cc.Add(100, 1);
}

where:

C#
string basicHttpAuth =
string.Format
(
  "Basic {0}", Convert.ToBase64String(new UTF8Encoding().GetBytes("MyUserName:MyPassword"))
);

For .NET 4.6 or greater, you can use:

C#
string basicHttpAuth =
$"Basic {Convert.ToBase64String(new UTF8Encoding().GetBytes("MyUserName:MyPassword"))}";

and CalculatorOperationContextScope is a class that implements authorization by http header:

C#
public class CalculatorOperationContextScope
{
    private readonly OperationContextScope m_scope;

    public CalculatorOperationContextScope(IClientChannel channel)
    {
       m_scope = new OperationContextScope(channel);
    }

    public IDisposable ClientChannel(string basicHttpAuth, 
                                     string customHttpHeaderName = null,
                                     string customHttpHeaderValue = null)
    {
       return new ClientChannelItem
       (m_scope, basicHttpAuth, customHttpHeaderName, customHttpHeaderValue);
    }

    private class ClientChannelItem : IDisposable
    {
       private readonly OperationContextScope m_scope;

       public ClientChannelItem(OperationContextScope scope, string basicHttpAuth,
                                  string customHttpHeaderName = null,
                                  string customHttpHeaderValue = null)
       {
          m_scope = scope;

          HttpRequestMessageProperty httpRequestProperty = 
                                     new HttpRequestMessageProperty();

          httpRequestProperty.Headers[HttpRequestHeader.Authorization] = basicHttpAuth;

          if (!string.IsNullOrEmpty(customHttpHeaderName) && 
                                    customHttpHeaderName.Trim().Length > 0)
            httpRequestProperty.Headers.Add(customHttpHeaderName, customHttpHeaderValue);

          OperationContext.Current
          .OutgoingMessageProperties[HttpRequestMessageProperty.Name] = 
                                                       httpRequestProperty;
       }

       public void Dispose()
       {
          m_scope.Dispose();
       }
    }
}

In certain cases, third-party service can require additional http header(s) with specific name(s).

You can pass header name customHttpHeaderName and header value customHttpHeaderValue as optional parameters.

If you don't need support for such headers, simply remove that from code.

The check...

C#
!string.IsNullOrEmpty(customHttpHeaderName) && customHttpHeaderName.Trim().Length > 0

...is for backward compatibility with .NET 3.5.

For .NET 4.0 or greater, you can check:

C#
!string.IsNullOrWhiteSpace(customHttpHeaderName)

History

  • 25th December, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)