Add OpenID Connect to OAuth 2.0

If you want your app to use Intuit Single Sign-on for authorization, you can add additional functionality after you implement OAuth 2.0.

We’ll show you how to modify your existing implementation so it conforms with the OpenID specification and is thus OpenID certified.

The set up process is very similar to OAuth 2.0, but the key difference is the type of tokens used. Your app needs to authenticate users by obtaining and validating ID tokens. This additional authentication ensures that apps that use our single sign-on provider conform to the OpenID Connect spec.

Step 1: Pick the right authentication scopes

One of the primary differences between standard OAuth 2.0 authentication and OpenID Connect is the required scopes.

OAuth 2.0 in general supports the two main scopes for the QuickBooks Online Accounting API (com.intuit.quickbooks.accounting) and QuickBooks Payments API (com.intuit.quickbooks.payment).

OpenID Connect adds a few additional scopes:

Step 2: Set up OAuth 2.0

If you haven’t already, follow the steps to implement OAuth 2.0.

Once you get to the “Exchange the authorization code for access tokens”step, come back to these OpenID Connect steps.

Step 3: Learn about end-to-end ID token validation

To authenticate users via OpenID Connect, your app needs to get and then validate ID tokens. ID tokens are a standardized feature of OpenID Connect for sharing identity assertions over the web.

While authorization is a simple one-step process for users, it involves several tasks on the backend. Here’s a brief overview of the authorization flow for OpenID Connect:

Once you get access and ID tokens, your app can make API calls.

Step 4: Exchange authorization codes for ID tokens and access tokens

If you’ve followed the OAuth 2.0 set up and your app sends an authorization request when users connect, it will get an authorization code from the Intuit OAuth 2.0 Server.

Your app should send the authorization code (i.e. the value of the code parameter) back to the Intuit OAuth 2.0 server to exchange it for access and ID tokens.


If you’re developing with a QuickBooks SDK

Use the example code to create an object named tokenResponse. This automatically exchanges the authorization code for access and refresh tokens:

.NET

Java

PHP

Node.js

Python

Ruby

1
2
3
4
5
6
7
// Get OAuth2 Bearer token and ID token
var tokenResponse = await auth2Client.GetBearerTokenAsync(code);

//retrieve access_token and refresh_token
tokenResponse.AccessToken
tokenResponse.RefreshToken
idToken = tokenResponse.IdentityToken;

1
2
3
4
5
6
7
8
9
//Prepare OAuth2PlatformClient
OAuth2PlatformClient client  = new OAuth2PlatformClient(oauth2Config);

//Get the bearer token (OAuth2 tokens)
BearerTokenResponse bearerTokenResponse = client.retrieveBearerTokens(authCode, redirectUri);

//retrieve the token using the variables below
bearerTokenResponse.getAccessToken()
bearerTokenResponse.getIdToken()

1
2
3
$accessToken = $OAuth2LoginHelper->exchangeAuthorizationCodeForToken("authorizationCode", "realmId");
$accessTokenValue = $accessTokenObj->getAccessToken();
$refreshTokenValue = $accessTokenObj->getRefreshToken();

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Parse the redirect URL for authCode and exchange them for tokens
var 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.getJson()));
    })
    .catch(function(e) {
        console.error("The error message is :"+e.originalMessage);
        console.error(e.intuit_tid);
    });

1
2
3
4
5
6
7
# get OAuth2 Bearer token
auth_client.get_bearer_token(auth_code, realm_id=realm_id)

# retrieve access_token and refresh_token
auth_client.access_token
auth_client.refresh_token
auth_client.id_token

1
2
oauth2Token = oauth_client.token.get_bearer_token('the authorization code returned from authorizationCodeUrl')
# => #<IntuitOAuth::ClientResponse:0x00007f9152b5c418 @access_token="the access token", @expires_in=3600, @refresh_token="the refresh token", @x_refresh_token_expires_in=8726400>

If you’re creating an HTTPS/REST request manually

Get the base URI from the discovery document. You can also follow these links:

Create a POST request to exchange the authorization code for access and ID tokens.

Send requests to the token_endpoint (available in the discovery document) using the following parameters. Only send one request to exchange the authorization code. Multiple requests may invalidate tokens:


