Jump to content

Using Indy with Xero API

Recommended Posts

This is a Webbroker & Indy implementation with Xero API in Delphi 10.4 Patch 1

I'm just wondering why I get a 200 OK in the page when this code is process, but the actual response of the Idhttp.post is a Bad Request. My code looks like this:

    my_http := TIdHTTP.Create(nil);
    sCode := 'codehere234asfds3423';

    //tempString is Basic + base64 encoding of clientid & clientsecret
    tempString := StringReplace(UrlEncode(sClientID + ':' + sClientSecret),#$D#$A, '', [rfReplaceAll]);
    Parameters := TStringList.Create;
    Parameters.Add('code=' + sCode);
    Parameters.Add('redirect_uri=' + 'http://localhost:30001/testXeroLogin');

      my_http.Request.CharSet := 'utf-8';
      my_http.Request.UserAgent := 'DataBizApp';

      my_http.Request.BasicAuthentication := false;

      ssl := TIdSSLIOHandlerSocketOpenSSL.Create(my_http);
      ssl.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
      my_http.IOHandler := ssl; 
      my_http.Request.Accept := 'application/json';
      my_http.Request.ContentType := 'application/x-www-form-urlencoded';

      my_http.Request.CustomHeaders.FoldLines := false;
      my_http.Request.CustomHeaders.AddValue('Authorization', tempString);
      post_response := my_http.Post('https://identity.xero.com/connect/token', Parameters);
	  Response.Content := '<HTML><BODY>' + post_response + '</BODY></HTML>';
      on e: Exception do
        Logfile(ClassName + '.testProcess: ' + e.Message);
        Logfile('Post Response: ' + post_response);


I set BasicAuthentication to False since the Authorization needs to be encoded in Base64. I hope I am right with what I'm doing. Any help would be appreciated. Thank you.

Share this post

Link to post
12 hours ago, misc_bb said:

I'm just wondering why I get a 200 OK in the page when this code is process, but the actual response of the Idhttp.post is a Bad Request.

Do you mean your WebBroker server is sending back a 200 response to your clients?  That is likely because you are not reporting any error to WebBroker, so it thinks the client's request is successful.  If TIdHTTP receives a HTTP "400 Bad Request" response, it will raise an EIdHTTPProtocolException exception, unless you explicitly tell it not to.  But, you are swallowing ALL exceptions raised by TIdHTTP, so WebBroker won't know about them.  Your exception handler should either re-raise any caught exception, or at least populate WebBroker's Response with an appropriate error code and content.


Does the body content of Xero's response say WHY the request is bad?  I suspect it is because of the issue below...

12 hours ago, misc_bb said:

I set BasicAuthentication to False since the Authorization needs to be encoded in Base64.

That is not what BasicAuthentication means.  Setting that property to False tells TIdHTTP that it is not allowed to fallback to using HTTP's "BASIC" authentication scheme if a more appropriate scheme is not selected, after TIdHTTP has looked at a response's "WWW-Authenticate" header, matched it to any registered TIdAuthentication classes, and fired its On(Select)Authorization events to allow you to customize the handling.


Since you are using the TIdHTTP.Request.CustomHeaders property to provide authentication credentials manually, you are responsible for ensuring the proper formatting of that data.  Which, if Xero is really expecting BASIC authentication, as your code suggests, then your credential data is NOT being formatted properly.  That could easily cause a 400 error.  BASIC does not use url-encoding at all.  And even if it did, your formatted string would still be malformed, as it lacks the required 'Basic ' prefix.


For proper BASIC formatting, you would need to change this:

tempString := StringReplace(UrlEncode(sClientID + ':' + sClientSecret),#$D#$A, '', [rfReplaceAll]);   

To this instead:

// TIdEncoderMIME is Indy's Base64 encoder
tempString := 'Basic ' + TIdEncoderMIME.EncodeString(sClientID + ':' + sClientSecret, IndyTextEncoding_UTF8);

But, if Xero really expects BASIC authentication, you don't need to use the CustomHeaders property for that, as TIdHTTP has built-in support for BASIC.  Simply set BasicAuthentication to True, and then set the TIdHTTP.Request.UserName and TIdHTTP.Request.Password properties as needed.  Let TIdHTTP handle the formatting of the request's "Authorization" header for you:

my_http.Request.BasicAuthentication := True;
my_http.Request.UserName := sClientID;
my_http.Request.Password := sClientSecret;


Share this post

Link to post

Thank you Remy.


For the BASIC formatting my UrlEncode function includes already the BASIC keyword but I'll definitely try the Indy base64 encoder.


I was expecting that Xero will more details as to why I got a Bad Request but not much. I'm suspecting it has something to do with the Basic Authentication parameters with Indy. I'll give this a try.


With regards to enabling the Basic Authentication to True, does this mean that the Username and Password are automatically encoded in base64? I did read your comments on this in Stackoverflow but I was hesitant because Xero expects it to be encoded.


Thank you very much!

Share this post

Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now