Reputation: 199
I am following this example in an attempt to authenticate my users with the Google API.
Here is the relevant code I am using, almost exactly as in the example:
@app.route('/login/')
def login():
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
'client_secret.json',
scopes=['https://www.googleapis.com/auth/drive.file'])
flow.redirect_uri = 'https://localhost:5000/oauth2callback/'
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true')
session['state'] = state
return redirect(authorization_url)
#called by Google's oauth
@app.route('/oauth2callback/')
def oauth2callback():
state = request.args['state']
# Use the client_secret.json file to identify the application requesting
# authorization. The client ID (from that file) and access scopes are required.
state = session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
'client_secret.json',
scopes=['https://www.googleapis.com/auth/drive.file'],
state=state)
flow.redirect_uri = url_for('oauth2callback', _external=True)
authorization_response = request.url
flow.fetch_token(authorization_response=authorization_response, code_verifier=False)
When I do this, it gives the error,
oauthlib.oauth2.rfc6749.errors.InvalidGrantError: (invalid_grant) Missing code verifier.
I do not know what a code verifier is, nor did the examples mention one.
The stack trace is available if anyone thinks it will help
How can I solve this issue? Any help is appreciated!
Upvotes: 7
Views: 5328
Reputation: 5996
Based on s3rvac's answer, I dug a bit into the source code and found that the code_verifier
property can in fact be auto generated in the authorization_url()
method! So no need to generate one yourself.
First when you first construct the Flow object, you must enable autogenerate_code_verifier
:
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
'client_secret.json',
scopes=['https://www.googleapis.com/auth/drive.file'],
autogenerate_code_verifier=True # <-- ADD THIS HERE
)
Then in your login()
method, after the call to flow.authorization_url()
, store flow.code_verifier
in the session:
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true') # code_verifier is set in this method
session['code_verifier'] = flow.code_verifier # Get and store the code
session['state'] = state
return redirect(authorization_url)
Then, in the callback method, just load it back:
flow = google_auth_oauthlib.flow.Flow(
oauth2_session,
client_type='web',
client_config=client_config,
redirect_uri='https://localhost:5000/oauth2callback/',
code_verifier=session.get('code_verifier') # Load the code. Don't set autogenerate_code_verifier here, otherwise code_verifier would be overwritten.
)
I use Flow.from_client_config()
to construct a Flow
but I just add this line and it works fine:
flow.code_verifier = session.get('code_verifier')
Upvotes: 5
Reputation: 10329
This seems to be a bug in the 0.4.0 version of google-auth-oauthlib
(see this upstream issue; note that it has been reported after this SO question was posted).
You have the following options:
pip install --upgrade google-auth-oauthlib==0.3.0
google_auth_oauthlib.flow.Flow()
, which should be a random string of 43-128 characters used to verify the key exchange using PKCE:
oauth2_session, client_config = google_auth_oauthlib.helpers.session_from_client_secrets_file(
'client_secret.json',
scopes=['https://www.googleapis.com/auth/drive.file']),
)
flow = google_auth_oauthlib.flow.Flow(
oauth2_session,
client_type='web',
client_config=client_config,
redirect_uri='https://localhost:5000/oauth2callback/',
code_verifier='<random string>'
)
Note: The code is for your login()
function. You will have to slightly adjust it to work in your oauth2callback()
function.Upvotes: 8