0

I've done most of the work based on other people's code on stack overflow. See below.

My current issue is that I'm still getting authorization failure and I can clearly see why. v2 API requires X-IBM-Client-Id and X-IBM-Client-Secret to be passed as well as SOAP security header. However, I don't know how to inject it while using service created from Wsdl file.

Solved problems:

Overcame namespaces problem (using message formatter Consume WCF Royal Mail API in c# Console Application).

Solved binding configuration problem which results in two security headers. Also, you must set maxReceivedMessageSize if you want to get no exception while retrieving label.Final binding:

<system.serviceModel> <bindings> <basicHttpBinding> <binding name="basicBindingTransport" maxReceivedMessageSize="2147483647"> <security mode="Transport"> </security> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="https://api.royalmail.net/shipping/v2" binding="basicHttpBinding" bindingConfiguration="basicBindingTransport" contract="RoyalMailApiWsdl.shippingAPIPortType" name="shippingAPIPort" /> </client> </system.serviceModel>

Solved E0007 Authorization Failure.

Solved The HTTP request is unauthorized with client authentication scheme 'Anonymous' (you must use the binding as above with security "Transport" and inject credentials directly into http post header itself (See my answer below).

And many other issues, which I cannot remember now. I hope this post will help others.

Community
  • 1
  • 1
Povilas Panavas
  • 329
  • 1
  • 2
  • 10

1 Answers1

1

To implement double authentification in the Shipping API v2, you can use this code (the idea taken from How to add custom Http Header for C# Web Service Client consuming Axis 1.4 Web service)

shippingAPIPortTypeClient client = GetProxy();
<..>
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
    var httpRequestProperty = new HttpRequestMessageProperty();
    httpRequestProperty.Headers.Add(@"X-IBM-Client-Id", _credentials.HttpSecurity.ClientId);
    httpRequestProperty.Headers.Add(@"X-IBM-Client-Secret", _credentials.HttpSecurity.ClientSecret);
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;

    createShipmentResponse response = client.createShipment(GetSecurityHeaderType(), request);
    return response;
}

private shippingAPIPortTypeClient GetProxy()
{
    // binding comes from configuration file
    var shippingClient = new shippingAPIPortTypeClient();

    shippingClient.ClientCredentials.UserName.UserName = _credentials.SoapSecurity.Username;
    shippingClient.ClientCredentials.UserName.Password = _credentials.SoapSecurity.Password;

    shippingClient.ClientCredentials.UseIdentityConfiguration = true;

    foreach (OperationDescription od in shippingClient.Endpoint.Contract.Operations)
    {
        od.Behaviors.Add(new RoyalMailIEndpointBehavior());
    }

    return shippingClient;
}

private SecurityHeaderType GetSecurityHeaderType()
{
    SecurityHeaderType securityHeader = new SecurityHeaderType();

    DateTime created = DateTime.Now;

    string creationDate;
    creationDate = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");

    string nonce = nonce = (new Random().Next(0, int.MaxValue)).ToString();

    byte[] hashedPassword;
    hashedPassword = GetSHA1(_credentials.SoapSecurity.Password);

    string concatednatedDigestInput = string.Concat(nonce, creationDate, Encoding.Default.GetString(hashedPassword));
    byte[] digest;
    digest = GetSHA1(concatednatedDigestInput);

    string passwordDigest;
    passwordDigest = Convert.ToBase64String(digest);

    string encodedNonce;
    encodedNonce = Convert.ToBase64String(Encoding.Default.GetBytes(nonce));

    XmlDocument doc = new XmlDocument();
    using (XmlWriter writer = doc.CreateNavigator().AppendChild())
    {
        writer.WriteStartDocument();
        writer.WriteStartElement("Security");
        writer.WriteStartElement("UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        writer.WriteElementString("Username", _credentials.SoapSecurity.Username);
        writer.WriteElementString("Password", passwordDigest);
        writer.WriteElementString("Nonce", encodedNonce);
        writer.WriteElementString("Created", creationDate);
        writer.WriteEndElement();
        writer.WriteEndElement();
        writer.WriteEndDocument();
        writer.Flush();
    }

    doc.DocumentElement.RemoveAllAttributes();

    System.Xml.XmlElement[] headers = doc.DocumentElement.ChildNodes.Cast<XmlElement>().ToArray<XmlElement>();

    securityHeader.Any = headers;

    return securityHeader;

}

On top of that complete working Royal Mail Shipping API v2 solution in C# is available here: https://github.com/povilaspanavas/RoyalMailShippingApiV2

It has three tests, one to create local (GB) shipment, another international, and the third to retrieve label pdf file.

Community
  • 1
  • 1
Povilas Panavas
  • 329
  • 1
  • 2
  • 10
  • I just had a look at your solution on github. It doesn't look like it has a 'working' project. Is that correct? Am I correct in thinking that to use it I would need to create a new project and then call the RoyalMailApiCredentials and RoyalMailApiClient classes from that? Thanks. – jimmy Nov 13 '17 at 09:58
  • @jimmy i'm not sure what you mean by "working project". This is a library to use in your software. However, as the documentation states, just go to the test project, put your credentials and you can test both creating shipments and printing labels. – Povilas Panavas Nov 13 '17 at 10:10
  • Thanks for the answer, I did try following the documentation, but got the error. "A project with an output type of Class Library cannot be started directly". What I meant by working project is a simple executable project so the code could run. From the documentation it sounded like it should just run 'out of the box' so I mainly wanted to be sure I hadn't messed up my version of the code – jimmy Nov 14 '17 at 07:29
  • Well, you just need to execute the tests. There are two projects, the second on is tests. Executing them is the same as running exe. It will run the code, and make the calls to Royal Mail Api. Open the test, right click and click "Run tests". Simple as that if you use Visual Studio. – Povilas Panavas Nov 16 '17 at 18:38