I’m working on a Python app (my first!) that needs to allow my users to authenticate using Auth0. I followed the Python Quickstart and that all works fine. Having authenticated the user, I need to get an IDToken instead of an Access Token, because I need to access some Amazon AWS resources (S3, DynamoDB) using Cognito credentials. To get this ID token I’m following the Auth0 ‘Execute an Authorization Code Grant Flow’ tutorial. This says I need to pass the ‘code’ value I get back from the authentication step. I have done that, but when I make the POST to Auth0 /token endpoint I’m not getting an ID token back, in fact I’m always getting an HTTP 403 error code. Here’s the relevant section of my Python app, any assistance gratefully received!
@app.route(‘/callback’)
def callback_handling():
auth0.authorize_access_token()
payload = {‘grant_type’: ‘authorization_code’, ‘client_id’: AUTH0_CLIENT_ID, ‘client_secret’: AUTH0_CLIENT_SECRET, ‘code’: request.args.get(‘code’,‘’), ‘redirect_uri’: AUTH0_CALLBACK_URL}
response = requests.post(‘https://pichow.auth0.com/oauth/token’, json=payload)
Maybe there’s some method of the oauth object I can use to get the id token, or I need additional scope(s)? As I say, my code is heavily based on the Python Quickstart from Auth0, so my code looks like this:
oauth = OAuth(app)
auth0 = oauth.register(
‘auth0’,
client_id=AUTH0_CLIENT_ID,
client_secret=AUTH0_CLIENT_SECRET,
api_base_url=AUTH0_BASE_URL,
access_token_url=AUTH0_BASE_URL + ‘/oauth/token’,
authorize_url=AUTH0_BASE_URL + ‘/authorize’,
client_kwargs={
‘scope’: ‘openid profile’,
},
Check your tenant logs (in the Logs tab available from the left of your Auth0 dashboard).
Any message there that might be helpful?
John
Hi John. Many thanks for your prompt response!
I didn’t know about the Logs tab so thanks for pointing me to that. I’m sure it will be useful in all sorts of ways…
The log entries tell me what the problem is but don’t give many clues as to how to fix it. Each time I tried my test case I see three log entries: A “Success Login” entry from where the Quickstart-based code has signed in the user, followed by a “Success Exchange” (Authorization Code for Access Token) which I believe also comes from the Quickstart code authenticating (the auth0.authorize_access_token() line), followed by a “Failed Exchange” (Invalid Authorization Code) which I guess is from my attempt to perform a POST to the /token endpoint to get an ID token. Here’s an example of the “Failed Exchange” log entry
{
“date”: “2018-08-03T11:45:00.488Z”,
“type”: “feacft”,
“description”: “Invalid authorization code”,
“connection_id”: “”,
“client_id”: “icr…”,
“client_name”: “Pic…”,
“ip”: “109.159.174.218”,
“user_agent”: “python-requests/2.19.1”,
“hostname”: “pic…auth0.com”,
“user_id”: “”,
“user_name”: “”,
“log_id”: “90020180803114500489927443518490143471738261424146743426”
}
as you see from my code, for the authorization code I’m just passing the value of the ‘code’ provided by the aguments of the preceding request (request.args.get(‘code’)) and I made sure in the debugger that this was not a None value and looked like the right sort of string…
Hi, you can use the code only once. Your first call to /oauth/token should return both the access token and id token.
John
Sorry John, I’m new to this. I’m only making one explicit call to /oauth/token in my Python code. Is it the case that the auth0.authorize_access_token() line is making a call to /oauth/token behind the scenes?. This method is from authlib (as used in the Quickstart guide) an I’ve read through the authlib documentation but it isn’t clear to me, although as far as I can see that’s the only thing that could be resulting in the “Success Exchange” log entry. From the Auth0 documentation it looked like I should be able to tell authlib to give me back an idtoken as well as the authorization code but I don’t see anywhere to change the response_type to include id_token. I do really appreciate your help and patience with a newbie.
Hi. In the Auth0 Quickstart documentation (https://auth0.com/docs/quickstart/webapp/python#configure-flask-to-use-auth0) under “Add the callback handler”, it says “This handler exchanges the code that Auth0 sends to the callback URL for an Access Token and an ID Token” so I understand that to mean that in the callback handler, before calling auth0.authorize_access_token() I should be able to recover the ID token from the auth0 object. But using something like idtok=auth0.get(‘idtoken’) (or ‘id-token’) don’t give me an ID token value, so I’m at a loss how to get the ID token from the auth0 object…
Hi again,
The call to auth0.authorize_access_token() actually makes the HTTP request to exchange the authorization code for an access token and optional ID token. So in the code above, when you create a payload and call requests.post, you are reusing the authorization code (it was already exchanged in that authorize_access_token call), thus the 403 error.
I’d suggest running the code in a debugger so you can look at the results of the authorize_access_token call, and seeing this page for docs:
They are calling:
resp = auth0.get(‘userinfo’)
userinfo = resp.json()
This should give you the ID token. But use the debugger and poke around in the auth0 object and resp to see what is there.
John
Hi, thanks again for your patience in sticking with this.
Did I mention this was my first Python project? It turned out to be a very easy problem to solve, and now I understand why you didn’t know why I wasn’t seeing the idtoken … because I was looking in the wrong place! Based on the sample, I was expecting to need to either use a method, similar to auth0.authorize_access_token(), to ‘retrieve’ the idtoken, or a getter similar to auth0.get(‘userinfo’). Eventually the penny dropped and I grasped what you meant in your last response by “This should give you the ID token”: the token is (and always was) in the result of the authorize_access_token method! I just hadn’t seen the obvious fact that the Quickstart code just calls that method but doesn’t assign the result to a variable or do anything else with it. So, as is now obvious, all I needed to do was:
idtoken = auth0.authorize_access_token()[‘id_token’]
Embarrassing, but I’m pleased to be able to move on, and thanks for all your help…
I’m glad you got it working, and we ALL have first projects.
John