Introduction to token management
As APIs and integrations have become increasingly important in today’s digital landscape, token management is critical to a secure and reliable integration design. In this article, we’ll provide an overview of token management, including implementation considerations and best practices.
Token management encompasses the generation, storage, usage, refreshing, and revocation of tokens. These tokens grant us access to protected resources, such as financial transactions or reports housed inside a customer’s QuickBooks Online (QBO) account.
We generally interact with three types of tokens on the Intuit Developer Platform:
1. Access token
This is also referred to as a bearer token. We pass this in the Authorization header of requests each time we call Intuit APIs. It follows this syntax:
Authorization: bearer {accessToken}
Characteristics:
- Access tokens are valid for one hour.
- If an API request returns a 401 unauthorized error, it likely means the access token has expired.
2. Refresh token
We use this token to refresh an access token. To do so, we send a request to the token_endpoint and pass the refresh token in the body.
Characteristics:
- Refresh tokens are valid for 100 days. This is a rolling expiry date that is extended each time the token is used to refresh an access token.
3. ID token
This standardized feature of OpenID Connect is used for sharing identity assertions. After being validated and decoded, the payload will contain data such as the aud (audience identifier) and sub (unique identifier for the user).
Characteristics:
- We’ll receive this token post-handshake if OpenID scopes were requested in the authorization request.
- Our app needs to validate all ID tokens we receive.
Implementation considerations
Let’s start with a quick recap. During the OAuth flow, a user will choose to authorize your app. Upon doing so, your app will receive a response with an authorization code as well as a realmID (sent to the redirect_uri you declared in the request). It may look like this:
https://www.mydemoapp.com/oauthcallback?
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
state=kc913_security_cr239rklafka7ff324f24f2clpa9i08457macl138gnascac3&
realmId=1231434565226279
After receiving the authorization code and realmID, you’ll want to do a few things:
- Check that the state value in the server response matches the value you sent in the initial request.
- If it does, copy the code and realmId received in the server response.
- If it doesn’t, disregard the server response entirely. This a necessary step to ward against cross-site request forgery (CSRF).
With the code value in hand, exchange the authorization code for tokens by raising a server-to-server request to the token_endpoint and passing the authorization code in the body like this:
POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer HTTP/1.1
Accept: application/json
Authorization: Basic UTM0dVBvRDIwanp2OUdxNXE1dmlMemppcTlwM1d2
NzRUdDNReGkwZVNTTDh0WEJlR3dheEtZSlVNaXaFg3ZlFlRzFtN2szTFRwbw==
Content-Type: application/x-www-form-urlencoded
Host: oauth.platform.intuit.com
Body: grant_type=authorization_code&
code=L3114709614564VSU8JSEiPkXx1xhV8D9mv4x6sZJycibMUI&
redirect_uri=https://www.mydemoapp.com/oauthcallback
The server response will contain an access token, a refresh token, and if you requested OpenID Connect scopes, an ID token as well. An example response is as follows:
{
"token_type": "bearer",
"expires_in": 3600,
"refresh_token":"L311478109728uVoOkDSUCl4s8FDRvkU0RHe3WtZQuBq",
"x_refresh_token_expires_in":8640000,
"access_token":"eyJlbmMiOiJBMTI4Q0JDLUIiwiYWxnIjoiZ.....vw.ZS298t_udSlkfajxLfO9Q",
"id_token":"eyJraWQiOiJyNHA1U2JMMnFhRmVoRnpoajhnSSIsI.....J6KPOfLVe2UQ"
}
At this point, you’ll want to store the realmId (received earlier in the first response), access token, refresh token, and the refresh token expiry time (initially expressed in seconds). ID tokens are generally not stored since they are used to validate the identity of a user.
An example database table is as follows:
id | ingest_date | realm_id | access_token | refresh_token | refresh_token_expiry |
1 | 2024-05-25 | 913012341234 | eyJlMi…kcz98Qp | eyJraQ…12k | 2024-09-02 |
2 | 2024-05-28 | 450167671212 | yN1U2…28uVc0Zs | J6KOf…3kdlR | 2024-09-05 |
3 | 2024-05-29 | 912067890021 | 8t_ufajx…9ckzZlo1 | NBoTIQ…9ckz | 2024-09-06 |
Best practices
Storage
Always store the latest values of an access token and a refresh token, even if the value of the refresh token hasn’t changed. This helps to ensure you won’t ever miss new values.
Relationships
Maintain an accurate relationship between a realmId and its related access and refresh token. An access token or a refresh token will only work for API requests to the realmId it was authorized against.
Encryption
When not in use, ensure tokens are encrypted at all times with a key that is stored elsewhere – e.g. as an environment variable.
Key/secrets vaults
One option is to use a key or a secrets vault to store tokens. Many of the popular cloud options offer envelope encryption as an additional security measure.
Refreshing tokens nearing expiration
If a refresh token is nearing the expiration date, consider refreshing these proactively to ensure a delightful customer experience (assuming they relate to an active customer).
Token revocation
If a user wants to disconnect your app from their QuickBooks Online account, ensure you fully revoke the tokens by calling the revocation endpoint. This should be followed by a cleanup process in the relevant database table.
Token cleanup
If tokens expire or your app receives a disconnection request, consider cleaning up the tokens in your database.
SDKs
Consider using one of Intuit’s SDKs or OAuth client libraries. See this page for more information.
We hope this article is useful and provides you with new ideas to improve token management in your app. You might also want to read our article on new discoverability features in the QuickBooks App Store, which you can find here.