Request parameters for token exchange
Field Description Required
code The authorization code your app received from the Intuit OAuth 2.0 response. Yes
redirect_uri The redirect URI listed for your app. You set this in Step 7. Yes
grant_type The type defined by the OAuth 2.0 server specification. It must have the value authorization_code. Yes

Here’s an example token exchange request:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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

The authorization header should follow this format:

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

See an example server response for exchanged tokens

The server returns a JSON object with both tokens.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"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",
"id_token":"eyJraWQiOiJyNHA1U2JMMnFhRmVoRnpoajhnSSIsImFsZyI6IlJTMjU2In0.eyJzd
WIiOiJiMDUzZDk5NC0wN2Q1LTQ2OGQtYjdlZS0yMmUzNDlkMmU3MzkiLCJhdWQiOlsiTDM5ZWxTdWJGeGpQT1
NwZFpvWVdSS2lDQ0U2VElOanY2N1JvYUU4ekJxYkl4eGI0bEsiXSwicmVhbG1pZCI6IjExMDgwMzM0Nz
EiLCJhdXRoX3RpbWUiOjE0NjI1NTQ0NzUsImlzcyI6Imh0dHBzOlwvXC9vYXV0aC1lMmUucGxhdGZvcm
0uaW50dWl0LmNvbVwvb2F1dGgyXC92MVwvb3BcL3YxIiwiZXhwIjoxNDYyNTYxMzI4LCJpYXQiOjE0NjI1
NTc3Mjh9.BIJ9x_WPEOZsLJfQE3mGji_Q15j_rdlTyFYELiJM-W92fWSLC-TLEwCp5IrRhDWMvyvrLSMZCEd
QALYQpbVy8uKI22JgGWYvkwNEDweOjbYzyt33F4xtn3GGcW9nAwRtA3M19qquWyi7G0kcCZUDN8RfUXz2qKM
J6KPOfLVe2UQ"
}
Field Description
access_token The token used to access the QuickBooks Online API. Max length: 4096 characters.
refresh_token The token used for refreshing the access token. Max length: 512 characters.
id_token The token used for OpenID Connect and associated scopes for user authentication.
x_refresh_token_expires_in The remaining lifetime of the current refresh token. This is in seconds. When this expires, users must reauthorize your app.
expires_in The remaining lifetime of the access token. The value begins at 3600. This is in seconds (one hour).
token_type Identifies the type of token returned. This always has the value bearer.
Tip: The response may have other parameters. Don’t treat them as errors. This only lists the minimum set.
Step 5: Validate ID token

Your app needs to validate all ID tokens on your server unless you know they came directly from Intuit.

Since most external API libraries also validate when decoding the base64 and parsing the JSON, your app may automatically validate when it accesses the fields on the ID token.

You can also review the official OpenID validation specs.


If you’re developing with a QuickBooks SDK

If you use our supported SDKs, we handle the validation process for you. Here are some examples:

.NET

Java

PHP

Node.js

Python

Ruby

1
var isTokenValid = await oauthClient.ValidateIDTokenAsync(tokenResponse.IdentityToken);

1
boolean valid = client.validateIDToken(bearerTokenResponse.getIdToken());

1
2
//Return true if it is correct, or false otherwise
$result = $OAuthLoginHelper::ValidateIDToken($clientID, $ID_token);

1
2
3
4
5
6
7
8
9
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

1
2
# Python client sets id_token value only after validating it
# No additional step needed for validation

1
validated = validate_id_token(id_token)

If you’re not using an SDK

Apps need to include signatures in requests to ensure requests sent between your app, our server, and connected QuickBooks Online companies aren’t changed during transit.

Thus, your app needs to verify that the signing authority for responses are from Intuit. Here are a few ways you can check signatures:


Create JSON Web Tokens to use as ID tokens

You can create and use a JSON Web Token (JWT) as the ID token. JWTs consist of three parts: a header, a payload, and a signature, all separated by dots.

Create the header

Headers have two fields:

Here’s an example:

1
2
3
4
{
    kid: "r4p5SbL2qaFehFzhj8gI",
    alg: "RS256"
}

This JSON is then Base64Url encoded to form the first part of the JSON Web Token.

Create the payload

The payload contains claims - the metadata about the user.

