55

I'm trying to write a web service client in c# which the webservice is Java Axis 1.4. Axis service requires the Authorization: Basic Base64EncodedToken header value in the HTTP Headers. I can't find a way to set this header in standart ways of consuming web services in visual studio.net, like normal WSDL generated refernce nor with WSE3.0

I can't use WCF as the project is developed using .net 2.0.

Is there any way to do this ?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
UmutKa
  • 759
  • 2
  • 6
  • 11

7 Answers7

52

It seems the original author has found their solution, but for anyone else who gets here looking to add actual custom headers, if you have access to mod the generated Protocol code you can override GetWebRequest:

protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
  System.Net.WebRequest request = base.GetWebRequest(uri);
  request.Headers.Add("myheader", "myheader_value");
  return request;
}

Make sure you remove the DebuggerStepThroughAttribute attribute if you want to step into it.

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
user334291
  • 536
  • 5
  • 2
  • This was exactly what I needed. The Web Service was returning 500 instead of 401 for Unauthorized, so the credentials were never sent using the approach in the accepted answer. While I feel dirty modifying generated code, this allowed me to force send them with the first request. See http://www.west-wind.com/weblog/posts/243915.aspx. – g . Dec 24 '10 at 11:40
  • 2
    Hey, that's seems like exactly what I need. but where should I put this function? – Roee Gavirel Dec 19 '11 at 14:43
  • but can't a header addition have multiple comma seperated values in the value portion for the header you're adding? That too is very important to know such as for OAuth Authorization params – PositiveGuy Feb 28 '12 at 20:56
  • 3
    I've put this in a partial class along with the "reference.cs" file, so that if it is regnerated it will not be lost. – Brady Moritz Jun 22 '12 at 19:09
  • I tried this out, works like a charm. Just had to create a partial class with same name as proxy and under same namespace as the webservice client proxy. and add this method inside that class. – rDroid May 22 '19 at 10:26
29

Are we talking WCF here? I had issues where the service calls were not adding the http authorization headers, wrapping any calls into this statement fixed my issue.

  using (OperationContextScope scope = new OperationContextScope(RefundClient.InnerChannel))
  {
            var httpRequestProperty = new HttpRequestMessageProperty();
            httpRequestProperty.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " +
            Convert.ToBase64String(Encoding.ASCII.GetBytes(RefundClient.ClientCredentials.UserName.UserName + ":" +
            RefundClient.ClientCredentials.UserName.Password));
            OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;

            PaymentResponse = RefundClient.Payment(PaymentRequest);
   }

This was running SOAP calls to IBM ESB via .NET with basic auth over http or https.

I hope this helps someone out because I had massive issues finding a solution online.

robnick
  • 1,720
  • 17
  • 27
Simon RC
  • 391
  • 3
  • 3
  • 2
    This was actually my problem. Somehow when you set the credentials in the proxy, it doesn't work. Thanks! – Tawani Dec 02 '11 at 16:03
  • I'm using WCF/Soap, so this worked really well for me. In my case I needed a custom header called "Auth-Token". So my (VB.NET) code was --> httpRequestProperty.Headers.Add("Auth-Token", "xxxxx") – robnick Jan 14 '16 at 05:42
  • This solved my problems also. I had the same problem as Tawani, when i set the credentials through the proxy i got back 401. But with your code everything works perfectly! Big thanks! – merger May 24 '17 at 09:54
  • Excellent! Worked for me to set the Authorization header to include a Bearer token. – Mike Taverne May 25 '17 at 23:03
  • Hmmm... works great in the debugger, but after deploying to Azure, "The value of OperationContext.Current is not the OperationContext value installed by this OperationContextScope." – Mike Taverne May 26 '17 at 01:00
  • Thanks! Taking yours as base, I've used a slightly different solution to add x-api-key to the HTTP headers of the request and it worked. – jcs Mar 03 '20 at 18:17
18

Instead of modding the auto-generated code or wrapping every call in duplicate code, you can inject your custom HTTP headers by adding a custom message inspector, it's easier than it sounds:

public class CustomMessageInspector : IClientMessageInspector
{
    readonly string _authToken;

