Security News
JSR Working Group Kicks Off with Ambitious Roadmap and Plans for Open Governance
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
xero-node
Advanced tools
Version 4.x of Xero NodeJS SDK only supports oAuth2 authentication and the following API sets.
We've moved this code into the oauth1 branch.
Follow these steps to create your Xero app
This SDK's functionality is majority generated from our OpenAPISpec.
The exception is the src/xeroClient.ts
which contains the typescript that is unique to this repository. Contributions are welcome but please keep in mind that majority of SDK is auto-generated from the OpenAPISpec. We try to get changes in that projects to be released on a reasonable cadence.
Read more about our process in maintaining our suite of SDK's
We are working to build out a more robust test suite, and currently just have tests setup for our xeroClient.ts - PR's will now run against a CI build - and as we add more tests to this project community collaboration will be easier to incorporate.
npm test
We use OAuth2.0 to generate access tokens that authenticate requests against our API. Each API call will need to have a valid token populated on the API client to succeed. In a tokenSet will be an access_token which lasts for 30 minutes, and a refresh_token which lasts for 60 days. If you don't want to require your users to re-authenticate each time you want to call the API on their behalf, you will need a datastore for these tokens and will be required to refresh the tokens at least once per 60 days to avoid expiration. The offline_access
scope is required for refresh tokens to work.
In Xero a user can belong to multiple organisations. Tokens are ultimately associated with a Xero user, who can belong to multiple tenants/organisations. If your user 'Allows Access' to multiple organisations, be hyper aware of which tenantId
you are passing to each function.
Step 1: Initialize the XeroClient
, and redirect user to xero auth flow
Step 2: Call apiCallback
to get your tokenSet
Step 3: Call updateTenats
to populate additional tenant data
You will need to have the accounting.settings
scope in order to use this helper
NOTE: If you have already authorized the user and have stored a valid tokenSet, you can create a new XeroClient()
and refresh your token without triggering the openid-client dependency:
const tokenSet = getTokenSetFromUserId(user.id) // example function
const newXeroClient = new XeroClient()
const newTokenSet = await newXeroClient.refreshWithRefreshToken(xero_client_id, xero_client_secret, tokenSet.refresh_token)
// refreshWithRefreshToken calls setAccessToken() so the refreshed token will be stored on newXeroClient
await newXeroClient.accountingApi.getInvoices('my-tenant-uuid))
initialize()
the client to set up the 'openid-client'consentUrl
const port = process.env.PORT || 3000
const xero = new XeroClient({
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
redirectUris: [`http://localhost:${port}/callback`],
scopes: 'openid profile email accounting.transactions offline_access'.split(" ")
});
// `buildConsentUrl()` calls `await xero.initialize()`
let consentUrl = await xero.buildConsentUrl();
res.redirect(consentUrl);
Call apiCallback
function with the response url which returns a tokenSet you can save in your datastore for future calls.
The tokenSet
can also be accessed from the client as xero.readTokenSet()
.
const { TokenSet } = require('openid-client');
const tokenSet: TokenSet = await xero.apiCallback(req.url);
The tokenSet
is what you should store in your database. That object is what you will need to pass to the client. It contains your access_token and refresh_token as well as other information regarding your connection.
{
id_token: 'eyJhxxxx.yyy',
access_token: 'eyJxxx.yyy.zzz',
expires_at: 1231231234,
token_type: 'Bearer',
refresh_token: 'xxxyyyyzzz',
scope: 'openid profile email accounting.settings accounting.reports.read accounting.journals.read accounting.contacts accounting.attachments accounting.transactions offline_access',
session_state: 'xxx.yyy'
}
Populate the XeroClient's active tenant data
For most integrations you will always want to display the org name and additional metadata about the connected org. The /connections
endpoint does not currently serialize that data so requires developers to make additional api calls for each org that your user connects to surface that information.
The updatedTenants
function will query & nest the additional orgData results in your xeroClient under each connection/tenant object and return the array of tenants. This requires accounting.settings
scope because updateTenants
calls the organisation endpoint.
const tenants = await xero.updateTenants()
console.log(tenants || xero.tenants)
[
{
id: 'xxx-yyy-zzz-xxx-yyy',
tenantId: 'xxx-yyy-zzz-xxx-yyy',
tenantType: 'ORGANISATION',
createdDateUtc: 'UTC-DateString',
updatedDateUtc: 'UTC-DateString',
orgData: {
organisationID: 'xxx-yyy-zzz-xxx-yyy',
name: 'My first org',
version: 'US',
shortCode: '!2h37s',
...
}
},
{
id: 'xxx-yyy-zzz-xxx-yyy',
tenantId: 'xxx-yyy-zzz-xxx-yyy',
tenantType: 'ORGANISATION',
createdDateUtc: 'UTC-DateString',
updatedDateUtc: 'UTC-DateString',
orgData: {
organisationID: 'xxx-yyy-zzz-xxx-yyy',
name: 'My second org',
version: 'AUS',
shortCode: '!yrcgp',
...
}
}
]
// You can also remove a connection by passing `disconnect()` the `.id` which is that tenant's connection id.
await xero.disconnect(xero.tenants[0].id)
Once you have a valid token/tokenSet saved you can set the tokenSet on the client without going through the callback by calling setTokenSet
.
For example - once a user authenticates you can refresh the token (which will also set the new token on the client) to make authorized api calls.
There are two ways to refresh a token.
// refreshToken()
const validTokenSet = await xero.refreshToken()
If you already generated a valid access token, you can initialize an empty client and refresh any saved access_tokens by passing the client, secret, and refresh_token to refreshWithRefreshToken()
const newXeroClient = new XeroClient()
const refreshedTokenSet = await newXeroClient.refreshWithRefreshToken(client_id, client_secret, tokenSet.refresh_token)
Making AUthorized API calls:
const tokenSet = getTokenSetFromDatabase(userId) // example function name
await xero.setTokenSet(tokenSet)
// you can call this to fetch/set your connected tenant data on your client, or you could also store this information in a database so you don't need to updateTenants every time you connect to API
await xero.updateTenants()
await xero.accountingApi.getInvoices(xero.tenants[0].tenantId)
// example flow of initializing and using the client after someone has already authenticated and you have saved their tokenSet
const xero = new XeroClient({
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
redirectUris: [`http://localhost:${port}/callback`],
scopes: 'openid profile email accounting.transactions offline_access'.split(" ")
});
await xero.initialize();
const tokenSet = getYourTokenSetFromSavedLocation(currentUser)
await xero.setTokenSet(tokenSet)
...
const activeTenantId = xero.tenants[0].tenantId
const getOrgs = await xero.accountingApi.getOrganisations(activeTenantId)
const orgCountry= getOrgs.body.organisations[0].countryCode
const contactsResponse = await xero.accountingApi.getContacts(activeTenantId)
const contactId = getContactsResponse.body.contacts[0].contactID
---
import { XeroClient, Invoice } from "xero-node";
const invoices = {
invoices: [
{
type: Invoice.TypeEnum.ACCREC,
contact: {
contactID: contactId
},
lineItems: [
{
description: "Acme Tires",
quantity: 2.0,
unitAmount: 20.0,
accountCode: "500",
taxType: "NONE",
lineAmount: 40.0
}
],
date: "2019-03-11",
dueDate: "2018-12-10",
reference: "Website Design",
status: Invoice.StatusEnum.AUTHORISED
}
]
};
const createdInvoice = await xero.accountingApi.createInvoices(activeTenantId, invoices)
For more robust examples in how to utilize our accounting api we have (roughly) every single endpoint mapped out with an example in our sample app - complete with showing the Xero data dependencies required for interaction with many objects ( ie. types, assoc. accounts, tax types, date formats).
Just visit the repo https://github.com/XeroAPI/xero-node-oauth2-app configure your credentials & get started.
xero.tenants
// This needs to be called to setup relevant openid-client on the XeroClient
await xero.initialize()
// buildConsentUrl calls `await xero.initialize()` so if you wont't need to call initialize() if your using the client to send user through the auth flow.
await xero.buildConsentUrl()
// tokenSet and its expiration
const tokenSet = await xero.readTokenSet();
const now = new Date().getTime()
if (tokenSet.expires_in > now) {
const validTokenSet = await xero.refreshToken()
// or you can refresh the token without needing to initialize the openid-client
// helpful for background processes where you want to limit any dependencies
await xero.refreshWithRefreshToken(client_id, client_secret, tokenSet.refresh_token)
}
tokenSet.expires_in // returns seconds
tokenSet.expires_at // returns milliseconds
new Date(tokenSet.expires_at * 1000).toLocaleString()) // readable expiration
// some endpoints date fields require
// the MS date format for POST'ing data
const dateString = "1990-02-05"
const birthday = await xero.formatMsDate(dateString)
await xero.disconnect(xero.tenants[0].id)
await xero.readIdTokenClaims()
await xero.readTokenSet()
const tokenSet = await xero.readTokenSet()
await xero.setTokenSet(tokenSet)
FAQs
Xero NodeJS OAuth 2.0 client for xero-node
We found that xero-node demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
Security News
Research
An advanced npm supply chain attack is leveraging Ethereum smart contracts for decentralized, persistent malware control, evading traditional defenses.
Security News
Research
Attackers are impersonating Sindre Sorhus on npm with a fake 'chalk-node' package containing a malicious backdoor to compromise developers' projects.