Implement OAuth 2.0

Note

As of July 17, 2017, new or existing developers with no previous apps must implement OAuth 2.0. Click here for OAuth 1.0a documentation, available as a reference for existing applications. 

This document describes how to use the Intuit OAuth 2.0 endpoints to authorize your app's access to your user's QuickBooks company data. In this model, the user clicks the Connect to QuickBooks button from within your app to initiate the workflow.

​Sections included in this topic:

 

Prerequisites
  • Get client keys: Obtain OAuth 2.0 client keys from your app's dashboard on developer.intuit.com.  To locate the app's dashboard, sign in to developer.intuit.com and click My Apps. Find and open the app you want. From here, click the Keys tab. There are two versions of this key:
    • Development keys—use only in the sandbox environment.
    • Production keys—use only in the production environment. 
  • Define redirect URIs: On the app setting page, create one or more redirect URIs. These URIs handle responses from the OAuth 2.0 server and are called after the user authorizes the connection. URIs in this list are the only ones to which the authorization response can be sent from the OAuth 2.0 server.   You must define at least one URI specifically for your application's auth endpoint before you can use OAuth 2.0. For the sandbox environment, this list can include http://localhost. As a best practice, design your app's auth endpoints in a way that doesn't expose authorization codes to other resources on the page.
Learn more

Overview

When your application needs to access a user's data with a QuickBooks API, your application redirects the user to Intuit's OAuth 2.0 server. The OAuth 2.0 server authenticates the user and obtains consent from the user for your application to access the user's QuickBooks company. Next, Intuit's OAuth 2.0 server redirects the user back to your application along with a single-use authorization code. Your application exchanges this authorization code for an access token. Finally, your application can use the access token to call QuickBooks APIs.

Your app must keep track of when a stored access token can be used and when the application must re-acquire consent.

Note
  • Only the Master Administrator or a Company Administrator can authorize access to a QuickBooks company.
  • Only one user at a time can be connected to a given company. Learn how an app connection can transfer to another user. 

Initiating the authorization request

Making the request

Initiate the OAuth 2 process by redirecting the user to Intuit's OAuth 2.0 server. Retrieve the base URI from the discovery document using the key, authorization_endpoint. The discussion here assumes the base URI is: 

 GET https://appcenter.intuit.com/connect/oauth2

This endpoint is accessible over https; plain http connections are refused. Ensure query parameter values are always URL encoded. The set of query  parameters supported by the Intuit OAuth server include:

ParameterValuesDescription
client_idThe client ID you obtain from the developer dashboard.

Required. Identifies which app is making the request. Obtain this value from the Keys tab on the app profile via My Apps on the developer site. There are two versions of this key:

  • Development key—use only in the sandbox environment.
  • Production key—use only in the production environment. 
scopeSpace-delimited set of permissions that the application requests.

Required. Identifies the QuickBooks API access that your application is requesting. The values passed in this parameter inform the consent screen that is shown to the user.  Available scopes include:

  • com.intuit.quickbooks.accounting—QuickBooks Accounting API

  • com.intuit.quickbooks.payment—QuickBooks Payments API

Click here for OpenID Connect scopes.

It is generally a best practice to request scopes incrementally, at the time access is required, rather than up front; see Incremental authorization.

redirect_uriOne of the redirect URI values listed for this project in the developer dashboard.

Required. Determines where the response is sent. The value of this parameter must exactly match one of the values listed for this app in the app settings. This includes the https scheme, the same case, and the trailing '/'. For the sandbox environment, this list can include http://localhost (do not use HTTPS with localhost). IP addresses are allowed for redirect URIs.

response_typecodeRequired. Determines whether the Intuit OAuth 2.0 endpoint returns an authorization code. Always set this to code.
stateAny string

Required. Provides any state that might be useful to your application upon receipt of the response. The Intuit Authorization Server roundtrips this parameter, so your application receives the same value it sent. To mitigate against cross-site request forgery (CSRF), it is strongly recommended to include an anti-forgery token in the state, and confirm it in the response. See About anti-forgery state tokens.

Here is an example of a complete authorization request URI specifying the com.intuit.quickbooks.accounting scope, with line breaks and spaces for readability:

