OAuth NodeJS Client

Tutorial Objective

The Intuit NodeJS OAuth2.0 Client makes it easy to Authenticate / Authorize using OAuth2.0 / OpenID Connect when you integrate your Nodejs web app with the QuickBooks Online API.

This guide assumes that you have an existing web app that you want to integrate with QuickBooks Online.

Requirements

Install the SDK using NPM

The recommended way to install the Intuit Nodejs OAuth Client is with NPM. Node Package Manager is the package manager for JavaScript .

1
$ npm install intuit-oauth --save

Require the client

To add the Intuit OAuth2.0 Node JS Client as a dependency into your project, require the client as shown below :

1
2
3
4
5
6
7
8
9
var OAuthClient = require('intuit-oauth');

var oauthClient = new OAuthClient({
  clientId: 'Enter your clientId',            // enter the apps `clientId`
  clientSecret: 'Enter your clientSecret',    // enter the apps `clientSecret`
  environment: 'sandbox' || 'production',     // enter either `sandbox` or `production`
  redirectUri: 'Enter your callback URL'      // enter the redirectUri
  logging: true                               // by default the value is `false`
});

Options

The OAuthClient() method accepts the following parameters :

Sample App

We have a sample application to showcase the usage of Nodejs OAuth client to authenticate using OAuth2.0

However, you would need to have the below prerequisites:

Pre-Requisites

Note : Client Credentials can be found on the Keys section of the app when you create an app on Developer Portal. To know more on where to kind the Keys refer to Getting Started

Install Via Github Repo (Recommended):

1
2
3
$ git clone https://github.com/intuit/oauth-jsclient.git
$ cd sample
$ npm install

Configuration

1
$ cp .env.example .env

Edit the .env file to add your:

  • PORT:(optional) Optional port number for the app to be served
  • NGROK_ENABLED:(optional) By default it is set to false. If you want to serve the Sample App over HTTPS ( which is mandatory if you want to test this app using Production Credentials), set the variable to true

TLS / SSL (optional):

If you want your endpoint to be exposed over the internet. The easiest way to do that while you are still developing your code locally is to use ngrok.

You dont have to worry about installing ngrok. The sample application does that for you.

  1. Just set NGROK_ENABLED = true in .env

Usage:

1
$ npm start

Without ngrok (if you are using localhost i.e NGROK_ENABLED=false in .env)

You will see an URL as below:

💳 See the Sample App in your browser : http://localhost:8000

💳 Copy this into Redirect URI on the browser : http://localhost:8000/callback

💻 Make Sure this redirect URI is also copied on your app in : https://developer.intuit.com

With ngrok (if you are using localhost i.e NGROK_ENABLED=true in .env)

Your will see an URL as below :

💻 See the Sample App in your browser: https://9b4ee833.ngrok.io

💳 Copy and paste this Redirect URI on the browser : https://9b4ee833.ngrok.io/callback

💻 Make Sure this redirect URI is also copied on your app in : https://developer.intuit.com

Contributions

Any reports of problems, comments or suggestions are most welcome. Please report these on the Issue Tracker in Github

Helper Methods

The SDK offers the below helper methods :

1. Is Access-Token Valid

You can check if the access token associated with the oauthClient is valid or not using the helper method :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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.json()));
       })
       .catch(function(e) {
           console.error("The error message is :"+e.originalMessage);
           console.error(e.intuit_tid);
       });

}

.. container:: new-list-content

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

2. 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 :

1
2
3
4
5
6
7
8
oauthClient.refresh()
  .then(function(authResponse) {
      console.log('Tokens refreshed : ' + JSON.stringify(authResponse.json()));
})
.catch(function(e) {
      console.error("The error message is :"+e.originalMessage);
      console.error(e.intuit_tid);
});

3. Revoke Access-Token

When you no longer need the access_token, you could use the below helper method to revoke the tokens. You can also optionally pass the access_token or refresh_token to this helper method :

1
2
3
4
5
6
7
8
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);
   });

4. Getter / Setter for Token

You can call the below methods to set and get the tokens using the oauthClient instance :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
oauthClient.getToken().setToken({
      "token_type": "bearer",
      "expires_in": 3600,
      "refresh_token":"<refresh_token>",
      "x_refresh_token_expires_in":15552000,
      "access_token":"<access_token>"
});

// To get the tokens
oauthClient.getToken().getToken();

`OR`

oauthClient.token.getToken();

Response formats

The response provided by the client is a wrapped response of the below items which is what we call authResponse, below is how it looks like:

1
2
3
4
5
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 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
  "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 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
oauthClient.createToken(parseRedirect)
  .then(function(authResponse) {
      console.log('The Token in JSON is  '+ JSON.stringify(authResponse.getJson()));
      var status = authResponse.status();
      var body = authResponse.text();
      var jsonResponse = authResponse.getJson();
      var intuit_tid = authResponse.get_intuit_tid();
});

// remove this to next rst
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"
}
*/
  • getJson() : Get the Response in JSON
  • status() : Get the status of the response ( 200, 500 etc )
  • text() : Get the Response in text format
  • get_intuit_tid() : Get the intuit-tid from response headers

Logging

1
2
3
4
5
6
7
var 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 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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"
}
*/

Exception Handling

Whenever there is an error, the library throws an exception and you can use the below helper methods to retrieve more information :

Check the Error Message and Description

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
oauthClient.createToken(parseRedirect)
  .catch(function(error) {
      console.error("The error message is :"+e.originalMessage);
      console.error("The error description is :"+e.error_description);
  });

/**
  * This is how the Error Object Looks :
  {
     "originalMessage":"Response has an Error",
     "error":"invalid_grant",
     "error_description":"Token invalid",
     "intuit_tid":"1234-1234-1234-123"
  }
*/

Report an Error to Intuit

Sometimes the error returned by QuickBooks Online may not be clear, and you would like Intuit Support to help identify the cause. If so, use the following code to record the Intuit-tid from the response, and send us this value along with the Request and Response log you recorded, so we can help you diagnose the issue:

1
2
3
4
oauthClient.createToken(parseRedirect)
  .catch(function(error) {
      console.error("The intuit-tid is :" + e.intuit_tid);
  });

Known Issues

Known Issues