python-quickbooks
python-quickbooks copied to clipboard
Getting " 400 - invalid grant" with Quickbooks online
I am using https://pypi.org/project/intuit-oauth/ and https://github.com/ej2/python-quickbook every couple of days I am getting invalid grant and need to go through the online url again to get new refresh and access tokens.
What am I doing wrong?
from intuitlib.client import AuthClient
def get_auth_client(config):
return AuthClient(
client_id=_get_client_id(config),
client_secret=_get_client_secret(config),
environment="production",
redirect_uri={my_redirect_url},
)
def get_quickbooks_client(config):
auth_client = get_auth_client(config)
quickbook_client = QuickBooks(
auth_client=auth_client,
company_id=config.COMPANY_ID,
refresh_token=_get_refresh_token(config)
)
return quickbook_client
_get_client_id
returns the correct client_id.
_get_client_secret
returns the correct client secret.
config.COMPANY_ID
is the Quickbooks company ID.
_get_refresh_token
is the refresh token that is returned during the initial oauth set when I sign in via the authorisation url.
I had the same issue and stumbled upon this thread.
Key points from the Intuit faq: https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/faq
"Tip: We update the value of the refresh_token value every 24 hours hours, or the next time you refresh the access tokens after 24 hours. This is an additional security measure by Intuit to reduce risk of compromise."
"Each time you make an API call and refresh your app’s access token, our server also sends your app a new value for the refresh_token."
When we initialize an AuthClient without an access_token, the refresh function is called to get a new access_token. When that api call is made, a new refresh_token is also returned that may be different from the original. We need to store the new refresh_token because the original is now considered “stale”. Using it will throw the invalid grant error.
Haven't been able to test this yet since I haven't found a way to simulate the changing refresh_token value in sandbox mode - I need to wait 24 hrs. I hope this points you in the right direction!
Check out this thread too
https://help.developer.intuit.com/s/question/0D50f00006mv4MBCAY/randomly-getting-invalid-grant-error-when-trying-to-generate-access-token-from-refresh-token-in-authclient
I just implemented the proposed solution there last night, I'm not sure yet if it will work for the long term, but in a nutshell it is to check the auth_client.refresh_token after initializing and storing if it has changed
I treat the refresh_token as a single-use token, even though it has a 100-day lifespan. When a new access_token is obtained, the refresh_token should be replaced with the one returned alongside the new access token. In a cloud server environment where multiple instances of my program might attempt to access QuickBooks simultaneously, a race condition can occur. To address this, I serialize access to the stored refresh_token using Postgres.
I store the tokens in a table row designated for each company_id and include a column that serves as a lock when refreshing the tokens. This ensures that only one process can refresh the tokens at any given time. While I have not extensively tested this solution in a production setting, it appeared to work effectively during the limited testing conducted before the project shifted away from QuickBooks.