GET https://appcenter.intuit.com/connect/oauth2?
 client_id=Q3ylJatCvnkYqVKLmkH1zWlNzNWB5CkYB36b5mws7HkKUEv9aI&
 response_type=code&
 scope=com.intuit.quickbooks.accounting&
 redirect_uri=https://www.mydemoapp.com/oauth-redirect&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps://www.mydemoapp.com/oauth-redirect&

Handling the response

The response is sent to the redirect_uri that you specified in the request. All responses are returned in the query string, as shown below:

https://www.mydemoapp.com/oauth-redirect?state=security_token%3D138r5719ru3e1%26url
%3Dhttps://www.mydemoapp.com/oauth-redirect&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&realmId=1231434565226279
  • Note the authorization code returned in the code query parameter. You use it later in the flow.
  • Note the realmId. You use it in subsequent API endpoint URLs when retrieving data from the QuickBooks company. Keep track of the realmID from each response and check it against previous responses.  A duplicate realmID means that a user's existing connection has transferred to another user. Make sure your app handles this situation as appropriate. For instance, you may want to make sure you save settings from the first user.
  • Confirm that the state received from Intuit matches the state token you sent in the authentication request. This round-trip verification helps to ensure that the user, not a malicious script, is making the request.

Error responses

Certain error conditions trigger the single query parameter, error=, to be sent to the redirect URI.

Error responseDescription
access_deniedThe user did not authorize the request.
invalid_scopeAn invalid scope string was sent in the request.
Note

If your response endpoint renders an HTML page, any resources on that page will be able to see the authorization code in the URL. Scripts can read the URL directly, and all resources may be sent the URL in the Referer HTTP header. Carefully consider if you want to send authorization credentials to all resources on that page (especially third-party scripts such as social plugins and analytics). To avoid this issue, we recommend that the server first handle the request, then redirect to another URL that doesn't include the response parameters.

Exchange code for refresh and access tokens

Making the request

After the app receives the authorization code, it exchanges the authorization code for refresh and access tokens. Retrieve the base URI from the discovery document using the key, token_endpoint. The discussion here assumes the base URI is:

POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer 

This endpoint is accessible over https; plain http connections are refused.

FieldDescription
codeRequired. The authorization code returned from the initial request.
redirect_uriRequired. One of the redirect URIs listed for this project in the developer dashboard.
grant_typeRequired. As defined in the OAuth 2.0 specification, this field must contain a value of authorization_code.

The actual request might look like the following:

POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer HTTP/1.1
Accept: application/json
Authorization: Basic UTM0dVBvRDIwanp2OUdxNXE1dmlMemppcTlwM1d2
    NzRUdDNReGkwZVNTTDhFRWwxb0g6VEh0WEJlR3dheEtZSlVNaFhzeGxma1l
    XaFg3ZlFlRzFtN2szTFRwbw==
Content-Type: application/x-www-form-urlencoded
Host: oauth.platform.intuit.com
Body: grant_type=authorization_code&
code=L3114709614564VSU8JSEiPkXx1xhV8D9mv4xbv6sZJycibMUI&
redirect_uri=https://www.mydemoapp.com/oauth-redirect
Generating the authorization header

Calculate the authorization header value as follows:

"Basic " + base64encode(client_id + ":" + client_secret)

where client_id and client_secret are located on the Keys the tab on the app profile via My Apps on the developer site. There are two versions of these keys:

  • Development keys—use only in the sandbox environment.
  • Production keys—use only in the production environment. 

Handling the response

A successful response to this request contains the following fields:

FieldDescription
access_tokenThe token that must be used to access the QuickBooks API.
refresh_tokenA token used when refreshing the access token.

x_refresh_token_
expires_in

The remaining lifetime, in seconds, for the connection, after which time the user must re-grant access. See refresh_token policy for details.
expires_inThe remaining lifetime of the access token in seconds. The value always returned is 3600 seconds (one hour). Use the refresh token to get a fresh one. See Refreshing the access token for further information.   
token_typeIdentifies the type of token returned. At this time, this field will always have the value Bearer.

 

A successful response is returned as a JSON array, similar to the following:

