Migrating from Xero OAuth 1 to OAuth 2.0

Caleb Hines -

A guide on how to convert to Xero OAuth 2.0

In this article I will go through a simple way of converting a Xero OAuth 1 application to OAuth 2.0. I will be using the Xero PHP SDK in this example, but the steps will be similar for the other SDKs. You can get find the SDKs here: https://developer.xero.com/documentation/libraries/overview


Some useful resources

  • OAuth 2.0 can be hard to understand at first, but this video explains it very well
  • The Xero OAuth 2.0 code flow
  • Xero SDK guide


Before you start: Create a Xero app


The OAuth 2.0 redirect URI is where Xero will redirect the user to after they allow your app to access their data.



Step 1: Send the user to Xero to authorize your app

Create a provider object from the SDK that will create the link. This will be used to generate a URL that sends the user to Xero.


    $provider = new \League\OAuth2\Client\Provider\GenericProvider([

           'clientId' =>{Your client ID},

           'clientSecret' =>{Your client secret},

           'redirectUri' =>{Your redirect URI},

           'urlAuthorize' => 'https://login.xero.com/identity/connect/authorize',

           'urlAccessToken' => 'https://identity.xero.com/connect/token',

           'urlResourceOwnerDetails' => 'https://api.xero.com/api.xro/2.0/Organisation',



Make sure your redirect URI is the same as the one in your Xero app you created. The clientId and clientSecret are given to you by your Xero app.

Add whatever scopes you need. The “offline_access” scope tells Xero to give you a refresh token that can be used to get a new access token without sending the user back to Xero.


    $options = [

            'scope' => ['openid profile offline_access accounting.transactions   accounting.contacts accounting.settings.read']


    $authorization_url = $provider->getAuthorizationUrl($options);


Send your users to the URL stored in $authorization_url


Step 2: Callback function


After the user authorises your app, xero will send them back to the redirect URI with an authorization code. The authorization code will expire after 5 minutes, so it needs to be exchanged for an access token.


    $authorization_code = $_GET['code'];

    $access_token = $provider->getAccessToken('authorization_code', [

               'code' => $authorization_code



From the access token you can get the expiry date, tenant information, refresh token and the token id. The code below is how you get that information from the access token. The access token will expire after 30 minutes, but the refresh token can be used to get a new access token after it expires.


    $config = XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken( (string) $access_token);

    $identity_api = new XeroAPI\XeroPHP\Api\IdentityApi(

            new GuzzleHttp\Client(),



    $connections = $identity_api>getConnections();


$connections will store an array of tenants. We are only expecting 1. You should store the following information somewhere:








    $access_token->getToken(); // the import one


Done! Now use the access token and tenant id to request or send data to Xero.

Request some data 

Let's get the organization name as an example.


Start off by checking the access token hasn’t expired by using the expiry date retrieved in the previous step. If it has expired, use the refresh token to get a new one. I’ll explain how that’s done later. If it hasn’t expired, use the tenant_id and access token retrieved with getToken() to request data from xero.


    $accounting_api = new XeroAPI\XeroPHP\Api\AccountingApi(

          new GuzzleHttp\Client(),

          $config // same as config from before



    $api_response = $accountingApi->getOrganisations( {Tenant id} );

    $org_name = $api_response->getOrganisations()[0]->getName();


    $api_response will hold an array of organizations and/or any errors. $org_name will hold the Xero organization name.


Upload some data.

Let’s send an invoice to Xero.


When sending an invoice to Xero, the format of the invoice is very important. You need to send Xero an array that contains an array of invoices.


The invoices you are sending to Xero should be formatted as follows:



            “invoices” => array(

                     0  => [ { invoice array } ],

                     1  => [ { invoice array } ]




Each invoice array should look like:


               “Type” => ACCREC,

               “Contact” => array(

                             “ContactID” => { xero contact id if you have it },

                             “Name” => { contact name. Not needed if you have ContactID }                                            


                 “Date” => 2020-05-31 23:59:59,

                 “DueDate” => 2020-06-07,

                 “Status” => “DRAFT”,

                 “LineAmountTypes” => Exclusive,

                 “InvoiceNumber” => ””,

                 “LineItems” => array(

                                [0] => array(

                                            “Description” =>  “description”,

                                            “Quantity” => 7,

                                            “UnitAmount” => 20,

                                            “TaxType” => NONE,

                                            “AccountCode” => 276





Now send the array that holds the array of invoices to Xero.


    $api_response = $accounting_api->updateOrCreateInvoices( { tenant id }, $invoices_xero);


Xero should return the invoices saved along with any errors caused by each invoice.


    foreach ($api_response->getInvoices() as $xerod_invoice) {

             if ( $xerod_invoice['has_errors'] == null ) {

                     // no errors.  You can save the returned invoice id and contact id




Using the refresh token

An access token only lasts 30 minutes, so it is important to use the refresh token to refresh the access token. Refresh tokens expire after 60 days, but each time you use one you are given a new refresh token, so the user will only have to go through the authentication process again if the token isn’t refreshed for 60 days.


To get the new token:


    $provider->getAccessToken('refresh_token', [

            'refresh_token' => $refresh_token



Then save the new token, expiry date and refresh token.


    access_token = (string)$access_token->getToken();

    expires = $access_token->getExpires();

    refresh_token = $access_token->getRefreshToken();


Removing Connections to Xero

To remove connections to Xero, use the identity API (from step 2).


    $connections = $identity_api>getConnections();


Loop through the connections. In this example I will remove all the connections, however, you can use the tenant information (name/id) from the connection to decide which to delete.


    foreach ($connections as $connection){

             $id = $connection->getId();

             $result = $identity_api>deleteConnection($id);



The connections are now deleted!

More Articles