I'm trying to build a Google Classroom extension that gives the user control over when to receive "Work Due Soon" notifications. However, when the token refreshes, I get this error: "raise exceptions.RefreshError(google.auth.exceptions.RefreshError: Not all requested scopes were granted by the authorization server, missing scopes https://www.googleapis.com/auth/classroom.coursework.me.readonly. "
The code being used is straight from the google authorization page for google classroom
SCOPES = ['https://www.googleapis.com/auth/classroom.courses.readonly', 'https://www.googleapis.com/auth/classroom.coursework.me.readonly']
def main():
"""Shows basic usage of the Classroom API.
Prints the names of the first 10 courses 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())
As you can see, the scope in the error, is already in my list of scopes for the project. The only way I've found to get around this is to delete the token file, and sign-in every time the token expires. I've checked the classroom api documentation and stack overflow but I couldn't find a solution. Any help will be appreciated.
Related
i am accessing user data by connecting to bigquery via python with OAuth 2.0 method. But every time I make a query, it repeatedly asks for browser confirmation. How can I cancel this verification? When I set the launch_browser value to False, it requests confirmation from the terminal. Is there a different method I can use? I am using the same sample code found in the document https://cloud.google.com/bigquery/docs/authentication/end-user-installed
from google_auth_oauthlib import flow
from google.cloud import bigquery
launch_browser = True
appflow = flow.InstalledAppFlow.from_client_secrets_file(
"client_secrets.json", scopes=["https://www.googleapis.com/auth/bigquery"]
)
if launch_browser:
appflow.run_local_server()
else:
appflow.run_console()
credentials = appflow.credentials
project = 'user-project-id'
client = bigquery.Client(project=project, credentials=credentials)
query_string = """SELECT name, SUM(number) as total
FROM `bigquery-public-data.usa_names.usa_1910_current`
WHERE name = 'William'
GROUP BY name;
"""
query_job = client.query(query_string)
for row in query_job.result():
print("{}: {}".format(row["name"], row["total"]))
Storing the credentials in a file stops google from constantly requesting it, it only lasts for a little while though.
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_console()
#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())
This is what I use to load credentials (I do mine through console as it is on Linux with no browser)
You may have to change some parts to fit your code but this is the general idea.
i'm building python GMAIL API for checking emails for 10 email accounts
but in google documentation not very useful.
this seems only support one account
https://github.com/suleenwong/Gmail-API-Python
If we check the default sample python quickstart This sample was designed to be single user but that can be changed.
The following section creates a token.json file when the user authorizes the application. The file will contain the access token and refresh token for the user who authorized the code.
If the file does not exist then the app will prompt the user to authorize it. If it does then the app will load the credentials from that file and run the code with the authorization of that user.
# 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())
To add more users you can simply rename that file tokenUserOne.json, tokenUserTwo.json. Then set it up so that you can supply the file name you want your script to run on. You will only need to authorize each user once. As long as you have a token.json file for each user separated, your app can then be started using which ever token file you want, to access each users data.
https://developers.google.com/gmail/api/quickstart/python -this is the proper documentation everything you need is in here
aka (you also need a client_secet.json file but you will have to get yourself for the google cloud dashbord):
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/gmail.readonly']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
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:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
return
print('Labels:')
for label in labels:
print(label['name'])
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
I want to run this code https://developers.google.com/forms/api/quickstart/python that makes requests to google forms.
My question is how to get the credentials generate token.json file
from __future__ import print_function
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools
SCOPES = "https://www.googleapis.com/auth/forms.body"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"
store = file.Storage('token.json')
creds = None
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES)
creds = tools.run_flow(flow, store)
form_service = discovery.build('forms', 'v1', http=creds.authorize(
Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False)
The part most relevant to your question is here:
store = file.Storage('token.json')
creds = None
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES)
creds = tools.run_flow(flow, store)
Trying to put it simply:
The store variable defines a local file where the token will be located, in this case it's token.json.
Then the flow object tries to look for a file called client_secrets.json where your credentials should be located. There's an explanation on how to generate these credentials from the Cloud Console here. Once you have this file you can just place it in the same folder as your quickstart.py. Make sure that the name in the method matches the name in the directory.
Then tools.run_flow(flow, store) will use the credentials in the flow object (which are in your client_secrets.json file) to run a temporary webserver and open a page in your browser where you have to go through the OAuth flow to authorize the app. Once this flow is done, the resulting token will be written to the token.json file which you defined in the store variable. The creds variable can then be used to run API calls.
For some reason the Forms quickstart you linked has a few differences from others as pointed out by furas. The difference in the Drive API sample is that it checks for the token.json file first so if it exists it won't go through the OAuth flow. On the other hand the Forms quickstart seems like it would go through the flow every time without checking, and it also uses a different method to run the flow. This is the part in the authorization that changes:
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())
I don't know if there's a technical reason for this discrepancy in the documentation or if it's just an oversight/outdated way to do it, but you can just replace the top code block with the one at the bottom and it should work either way, but the second is preferable. Just keep in mind that you will need the credentials.json file in the same location as the script file.
Sources:
Create authorization credentials
oauth2client.tools module
Flow module
I have a problem with my google drive API.
I use this code to connect to my google account and get service :
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
def getService():
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive']
"""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(
'code_secret_client_XXX.apps.googleusercontent.com.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)
return service
It works perfectly, but when I call 2 times for example :
result1 = GoogleDrive.service.files().list(
pageSize=1000, fields="nextPageToken, files(id, name)").execute()
result2 = GoogleDrive.service.about().get(
fields="storageQuota").execute()
I have this error :
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:2633)
According to the documentation the Drive API is built on top of Httplib2 which is not thread safe.
I use oauth2client which is deprecated, could that be the problem?
If I add a time.sleep(1) between my requests, it works.
if I remove one of the two requests, it works...
I don't understand how I can achieve that..
Thanks a lot
I think I found a solution :
def getCredentials():
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive']
"""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(
'code_secret_client_XXX.apps.googleusercontent.com.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())
return creds
def getService(creds):
service = build('drive', 'v3', credentials=creds)
return service
service = getService(credentials)
and :
http = google_auth_httplib2.AuthorizedHttp(credentials=GoogleDrive.credentials, http=httplib2.Http ())
result = GoogleDrive.service.about().get(
fields="storageQuota").execute(http=http)
I tried to use the API on python in a autorun on a PC. But I can't because every time the program starts, it asks me about the authorization code.
This is my code:
client_secret_file = "client_secret.json"
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
client_secrets_file, scopes)
credentials = flow.run_console()
youtube = googleapiclient.discovery.build(
api_service_name, api_version, credentials=credentials)
Any help? Thanks
Becouse you are using the YouTube API you can not use a service account you will need to use Oauth2.
Oauth2 can return something called a refresh token, if you store this token your code can then access the refresh token the next time it runs and use the refresh token to request a new access token this way it will not need to ask you every time it runs to access your data.
# 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)
The only offical sample that includes refresh token that i know if is here Python quickstart you will need to alter it for YouTube but the auth code is really all you need just plug that int your code and you should be GTG
You can achieve this using Service Account.
https://googleapis.dev/python/google-api-core/latest/auth.html#service-accounts
Service account are technically pre authorized by the developer.
IF the API does not support Service account( in some cases) then you can try oauth2 where you have a consent for asking the owner of the account if you can access