{ 
"token_type": "bearer", 
"expires_in": 3600, 
"refresh_token":"L311478109728uVoOkDSUCl4s8FDRvjHR6kUKz0RHe3WtZQuBq",
"x_refresh_token_expires_in":15552000,
"access_token":"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..KM1_Fezsm6BUSaqqfTedaA.
dBUCZWiVmjH8CdpXeh_pmaM3kJlJkLEqJlfmavwGQDThcf94fbj9nBZkjEPLvBcQznJnEmltCIvsTGX0ue_w45h7_
yn1zBoOb-1QIYVE0E5TI9z4tMUgQNeUkD1w-X8ECVraeOEecKaqSW32Oae0yfKhDFbwQZnptbPzIDaqiduiM_q
EFcbAzT-7-znVd09lE3BTpdMF9MYqWdI5wPqbP8okMI0l8aa-UVFDH9wtli80zhHb7GgI1eudqRQc0sS9zWWb
I-eRcIhjcIndNUowSFCrVcYG6_kIj3uRUmIV-KjJUeXdSV9kcTAWL9UGYoMnTPQemStBd2thevPUuvKrPdz3ED
ft-RVRLQYUJSJ1oA2Q213Uv4kFQJgNinYuG9co_qAE6A2YzVn6A8jCap6qGR6vWHFoLjM2TutVd6eOeYoL2bb7jl
QALEpYGj4E1h3y2xZITWvnmI0CEL_dYQX6B3QTO36TDaVl9WnTaCCgAcP6bt70rFlPYbCjOxLoI6qFm5pUwGLLp
67JZ36grc58k7NIyKJ8dLJUL_Q9r1WoUvw.ZS298t_u7dSlkfajxLfO9Q"
}
Note

Other fields may be included in the response, and your application should not treat this as an error. The set shown above is the minimum set.

Add the Connect to QuickBooks button

To enable the user to initiate this OAuth authorization workflow from you app, provide the Connect to QuickBooks button on one or more pages of your app. Upon clicking this button, a popup window appears that begins the user authorization flow.  During this flow, the user selects a company and then authorizes your app to access the data.

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

refresh_token policy

The refresh_token policy specifies lifetime and expiry details of the refresh_token. Of note:

  • The lifetime for the refresh_token returned with the initial access_token is set to at least 100 days.
  • Each time a new access_token is requested, the refresh_token is reset to a new lifetime of at least 100 days.
  • If an access_token is not requested during the current refresh_token 100 day lifetime, the refresh_token expires and access to the QuickBooks company terminates. 
  • As long as it hasn't expired, the refresh_token can be reset as often as needed within a one year access window from the time the original access_token was generated.
  • If the user revokes the connection to the QuickBooks company, the refresh_token is revoked and is no longer valid. 

Access to the QuickBooks company terminates when the current refresh_token expires or at the end of the one year access window from the time the original access_token was generated, whichever is earlier. When access terminates, your app must ask the user to reauthorize the connection. 

Best practices
  • Always use the refresh token returned in the last token_endpoint response in your request for a fresh access token. 
  • The time to request a new access_token is when a QuickBooks API call returns a 401 error.

If your app uses an expired, revoked, or otherwise invalid refresh_token, it gets the following response:

{ 
    "error": "invalid_grant" 
}

Making the request

Retrieve the base URI for the request from the discovery document using the key, token_endpoint. The discussion here assumes the base URI is:

POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer 

This endpoint is accessible over https; plain http connections are refused.

FieldDescription
grant_typeRequired. When requesting a refresh token, set this to refresh_token.
refresh_tokenRequired. The refresh token returned in the last access token response.

The actual request might look like the following:

POST /oauth2/v1/tokens/bearer HTTP/1.1
Accept: application/json
Authorization: Basic UTM0dVBvRDIwanp2OUdxNXE1dmlMemppcTlwM1d2
    NzRUdDNReGkwZVNTTDhFRWwxb0g6VEh0WEJlR3dheEtZSlVNaFhzeGxma1l
    XaFg3ZlFlRzFtN2szTFRwbw==
Content-Type: application/x-www-form-urlencoded
Host: oauth.platform.intuit.com
Cache-Control: no-cache
Body: grant_type=refresh_token&
refresh_token=Q311488394272qbajGfLBwGmVsbF6VoNpUKaIO5oL49aXLVJUB

Handling the response

A successful response to this request contains the following fields:

FieldDescription
access_tokenThe token that must be used to access the QuickBooks API. The previous token is invalidated.
expires_inThe remaining lifetime of the access token in seconds. The value always returned is 3600 seconds (one hour). Use the refresh token to get a fresh one. See Refreshing the access token for further information.   
refresh_token

