I enabled the Google Drive API in my personal GCP, set up my OAuth2 consent screen, and downloaded my credentials.json file. I'm now running this code from the Python quickstart in Colab:
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
else:
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
if __name__ == '__main__':
main()
When I execute the code, I am prompted to login via Google's OAuth sign-in page, to give my program access to my Google account. I click Allow, then immediately see this page:
I've redone this without a VPN or firewall with no luck, and I don't know if this is a problem with my OAuth or creds setup in the Project. Thoughts?
Solved by running in PyCharm. VSCode also works. For whatever reason, Colab continues to throw the error.
Provide a solution:
When run_local_server() opening browser, copy the url and use private browsing to login with this link.
It work for me.
Related
the code was working but I had to revoke the temporary token because my app was still in testing, but now it shows on console:
"Enter the authorization code:" But What is the authorization code??
CLIENT_SECRET_FILE = 'client_secret_xxxxxxx.json'
API_NAME = 'drive'
API_VERSION = 'v3'
SCOPES = ['https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.metadata',
'https://www.googleapis.com/auth/drive.photos.readonly',]
service = Create_Service(CLIENT_SECRET_FILE, API_NAME, API_VERSION, SCOPES)
The authorization code is the response from the authorization as part of the fist step in the authorization process.
When your application runs you should ask the user to navigate to a web site where they will be shown the consent screen.
When the user consents to the request for authorization. Then a new browser window will open and the authorization code will appear in the url of the browser.
This code is then used by your application to exchange for an access token which will give your application access to request data from the users account.
I think that you should have a look at the official google drive sample. python quickstart authorizaotn code you will find here will work better then what you have now.
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
return
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
except HttpError as error:
# TODO(developer) - Handle errors from drive API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
There is something I don't understand, when it comes to using the Google Drive API.
I'm trying to develop a desktop application that lets the user save his config file to his personal Google Drive, so he can use the same config from any computer.
The Python Quickstart guide has an example how to let a user authenticate, but this example requires the user to have the "credentials.json" file that I created in the Google Console. My understanding is that I should not share this file publicly.
So can I allow users to synchronize their configuration on multiple desktop computers without giving them the app's credentials?
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
return
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
except HttpError as error:
# TODO(developer) - Handle errors from drive API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
You should consult the TOS for using the Google apis
Asking developers to make reasonable efforts to keep their private keys private and not embed them in open source projects.
If you are giving your users the copy of your python code. You may not give your users your credetinals.json file. You must instead instruct your users on how to create their own credetinals.json file.
Solution
Solution to not sharing your credentials its to teach the users of your application to create their own credentials.
I have 1 idea. Use selenium and automate process of creating credentials.json. In this way non-technical users not need to do anything and with this automatic process credentials.json shall be created using users google account.
Apply this idea and you shall see good results.
I am trying to access my drive, and I have tried different options based on the answers here ...what should be correct URIs so that it doesn't mismatch?
Authorized Javascript is
http://localhost:8080
Redirect URI is
http://localhost:8080/
This is the credentials file
{"web":{"client_id":"1038794891433-f5vuivrc93fvqj9o65elka0shksbee5a.apps.googleusercontent.com","project_id":"bitcoin-961cf","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"","redirect_uris":["http://localhost:8080/"],"javascript_origins":["http://localhost:8080"]}}
The Error:
The redirect URI in the request, http://localhost:62605/, does not
match the ones authorized for the OAuth client.
I am using the standard documentation script
from __future__ import print_function
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
else:
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
if __name__ == '__main__':
main()
http://localhost:62605/
Add this in redirect URI in google console
but you have the credentials of web client and you are following documentation of desktop client
In my case, it was this line
creds = flow.run_local_server(port=0)
Need to change it to
creds = flow.run_local_server(port=8080)
I'm trying to get familiar with the google calendar api. In the getting started guide they have this code example:
from __future__ import print_function
import datetime
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
def main():
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
# Call the Calendar API
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
print('Getting the upcoming 10 events')
events_result = service.events().list(calendarId='primary', timeMin=now,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
if not events:
print('No upcoming events found.')
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
print(start, event['summary'])
if __name__ == '__main__':
main()
In this example we automatically open a window to ask the user to access their calendar, if we don't already have access through the pickle file. The thing is I don't want this window to open automatically, I want to print a link instead that the user can click to authenticate. I have looked around in the documentation but can't seem to find anything usefull. I would appriciate any help i could get, thanks!
For authorizing process, you want to show only the URL. You don't want to automatically open the browser.
You want to achieve this using googleapis with python.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
In this case, please use Flow.from_client_secrets_file instead of InstalledAppFlow.from_client_secrets_file.
Modified script:
When your script is modified, please modify as follows.
From:
from google_auth_oauthlib.flow import InstalledAppFlow
To:
from google_auth_oauthlib.flow import Flow
and
From:
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
To:
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
# Create the flow using the client secrets file from the Google API
# Console.
flow = Flow.from_client_secrets_file('client_secret.json', SCOPES, redirect_uri='urn:ietf:wg:oauth:2.0:oob')
# Tell the user to go to the authorization URL.
auth_url, _ = flow.authorization_url(prompt='consent')
print('Please go to this URL: {}'.format(auth_url))
# The user will get an authorization code. This code is used to get the
# access token.
code = input('Enter the authorization code: ')
flow.fetch_token(code=code)
creds = flow.credentials
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
In this case, when you run the script under token.pickle is not existing, the URL for the authorization is displayed to the console. The browser is not opened. So please access to the URL by opening the browser and authorize the scopes. Then, please the copied authorization code to the console and input enter key. By this, the access token is retrieved and the file of token.pickle is created.
Note:
If an error related to the redirect uri occurs, please modify it to http://localhost and test it again.
Reference:
google_auth_oauthlib.flow module
If I misunderstood your question and this was not the direction you want, I apologize.
Added:
From I want to print a link instead that the user can click to authenticate in your question, I proposed above sample script.
From some way not to manually confirm authorization codes in your replying, I think that that above sample script is not suitable.
In this case, how about using the service account? When the service account is used, no authorization code is required. The script for using the service account is as follows.
Sample script:
from google.oauth2 import service_account
from googleapiclient.discovery import build
SERVICE_ACCOUNT_FILE = 'service-account-credentials.json' # Here, please set the creadential file of the service account.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
service = build('calendar', 'v3', credentials=creds)
Note:
In order to access to the Google calendar using the service account, at first, please share the Google calendar with the email of the service account. Please be careful this.
Reference:
Creating a service account
Trying to get OAuth2 Google login working, this is the raw request that my app makes:
Method: POST
URL: https://www.googleapis.com/oauth2/v3/token
Headers: Content-Type: application/x-www-form-urlencoded
Values:
client_id: XXX-0123456789abcdef0123456789abcdef.apps.googleusercontent.com
client_secret: A1b2C3d4E5f6G7h8I9j0K1l2M
code: 1/A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v
grant_type: authorization_code
redirect_uri: http://localhost:5000/callback/google/
And this is the response:
Status: 401 Unauthorized
Body:
{
"error": "invalid_client",
"error_description": "Unauthorized"
}
Have verified that this is the exact request / response that my app is making (a Python app using Flask and rauth), and have verified that I can reproduce the exact same request / response using Postman.
Per instructions in other threads, I have done all of the following in the Google APIs console:
In "OAuth consent screen" settings, set "Product name" to something different than "Project name"
Also in "OAuth consent screen" settings, double-check that email is set
Enable the Google+ API
Enable the Gmail API
Recreate the client ID / secret
Double-check that there are no leading or trailing spaces in the client ID / secret values, I have copied them correctly from the API console
No matter what I do, still getting the same response of "invalid_client": "Unauthorized".
Help with this would be appreciated. Am trying to set up OAuth2-powered "Log in with X" functionality in my app, have gotten Facebook and Twitter working without issues, would like to get Google working too, but if I can't resolve this then I'm afraid I'll have to ditch Google auth.
Invalid client means that the client id or the client secret that you are using are not valid. They must be the ones you have downloaded from Google Developer console.
Tip: You might want to consider using the Google python client library it does all the heavy lifting for you.
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
else:
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
if __name__ == '__main__':
main()
Refresh Cliente Secret File on https://console.cloud.google.com/apis/credentials and delete token pickle folder