Claim Description
aud The audience identifier the ID token is intended for. Set this field to your app’s Client ID. Use the Client ID for the type of app you’re validating (i.e. an app in development or one that’s live and in production).
auth_time The time the ID token was authorized
exp The time the ID token expires. This is in unix time in integer seconds.
iat The time the ID token was issued. This is in unix time in integer seconds.
iss The identifier for the issuer of the response. Set this field to https://oauth.platform.intuit.com/op/v1.
realmId The identifier for the specific QuickBooks Online company connected to your app. The realmID is returned whenever apps specify the QuickBooks Online API or Payments API scopes in authorization requests.
sub The identifier for users’ Intuit accounts. This value is unique across all Intuit products. It’s never reused. Keep in mind, an Intuit account can have multiple emails at various points in time. However, the sub value never changes. Use the sub value as a unique identifier key for users.

Here’s an example payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "sub": "1182d6ec-2a1f-4aa3-af3f-bb3b95db45af",
  "aud": [
    "L3Y7SV6rRxVvArdYzlRxjPXo0b6ItrX4qFhopPXQ6aaEWgKyCa"
  ],
  "realmId": "123145880168382",
  "auth_time": 1464330769,
  "iss": "https://oauth.platform.intuit.com/op/v1",
  "exp": 1464335838,
  "iat": 1464332238
}

Create the signature

Signatures verify the identity of the source sending the JSON Web Token.

To create the signature, sign a concatenation of the encoded header, the encoded payload, and a secret with a specified algorithm.

HMAC is a good standard. Here’s an example signature using the HMAC SHA256 algorithm:

1
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Step 6: Get user profile info

Use access tokens to call a user’s Intuit user profile endpoint and get additional data about them.

The response for this endpoint depends on the scopes you chose when you got the original access token. We reviewed scopes in Step 1.

Here’s an example response if you picked the openID, email, and profile scopes:

1
2
3
4
5
6
7
{
   "sub": "1182d6ec-2a1f-4aa3-af3f-bb3b95db45af",
   "email": "john@doe.com",
   "emailVerified": true,
   "givenName": "John",
   "familyName": "Doe"
}

Warning

Warning: Your app must check for emailVerified field and allow users access to app only if emailVerified is true.
If you’re developing with a QuickBooks SDK

Here are example requests for supported SDKs:

.NET

Java

PHP

Node.js

Python

Ruby

1
var userInfoResp = await auth2Client.GetUserInfoAsync(“accessToken‘);

1
2
3
4
5
//Prepare OAuth2PlatformClient
OAuth2PlatformClient client  = new OAuth2PlatformClient(oauth2Config);

//Get user info (Use access token from bearerTokenResponse)
UserInfoResponse response = client.getUserInfo(accessToken);

1
$userInfo = $OAuthLoginHelper::getUserInfo($accessToken);

1
2
3
4
5
6
7
oauthClient.getUserInfo()
        .then(function(response){
            console.log('The User Info is  : ' + JSON.stringify(response.json()));
        })
        .catch(function(e) {
            console.log('The error is '+ JSON.stringify(e));
        });

1
response = auth_client.get_user_info(access_token='EnterAccessTokenHere')

1
result = oauth_client.openid.get_user_info('accessToken')

If you’re creating an HTTPS/Rest request manually

Get the base URI from the discovery document. You can also follow these links:

Create a GET request and use to the userinfo_endpoint. Here’s an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
GET https://accounts.platform.intuit.com/v1/openid_connect/userinfo
Accept: application/json
Authorization: Bearer <access token>

{
   "sub": "1182d6ec-2a1f-4aa3-af3f-bb3b95db45af",
   "email": "john@doe.com",
   "emailVerified": true,
   "givenName": "John",
   "familyName": "Doe",
   "phoneNumber": "+1 6305555555",
   "phoneNumberVerified": false,
   "address": {
      "streetAddress": "2007 saint julien ct",
      "locality": "mountain view",
      "region": "CA",
      "postalCode": "94043",
      "country": "US"
   }
}

After getting a user’s info, query your app’s user database.

If the user already exists, but their Intuit user profile (identified by the sub field) isn’t connected or established, initiate an application session for the user. Prompt them to enter their password and ask if you can want you to link their Intuit user profile info to their existing account on your database.

If the user doesn’t exist in your user database, redirect them to your app’s new user signup flow so they can create a new profile. You may be able to use info from our APIs to auto-register users, or populate data fields in your signup flow.

Next steps: Refresh or revoke access tokens

Follow the same basic steps as the OAuth 2.0 implementation to refresh or revoke access tokens.