A token used to obtain a new access token. All previous refresh tokens are stale and unusable.

token_typeIdentifies the type of token returned. At this time, this field will always have the value Bearer.
x_refresh_token_
expires_in
The remaining lifetime, in seconds, for the connection, after which time the user must re-grant access. See refresh_token policy for details.

A successful response is returned as a JSON array, similar to the following:

{ 
"token_type": "bearer", 
"expires_in": 3600, 
"refresh_token":"Q311488394272qbajGfLBwGmVsbF6VoNpUKaIO5oL49aXLVJUB",
"x_refresh_token_expires_in":15551893,
"access_token":"eJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGGlyIn0..KM1_Fezsm6BUSaqqfTedaA.
dBUCZWiVmjH8CdpXeh_pmaM3kJlJkLEqJlfmavwGQDThcf94fbj9nBZkjEPLvBcQznJnEmltCIvsTGX0ue_w45h7_
yn1zBoOb-1QIYVE0E5TI9z4tMUgQNeUkD1w-X8ECVraeOEecKaqSW32Oae0yfKhDFbwQZnptbPzIDaqiduiM_q
EFcbAzT-7-znVd09lE3BTpdMF9MYqWdI5wPqbP8okMI0l8aa-UVFDH9wtli80zhHb7GgI1eudqRQc0sS9zWWb
I-eRcIhjcIndNUowSFCrVcYG6_kIj3uRUmIV-KjJUeXdSV9kcTAWL9UGYoMnTPQemStBd2thevPUuvKrPdz3ED
ft-RVRLQYUJSJ1oA2Q213Uv4kFQJgNinYuG9co_qAE6A2YzVn6A8jCap6qGR6vWHFoLjM2TutVd6eOeYoL2bb7jl
QALEpYGj4E1h3y2xZITWvnmI0CEL_dYQX6B3QTO36TDaVl9WnTaCCgAcP6bt70rFlPYbCjOxLoI6qFm5pUwGLLp
67JZ36grc58k7NIyKJ8dLJUL_Q9r1WoUvw.ZS298t_u7dSlkfajxLfO9Q"
}

Calling QuickBooks APIs

After your application obtains an access token, you can use it to make calls to QuickBooks API resources. To do this, include the access token in a request to the API by including it in the Authorization: Bearer HTTP header. You can try out all the QuickBooks APIs and view their scopes at the OAuth 2.0 Playground.

Example

A call to the production Invoice endpoint to read a given object using the access_token parameter might look like the following, though you'll need to specify your own access token:

GET https://quickbooks.api.intuit.com/v3/company/<companyID>/invoice/<invoiceID>
Authorization=Bearer eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiZGlyIn0..eF2J3XEDnGBjpO469rw9Dw.Gi3Oc
  UsfFm8Pnxw1aneLN-5o0j_nE0G9t6jbvAFgMy4-QYkmzUCVmgJWCj9HTZ2ojgQaKjYoEwZMO3uBvs
  QUo8UIdZIOQz_3HxXggq9VIlhihH7d1NXLiFMC8dQGtFhECGypv6xJ8Ob7Ay00CbEq_tns1wgfXXFgr
  L-FcoWtuubxHUjKE7EwlM8nB0VQG1VTwjWzEcPPTr7EjK0KQWa9vXzs6W24s09mXZMli0axYobcuB
  GuFwt4RCfxARGOdyu8J9tg-QziFPX3TwIVMsAxKLdDZkMcy1fEyXW_A0H3z6IcKzvjxFRl10cASRmNb
  wSGHk8C1W89HLbeeL_wx_Bg6qTpMBjBqnpGYMVG9vSy-fmKDPd6Uysw3DvvOYkX5pFGkln6X5F2fcdxw
  VaV05IGUyxsX-Je4UY671P54ScRVlEXTAQpnmhCiR4euadvPmKqNtk70n02ExAkMJ5UbyS2mcupPjXk7C67
  qVu2kfVMLFFdg1AKhBzlpl-KqvTsgO2-bHYpKS9qwLP1CdKw_1NxB9cvjBmGQ3S33P6WjEg_eNM.rZ1h6
  s-rQb2D_PX8dIYb5g
