I am going through an official example of how to implement Oauth2 for using the quickbooks API. I am having trouble getting the state_token from the session variables within the callback view. Whenever I ise the oauth view it sends an authorization request to the quickbooks server. The request gives me a state token and a url to use to get an authorization code to use other APIs in quickbooks. I was able to print the state token in the oauth view by just printing "request.session['state']". This lets me now that it is being saved in the session. So when I redirect to the given url, Quickbooks sends me a code with the same state_token it gave me using the callback view in this example. The callback endpoint is requested by the Quickbooks server for validation purposes to make sure the state_token given back and the state_token in the session are the same, otherwise it'll return an error. This callback endpoint was given by me to Quickbooks website. But whenever I try and retrieve the state_token within the callback view, it is always null/None. So whenever the callback view checks if the state tokens are the same in
it always returns unauthorized. I've debugged a lot and found out that the session id when the session is stored in oauth() and when I try fetching it in callback() are different. I've looked in the issues on GitHub and it seems no one is having an issue like me. Is there a step I am missing or could the issue be out-dated dependencies?
Here is a part from the docs where I am referring to:
Use the ‘state’ parameter in your authorization request to add your unique session token. Your app will match the unique session token with the one returned in the Intuit OAuth 2.0 server response. This verifies the user, not a malicious attacker or bot, started the authorization process.
from intuitlib.client import AuthClient
from intuitlib.migration import migrate
from intuitlib.enums import Scopes
from intuitlib.exceptions import AuthClientError
from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseServerError
from django.conf import settings
from django.contrib.sessions.models import Session
from django.core import serializers
from app.services import qbo_api_call
# Create your views here.
def index(request):
return render(request, 'index.html')
def oauth(request):
auth_client = AuthClient(
settings.CLIENT_ID,
settings.CLIENT_SECRET,
settings.REDIRECT_URI,
settings.ENVIRONMENT,
)
url = auth_client.get_authorization_url([Scopes.ACCOUNTING])
request.session['state'] = auth_client.state_token
return redirect(url)
def callback(request):
auth_client = AuthClient(
settings.CLIENT_ID,
settings.CLIENT_SECRET,
settings.REDIRECT_URI,
settings.ENVIRONMENT,
state_token=request.session.get('state', None),
)
state_tok = request.GET.get('state', None)
error = request.GET.get('error', None)
if error == 'access_denied':
return redirect('app:index')
if state_tok is None:
return HttpResponseBadRequest()
elif state_tok != auth_client.state_token:
return HttpResponse('unauthorized', status=401)
auth_code = request.GET.get('code', None)
realm_id = request.GET.get('realmId', None)
request.session['realm_id'] = realm_id
if auth_code is None:
return HttpResponseBadRequest()
try:
auth_client.get_bearer_token(auth_code, realm_id=realm_id)
request.session['access_token'] = auth_client.access_token
request.session['refresh_token'] = auth_client.refresh_token
request.session['id_token'] = auth_client.id_token
except AuthClientError as e:
# just printing status_code here but it can be used for retry workflows, etc
print(e.status_code)
print(e.content)
print(e.intuit_tid)
except Exception as e:
print(e)
return redirect('app:connected')
def connected(request):
auth_client = AuthClient(
settings.CLIENT_ID,
settings.CLIENT_SECRET,
settings.REDIRECT_URI,
settings.ENVIRONMENT,
access_token=request.session.get('access_token', None),
refresh_token=request.session.get('refresh_token', None),
id_token=request.session.get('id_token', None),
)
if auth_client.id_token is not None:
return render(request, 'connected.html', context={'openid': True})
else:
return render(request, 'connected.html', context={'openid': False})
I've used print methods to print the session id and to check if the sessions were the same between oauth and callback.
I am going through an official example of how to implement Oauth2 for using the quickbooks API. I am having trouble getting the state_token from the session variables within the callback view. Whenever I ise the oauth view it sends an authorization request to the quickbooks server. The request gives me a state token and a url to use to get an authorization code to use other APIs in quickbooks. I was able to print the state token in the oauth view by just printing "request.session['state']". This lets me now that it is being saved in the session. So when I redirect to the given url, Quickbooks sends me a code with the same state_token it gave me using the callback view in this example. The callback endpoint is requested by the Quickbooks server for validation purposes to make sure the state_token given back and the state_token in the session are the same, otherwise it'll return an error. This callback endpoint was given by me to Quickbooks website. But whenever I try and retrieve the state_token within the callback view, it is always null/None. So whenever the callback view checks if the state tokens are the same in
it always returns unauthorized. I've debugged a lot and found out that the session id when the session is stored in oauth() and when I try fetching it in callback() are different. I've looked in the issues on GitHub and it seems no one is having an issue like me. Is there a step I am missing or could the issue be out-dated dependencies?
Here are the docs I am following: https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/faq
Here is a part from the docs where I am referring to:
Use the ‘state’ parameter in your authorization request to add your unique session token. Your app will match the unique session token with the one returned in the Intuit OAuth 2.0 server response. This verifies the user, not a malicious attacker or bot, started the authorization process.
I've used print methods to print the session id and to check if the sessions were the same between oauth and callback.