Robin Craig
Robin Craig

Reputation: 41

OAuth2 integration with Xero using Xojo and Chilkat

The old Chilkat forum appears to have moved here.

For years I've had Chilkat's OAuth1 working fine for integration with Xero (from Xojo). Now Xero are replacing OAuth1 with OAuth2. Xero say that once you successfully connect with 3-legged OAuth2, you can use the refresh token to keep reconnecting, without having to go through the 3-legged process (i.e. user agreement to allow your connection) each time. So once you have done the 3-legged once, you don't need to do it again: each unused Refresh token remains valid for 60 days (but can be used only once). So the process appears to be:

  1. 3-Legged connection, approve manually, retrieve Access and Refresh Tokens
  2. Use Refresh token, getting the required data plus a new Refresh token, without manual approval being needed.
  3. Repeat (2) forever, only needing to do (1) again if something goes wrong.

The Chilkat OAuth2 Xojo plugin help shows how to do (1). However I've no idea how to do (2). I'm far from expert in using REST (if I was, I wouldn't be using Chilkat ;-) ) so Xojo code preferred, but even pseudo-code would help!

Thanks

Upvotes: 2

Views: 296

Answers (1)

Robin Craig
Robin Craig

Reputation: 41

Thanks for the comments. I have now got this working so am posting my answer for anyone who has the same problems.

After linked your oauth2-using App in Xero Developer, the first step in oauth2 is the "3-legged" step where you have to have user approval. I have been unable to get the Chilkat example to work (redirect not allowed). However as you generally only need to do this once, it is easy enough to just use the xoauth Terminal script Xero provide. Basically you just:

  1. run xoauth setup, being sure to add scopes offline_access (so you get the vital refresh token) and whatever organisation data access scopes you need, e.g. accounting.transactions and accounting.contacts
  2. run xoauth connect
  3. copy and save the resulting access_token and refresh_token

Once you've done that, you can use the access_token in your App, refreshing it as required with the refresh_token (which is changed each time you do that).

Here's how, using the Chilkat plug-ins:

// Set up the Authorisation:

Dim oauth2 As New Chilkat.OAuth2
'Set up from saved data:
oauth2.ClientId = { Client id used in initial setup }
oauth2.AccessToken = { access_token retrieved in setup }
oauth2.RefreshToken = { refresh_token: first retrieved in setup, then from each refresh }
oauth2.TokenEndpoint = "https://identity.xero.com/connect/token"
yourTenantID = { the <OrganisationID> of the <Organisation> you will be connecting to }

Dim success As Boolean
Dim rest As Chilkat.Rest
rest.Authorization = "Bearer " + oauth2.AccessToken
success = rest.AddHeader( "xero-tenant-id", yourTenantID)

'Responses will be in json: if you want xml, add this:
success = rest.AddHeader( "accept", "application/xml" )

The rest object is now ready to use for interfacing with Xero. E.g. to get some data:

// Get some data:
'endpoint is the endpoint you want, e.g. "Accounts"
'identifier and condition are optional parameters, see Xero's API Previewer

Dim sbPath As New Chilkat.StringBuilder
success = sbPath.Append( "/api.xro/2.0/" + endpoint )

if success then
  if identifier>"" then
    success = sbPath.Append("/" + identifier)
  elseif condition>"" then
    success = rest.AddQueryParam( "where" ,condition )
  end if
end if
if not success then return rest.LastErrorText

//  Get the full or matching list:
Dim sbXml As New Chilkat.StringBuilder
success = rest.FullRequestNoBodySb( "GET", sbPath.GetAsString(), sbXml)
dim statuscode As Integer = rest.ResponseStatusCode
if statuscode = 401 then
    'access code expired: use refresh token to revive it and get new refresh token
    success = oauth2.RefreshAccessToken()
    if success then
        { Save the new oauth2.RefreshToken }
        { Disconnect and reconnect rest }
        success = rest.FullRequestNoBodySb( "GET", sbPath.GetAsString(), sbXml)
    end if
end if

if success then
  If (rest.ResponseStatusCode <> 200) Then   //  A 200 response is expected for actual success
    return sbXml.GetAsString()
  else  // get the response, in this case XML
    Dim bAutoTrim As Boolean = True
    success = xml.LoadSb(sbXml,bAutoTrim)
  end if
end if
if not success then return rest.LastErrorText

Upvotes: 2

Related Questions