Accept=*/*
Content-Type=application/json;charset=UTF-8

Incremental authorization

We recommend requesting scopes as needed. It is generally a best practice to request scopes incrementally, at the time access is required, rather than up front. Each time you change the set of scopes you must initiate the authorization workflow again with the new list of scopes and you must get new access and refresh tokens. 

When adding scopes to your authorization, you must send the full complement of scopes you need at that point in your app. For example, an app that wants to support purchases should not request QuickBooks Payments API access until the user presses the Buy button. That is, in the request for a new authorization code you add com.intuit.quickbooks.payment to your list of existing scopes.

About anti-forgery state tokens

You must protect the security of your users by preventing request forgery attacks. The first step is creating a unique session token that holds state between your app and the user's client. You later match this unique session token with the authentication response returned by the Intuit OAuth Login service to verify that the user is making the request and not a malicious attacker. These tokens are often referred to as cross-site request forgery (CSRF) tokens.

One good choice for a state token is a string of 30 or so characters constructed using a high-quality random-number generator. Another is a hash generated by signing some of your session state variables with a key that is kept secret on your back-end.

Revoking access

Your app can programmatically revoke access given to it by a specific user. Use the revoke endpoint to request permissions granted to the application to be removed. 

Making the request

Retrieve the base URI for the revoke endpoint from the discovery document using the key, token_endpoint. The discussion here assumes the base URI is:

POST https://developer.api/intuit.com/v2/oauth2/tokens/revoke

 The actual request might look like the following:

POST https://developer.api/intuit.com/v2/oauth2/tokens/revoke HTTP/1.1
Accept: application/json
Authorization: Basic UTM0dVBvRDIwanp2OUdxNXE1dmlMemppcTlwM1d2
    NzRUdDNReGkwZVNTTDhFRWwxb0g6VEh0WEJlR3dheEtZSlVNaFhzeGxma1l
    XaFg3ZlFlRzFtN2szTFRwbw==
Content-Type: application/json
{
"token": "{bearerToken or refreshToken}"
}

Generating the authorization header 

Calculate the authorization header value as follows:

"Basic " + base64encode(client_id + ":" + client_secret)

where client_id and client_secret are located on the Keys the tab on the app profile via My Apps on the developer site. There are two versions of these keys:

  • Development keys—use only in the sandbox environment.
  • Production keys—use only in the production environment.

Handling the response

This request returns and empty response body and one of the following status codes:

CodeDescription
200Successful revoke.
400One or more of BearerToken, RefreshToken, ClientId or,ClientSecret are incorrect.
401Bad authorization header or no authorization header sent.
500Intuit server internal error, not the fault of the developer.

Discovery document

The OpenID Connect protocol requires the use of multiple endpoints for authenticating users, and for requesting resources including tokens, user information, and public keys.

To simplify the implementation and increase flexibility, OpenID Connect allows the use of a discovery document, a JSON document found at a well known location containing key-value pairs that provide details about the OpenID Connect configuration, including the URIs of the authorization, token, userinfo, and public-keys endpoints. The discovery documents for Intuit's OpenID Connect service are located here:

Here is an example of a discovery document; the field names are those specified in OpenID Connect Discovery 1.0 (refer to that document for their meanings). The values here are illustrative, although they are copied from from a recent version of the actual Intuit discover document, they may have changed. Always refer to the actual document for up-to-date information.

{ 
   issuer:"https://oauth.platform.intuit.com/op/v1",
   authorization_endpoint:"https://appcenter.intuit.com/connect/oauth2",
   token_endpoint:"https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer",
   userinfo_endpoint:"https://accounts.intuit.com/v1/openid_connect/userinfo",
   revocation_endpoint:"https://developer.api.intuit.com/v2/oauth2/tokens/revoke",
   jwks_uri:"https://oauth.platform.intuit.com/op/v1/jwks",
   response_types_supported:[ 
      "code"
   ],
   subject_types_supported:[ 
      "public"
   ],
   id_token_signing_alg_values_supported:[ 
      "RS256"
   ],
   scopes_supported:[ 
      "openid",
      "email",
      "profile",
      "address",
      "phone"
   ],
   token_endpoint_auth_methods_supported:[ 
      "client_secret_post",
      "client_secret_basic"
   ],
   claims_supported:[ 
      "aud",
      "exp",
      "iat",
      "iss",
      "realmid",
      "sub"
   ]
}

Security guidelines

 Got Questions? Get Answers in our developer forums.