    public CustomMessageInspector(string authToken)
    {
        _authToken = authToken;
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        var reqMsgProperty = new HttpRequestMessageProperty();
        reqMsgProperty.Headers.Add("Auth-Token", _authToken);
        request.Properties[HttpRequestMessageProperty.Name] = reqMsgProperty;
        return null;
    }

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


public class CustomAuthenticationBehaviour : IEndpointBehavior
{
    readonly string _authToken;

    public CustomAuthenticationBehaviour (string authToken)
    {
        _authToken = authToken;
    }
    public void Validate(ServiceEndpoint endpoint)
    { }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    { }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    { }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.ClientMessageInspectors.Add(new CustomMessageInspector(_authToken));
    }
}

And when instantiating your client class you can simply add it as a behavior:

this.Endpoint.EndpointBehaviors.Add(new CustomAuthenticationBehaviour("Auth Token"));

This will make every outgoing service call to have your custom HTTP header.

Saeb Amini
  • 23,054
  • 9
  • 78
  • 76
  • 2
    Saeb Amini, thank you. This was the answer I was looking for to solve my issue using a 3rd party web service that had changed over to 2-factor authentication. Their API was provided to us by XSDs and WSDLs which we generated the client code via the svcutil program. Using the code above allowed me to add the behavior to inject the HTTP header with the token to do the 2-factor authentication. Thanks again! – bolski Sep 22 '17 at 17:30
  • @bolski great to know it's helped you! :) – Saeb Amini May 23 '18 at 22:47
  • 1
    I have added my header in the above solution and its worked well. I faced the issue for adding security header in Soap Request. Thanks! – Jayakumar Thangavel Oct 02 '19 at 08:27
  • I ported my .Net Framwork code to .Net Core and was having issues with my `CustomBehavior` class. All I had to do was remove a few class inheritance that are apparently not needed in .Net Core. Thanks for adding ONLY the code needed! – Barns Jun 02 '21 at 19:09
7

If you want to send a custom HTTP Header (not a SOAP Header) then you need to use the HttpWebRequest class the code would look like:

HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Headers.Add("Authorization", token);

You cannot add HTTP headers using the visual studio generated proxy, which can be a real pain.

John Hunter
  • 4,042
  • 4
  • 26
  • 35
5

I find this code and is resolve my problem.

http://arcware.net/setting-http-header-authorization-for-web-services/

protected override WebRequest GetWebRequest(Uri uri)
{
    // Assuming authValue is set from somewhere, such as the config file
    HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);
    request.Headers.Add("Authorization", string.Format("Basic {0}", authValue));
    return request;
}
CoolBeans
  • 20,654
  • 10
  • 86
  • 101
1

user334291's answer was a life saver for me. Just want to add how you can add what the OP originally intended to do (what I ended up using):

Overriding the GetWebRequest function on the generated webservice code:

protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
    System.Net.WebRequest request = base.GetWebRequest(uri);          
    string auth = "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(this.Credentials.GetCredential(uri, "Basic").UserName + ":" + this.Credentials.GetCredential(uri, "Basic").Password));
    request.Headers.Add("Authorization", auth);
    return request;
}

and setting the credentials before calling the webservice:

  client.Credentials = new NetworkCredential(user, password);       
Tiago Martins
  • 727
  • 6
  • 12
0

Here is what worked for me:

protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
        HttpWebRequest request;
        request = (HttpWebRequest)base.GetWebRequest(uri);
        NetworkCredential networkCredentials =
        Credentials.GetCredential(uri, "Basic");
        if (networkCredentials != null)
        {
            byte[] credentialBuffer = new UTF8Encoding().GetBytes(
            networkCredentials.UserName + ":" +
            networkCredentials.Password);
            request.Headers["Authorization"] =
            "Basic " + Convert.ToBase64String(credentialBuffer);
            request.Headers["Cookie"] = "BCSI-CS-2rtyueru7546356=1";
            request.Headers["Cookie2"] = "$Version=1";
        }
        else
        {
            throw new ApplicationException("No network credentials");
        }
        return request;
}

Don't forget to set this property:

service.Credentials = new NetworkCredential("username", "password");  

Cookie and Cookie2 are set in header because java service was not accepting the request and I was getting Unauthorized error.

  • I got rid of above overload and only this code did all magic: serviceClient.CookieContainer = new CookieContainer(); – Attiq Rehman Aug 07 '13 at 16:11