OAuth NodeJS Client

Tutorial objective

The Intuit Node.js OAuth 2.0 Client makes it easy to authenticate and authorize using OAuth 2.0 and OpenID Connect when you integrate your Node.js 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 Node.js 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 Node.js OAuth 2.0 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 app to showcase the usage of Intuit Node.js OAuth 2.0 Client to authenticate using OAuth 2.0. To use the sample app, you need the following prerequisites:

Prerequisites

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 using 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:

TLS/SSL (optional)

If you want your endpoint to be exposed over the internet, the easiest way while still developing your code locally is to use ngrok. You do not have to install ngrok. The sample app installs it 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 a URL as shown below:

💳 See the sample app in your browser: http://localhost:8000

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

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

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

Your will see a URL as shown below:

💻 See the sample app in your browser: https://9b4ee833.ngrok.io

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

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

Contributions

We welcome any reports of problems, comments, or suggestions. Please report these on the Issue Tracker in Github.

Helper methods

The SDK offers the following 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 client provides an authResponse, which is a wrapped response that includes the following:

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 following 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"
}
*/
Logging
Error logging

By default the logging is disabled i.e set to false. However, to enable logging, pass the parameter logging : true when you create the oauthClient instance :

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