intuit-oauth
Version:
Intuit Node.js client for OAuth2.0 and OpenIDConnect
583 lines (466 loc) • 19.2 kB
Markdown
[][ss1]
[](https://travis-ci.org/intuit/oauth-jsclient?branch=master)
[](https://www.npmjs.com/package/intuit-oauth)
[](https://coveralls.io/github/intuit/oauth-jsclient?branch=master)
[](https://github.com/intuit/oauth-jsclient/graphs/contributors)
[](https://scrutinizer-ci.com/g/intuit/oauth-jsclient/?branch=master)

[](https://github.com/prettier/prettier)
[](https://snyk.io/test/github/intuit/oauth-jsclient)
# Intuit OAuth2.0 NodeJS Library
The OAuth2 Nodejs Client library is meant to work with Intuit's
[OAuth2.0](https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0)
and
[OpenID Connect](https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/openid-connect)
implementations which conforms to the specifications.
## Table of Contents
- [Intuit OAuth2.0 NodeJS Library](#intuit-oauth20-nodejs-library)
- [Table of Contents](#table-of-contents)
- [Requirements](#requirements)
- [Installation](#installation)
- [Using NodeJS](#using-nodejs)
- [Options :](#options)
- [Usage](#usage)
- [Authorization Code Flow](#authorization-code-flow)
- [Step 1](#step-1)
- [Scopes :](#scopes)
- [Step 2](#step-2)
- [Sample](#sample)
- [Helpers](#helpers)
- [Is AccessToken Valid](#is-accesstoken-valid)
- [Refresh access_token](#refresh-access_token)
- [Refresh access_token by passing the refresh_token explicitly](#refresh-access_token-by-passing-the-refresh_token-explicitly)
- [Revoke access_token](#retrieve-the-token-)
- [Getter / Setter for Token](#getter--setter-for-token)
- [Retrieve the Token :](#retrieve-the-token)
- [Set the Token :](#set-the-token-)
- [Migrate OAuth1.0 Tokens to OAuth2.0](#migrate-oauth10-tokens-to-oauth20)
- [Validate ID Token](#validate-id-token)
- [Make API call](#make-api-call)
- [Auth-Response](#auth-response)
- [Error Logging](#error-logging)
- [FAQ](#faq)
- [Contributing](#contributing)
- [Steps](#steps)
- [Changelog](#changelog)
- [License](#license)
# Requirements
The Node.js client library is tested against the `Node 10` and newer versions.
| Version | Node support |
|----------------------------------------------------------------------------------|-----------------------------------|
| [intuit-oauth@1.x.x](https://github.com/intuit/oauth-jsclient/tree/1.5.0) | Node 6.x or higher |
| [intuit-oauth@2.x.x](https://github.com/intuit/oauth-jsclient/tree/2.0.0) | Node 7.x or higher |
| [intuit-oauth@3.x.x](https://github.com/intuit/oauth-jsclient/tree/3.0.2) | Node 8.x or Node 9.x and higher |
**Note**: Older node versions are not supported.
# Installation
Follow the instructions below to use the library :
## Using NodeJS
1. Install the NPM package:
```sh
npm install intuit-oauth --save
```
2. Require the Library:
```js
const OAuthClient = require('intuit-oauth');
const oauthClient = new OAuthClient({
clientId: '<Enter your clientId>',
clientSecret: '<Enter your clientSecret>',
environment: 'sandbox' || 'production',
redirectUri: '<Enter your callback URL>',
});
```
### Options
- `clientId` - clientID for your app. Required
- `clientSecret` - clientSecret fpor your app. Required
- `environment` - environment for the client. Required
- `sandbox` - for authorizing in sandbox.
- `production` - for authorizing in production.
- `redirectUri` - redirectUri on your app to get the `authorizationCode` from Intuit Servers. Make sure this redirect URI is also added on your app in the [developer portal](https://developer.intuit.com) on the Keys & OAuth tab. Required
- `logging` - by default, logging is disabled i.e `false`. To enable provide`true`.
# Usage
We assume that you have a basic understanding about OAuth2.0. If not please read
[API Documentation](https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0)
for clear understanding
## Authorization Code Flow
The Authorization Code flow is made up of two parts :
**Step 1.** Redirect user to `oauthClient.authorizeUri(options)`.
**Step 2.** Parse response uri and get access-token using the function
`oauthClient.createToken(req.url)` which returns a
[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
### Step 1
```javascript
// Instance of client
const oauthClient = new OAuthClient({
clientId: '<Enter your clientId>',
clientSecret: '<Enter your clientSecret>',
environment: 'sandbox',
redirectUri: '<http://localhost:8000/callback>',
});
// AuthorizationUri
const authUri = oauthClient.authorizeUri({
scope: [OAuthClient.scopes.Accounting, OAuthClient.scopes.OpenId],
state: 'testState',
}); // can be an array of multiple scopes ex : {scope:[OAuthClient.scopes.Accounting,OAuthClient.scopes.OpenId]}
// Redirect the authUri
res.redirect(authUri);
```
### Scopes
The available scopes include :
- `com.intuit.quickbooks.accounting` - for accounting scope include `OAuthClient.scopes.Accounting`
- `com.intuit.quickbooks.payment` - for payment scope include `OAuthClient.scopes.Payment`
- `com.intuit.quickbooks.payroll` - for QuickBooks Payroll API (whitelisted beta apps only)
- `com.intuit.quickbooks.payroll.timetracking` - for QuickBooks Payroll API for for access to
compensation (whitelisted beta apps only)
- `com.intuit.quickbooks.payroll.benefits` - for QuickBooks Payroll API for access to
benefits/pension/deduction (whitelisted beta apps only)
OpenID Scopes :
- `openid` - for openID assertion include `OAuthClient.scopes.OpenId`
- `profile` - for profile assertion include `OAuthClient.scopes.Profile`
- `email` - for email assertion include `OAuthClient.scopes.Email`
- `phone` - for phone assertion include `OAuthClient.scopes.Phone`
- `address` - for address assertion include `OAuthClient.scopes.Address`
### Step 2
```javascript
// Parse the redirect URL for authCode and exchange them for tokens
const parseRedirect = req.url;
// Exchange the auth code retrieved from the **req.url** on the redirectUri
oauthClient
.createToken(parseRedirect)
.then(function (authResponse) {
console.log('The Token is ' + JSON.stringify(authResponse.getToken()));
})
.catch(function (e) {
console.error('The error message is :' + e.originalMessage);
console.error(e.intuit_tid);
});
```
# Sample
For more clarity, we suggest you take a look at the sample application below :
[sample](https://github.com/intuit/oauth-jsclient/tree/master/sample)
## Helpers
### Is AccessToken Valid
You can check if the `access_token` associated with the `oauthClient` is valid ( not expired ) or
not using the helper method.
```javascript
if (oauthClient.isAccessTokenValid()) {
console.log('The access_token is valid');
}
if (!oauthClient.isAccessTokenValid()) {
oauthClient
.refresh()
.then(function (authResponse) {
console.log('Tokens refreshed : ' + JSON.stringify(authResponse.getToken()));
})
.catch(function (e) {
console.error('The error message is :' + e.originalMessage);
console.error(e.intuit_tid);
});
}
```
\*\* Note: If the access_token is not valid, you can call the client's `refresh()` method to refresh
the tokens for you as shown below
### Refresh access_token
Access tokens are valid for 3600 seconds (one hour), after which time you need to get a fresh one
using the latest refresh_token returned to you from the previous request. When you request a fresh
access_token, always use the refresh token returned in the most recent token_endpoint response. Your
previous refresh tokens expire 24 hours after you receive a new one.
```javascript
oauthClient
.refresh()
.then(function (authResponse) {
console.log('Tokens refreshed : ' + JSON.stringify(authResponse.getToken()));
})
.catch(function (e) {
console.error('The error message is :' + e.originalMessage);
console.error(e.intuit_tid);
});
```
### Refresh access_token by passing the refresh_token explicitly
You can call the below helper method to refresh tokens by explictly passing the refresh_token.
\*\*Note : `refresh_token` should be of the type `string`
```javascript
oauthClient
.refreshUsingToken('<Enter the refresh token>')
.then(function (authResponse) {
console.log('Tokens refreshed : ' + JSON.stringify(authResponse.getToken()));
})
.catch(function (e) {
console.error('The error message is :' + e.originalMessage);
console.error(e.intuit_tid);
});
```
### Revoke access_token
When you no longer need the access_token, you could use the below helper method to revoke the
tokens.
```javascript
oauthClient
.revoke()
.then(function (authResponse) {
console.log('Tokens revoked : ' + JSON.stringify(authResponse.json));
})
.catch(function (e) {
console.error('The error message is :' + e.originalMessage);
console.error(e.intuit_tid);
});
```
Alternatively you can also pass `access_token` or `refresh_token` to this helper method using the
`params` object: refer to - [Getter / Setter for Token](#getter--setter-for-token) section to know
how to retrieve the `token` object
```javascript
oauthClient
.revoke(params)
.then(function (authResponse) {
console.log('Tokens revoked : ' + JSON.stringify(authResponse.json));
})
.catch(function (e) {
console.error('The error message is :' + e.originalMessage);
console.error(e.intuit_tid);
});
```
** Note ** : `params` is the Token JSON object as shown below : ( _If you do not pass the `params`
then the token object of the client would be considered._)
```
{
"token_type": "bearer",
"expires_in": 3600,
"refresh_token":"<refresh_token>",
"x_refresh_token_expires_in":15552000,
"access_token":"<access_token>",
"createdAt": "(Optional Default = Date.now()) <Milliseconds> from the unix epoch"
}
```
** Note ** :
### Getter / Setter for Token
You can call the below methods to set and get the tokens using the `oauthClient` instance:
#### Retrieve the Token :
```javascript
// To get the tokens
let authToken = oauthClient.getToken().getToken();
`OR`;
let authToken = oauthClient.token.getToken();
```
#### Set the Token :
```javascript
// To Set the retrieved tokens explicitly using Token Object but the same instance
oauthClient.setToken(authToken);
OR;
// To set the retrieved tokens using a new client instance
const oauthClient = new OAuthClient({
clientId: '<Enter your clientId>',
clientSecret: '<Enter your clientSecret>',
environment: 'sandbox',
redirectUri: '<http://localhost:8000/callback>',
token: authToken,
});
```
The authToken parameters are as follows:
```
{
token_type: '<String>',
access_token: '<String>',
expires_in: '<Int> Seconds',
refresh_token: '<String>',
x_refresh_token_expires_in: '<Int> Seconds',
id_token: "(Optional Default = '') <String>",
createdAt: '(Optional Default = Date.now()) <Milliseconds> from the unix epoch'
}
```
**Note** :
The OAuth Client library converts the accessToken and refreshToken expiry time to `TimeStamp`. If
you are setting a stored token, please pass in the `createdAt` for accurate experiations.
```javascript
oauthClient.setToken(authToken);
```
### Migrate OAuth1.0 Tokens to OAuth2.0
You can call the below method to migrate the bearer / refresh tokens from OAuth1.0 to OAuth2.0. You
```javascript
// Fill in the params object ( argument to the migrate function )
let params = {
oauth_consumer_key: '<Enter oauth1ConsumerKey>',
oauth_consumer_secret: '<Enter oauth1ConsumerSecret>',
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: Math.round(new Date().getTime() / 1000),
oauth_nonce: 'nonce',
oauth_version: '1.0',
access_token: '<Enter OAuth1.0 access_token>',
access_secret: '<Enter OAuth1.0 access_secret>',
scope: [OAuthClient.scopes.Accounting],
};
oauthClient
.migrate(params)
.then(function (response) {
console.log('The response is ' + JSON.stringify(response));
})
.catch(function (e) {
console.log('The error is ' + e.message);
});
```
### Validate ID Token
You can validate the ID token obtained from `Intuit Authorization Server` as shown below :
```javascript
oauthClient
.validateIdToken()
.then(function (response) {
console.log('Is my ID token validated : ' + response);
})
.catch(function (e) {
console.log('The error is ' + JSON.stringify(e));
});
// Is my ID token validated : true
```
The client validates the ID Token and returns boolean `true` if validates successfully else it would
throw an exception.
### Make API Call
You can make API call using the token generated from the client as shown below :
```javascript
// Body sample from API explorer examples
const body = {
TrackQtyOnHand: true,
Name: 'Garden Supplies',
QtyOnHand: 10,
InvStartDate: '2015-01-01',
Type: 'Inventory',
IncomeAccountRef: {
name: 'Sales of Product Income',
value: '79',
},
AssetAccountRef: {
name: 'Inventory Asset',
value: '81',
},
ExpenseAccountRef: {
name: 'Cost of Goods Sold',
value: '80',
},
};
oauthClient
.makeApiCall({
url: 'https://sandbox-quickbooks.api.intuit.com/v3/company/1234/item',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
.then(function (response) {
console.log('The API response is : ' + response);
})
.catch(function (e) {
console.log('The error is ' + JSON.stringify(e));
});
```
The client validates the ID Token and returns boolean `true` if validates successfully else it would
throw an exception.
#### Support for PDF format
In order to save the PDF generated from the APIs properly, the correct transport type should be passed into the `makeAPI()`.Below is an example of the same:
```
.makeApiCall({ url: `${url}v3/company/${companyID}/invoice/${invoiceNumber}/pdf?minorversion=59` , headers:{'Content-Type': 'application/pdf','Accept':'application/pdf'}, transport: popsicle.createTransport({type: 'buffer'})})
```
The response is an actual buffer( binary BLOB) which could then be saved to the file.
### Auth-Response
The response provided by the client is a wrapped response of the below items which is what we call
authResponse, lets see how it looks like:
```text
1. response // response from `HTTP Client` used by library
2. token // instance of `Token` Object
3. body // res.body in `text`
4. json // res.body in `JSON`
5. intuit_tid // `intuit-tid` from response headers
```
A sample `AuthResponse` object would look similar to :
```json
{
"token": {
"realmId": "<realmId>",
"token_type": "bearer",
"access_token": "<access_token>",
"refresh_token": "<refresh_token>",
"expires_in": 3600,
"x_refresh_token_expires_in": 8726400,
"id_token": "<id_token>",
"latency": 60000
},
"response": {
"url": "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer",
"headers": {
"content-type": "application/json;charset=UTF-8",
"content-length": "61",
"connection": "close",
"server": "nginx",
"strict-transport-security": "max-age=15552000",
"intuit_tid": "1234-1234-1234-123",
"cache-control": "no-cache, no-store",
"pragma": "no-cache"
},
"body": "{\"id_token\":\"<id_token>\",\"expires_in\":3600,\"token_type\":\"bearer\",\"x_refresh_token_expires_in\":8726400,\"refresh_token\":\"<refresh_token>\",\"access_token\":\"<access_token>\"}",
"status": 200,
"statusText": "OK"
},
"body": "{\"id_token\":\"<id_token>\",\"expires_in\":3600,\"token_type\":\"bearer\",\"x_refresh_token_expires_in\":8726400,\"refresh_token\":\"<refresh_token>\",\"access_token\":\"<access_token>\"}",
"json": {
"access_token": "<access_token>",
"refresh_token": "<refresh_token>",
"token_type": "bearer",
"expires_in": "3600",
"x_refresh_token_expires_in": "8726400",
"id_token": "<id_token>"
},
"intuit_tid": "4245c696-3710-1548-d1e0-d85918e22ebe"
}
```
You can use the below helper methods to make full use of the Auth Response Object :
```javascript
oauthClient.createToken(parseRedirect).then(function (authResponse) {
console.log('The Token in JSON is ' + JSON.stringify(authResponse.json));
let status = authResponse.status();
let body = authResponse.text();
let jsonResponse = authResponse.json;
let intuit_tid = authResponse.get_intuit_tid();
});
```
### Error Logging
By default the logging is `disabled` i.e set to `false`. However, to enable logging, pass
`logging=true` when you create the `oauthClient` instance :
```javascript
const oauthClient = new OAuthClient({
clientId: '<Enter your clientId>',
clientSecret: '<Enter your clientSecret>',
environment: 'sandbox',
redirectUri: '<http://localhost:8000/callback>',
logging: true,
});
```
The logs would be captured under the directory `/logs/oAuthClient-log.log`
Whenever there is an error, the library throws an exception and you can use the below helper methods
to retrieve more information :
```javascript
oauthClient.createToken(parseRedirect).catch(function (error) {
console.log(error);
});
/**
* This is how the Error Object Looks :
{
"originalMessage":"Response has an Error",
"error":"invalid_grant",
"error_description":"Token invalid",
"intuit_tid":"4245c696-3710-1548-d1e0-d85918e22ebe"
}
*/
```
## FAQ
You can refer to our [FAQ](https://github.com/intuit/oauth-jsclient/wiki/FAQ) if you have any
questions.
## Contributing
- You are welcome to send a PR to `develop` branch.
- The `master` branch will always point to the latest published version.
- The `develop` branch will contain the latest development/testing changes.
### Steps
- Fork and clone the repository (`develop` branch).
- Run `npm install` for dependencies.
- Run `npm test` to execute all specs.
## Changelog
See the changelog [here](https://github.com/intuit/oauth-jsclient/blob/master/CHANGELOG.md)
## License
Intuit `oauth-jsclient` is licensed under the
[Apache License, Version 2.0](https://github.com/intuit/oauth-jsclient/blob/master/LICENSE)
[ss1]: https://help.developer.intuit.com/s/SDKFeedback?cid=1120