I'm using code below to connect to gdrive service and get data from drive, as far as I am concerned with cliend_id and client_secret given I literally choose which drive I wanna connect to, however when authentication window pop-up there is i.e. Account A where my target drive is and Account B which i gave a share to Account A, if i choose Account A as authentication everything works well, but with Account B it's connecting to Account B's drive. My goal is to always connect to Account A's drive.
#via OAuth 2.0 token
def get_gdrive_service_v2(project_name):
status = ""
scopes = ['https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.metadata',
'https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/drive.photos.readonly',
'https://www.googleapis.com/auth/drive.readonly']
#insert credentials json file content here
creds_local = {}
if project_name == "stage":
creds_local = {"installed": {"client_id": "xxx",
"project_id": "xxx", "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": "{client_secret}".format(client_secret = os.environ.get("GDRIVE_STAGE_SECRET")), #not in code
"redirect_uris": ["urn:ietf:wg:oauth:2.0:oob", "http://localhost"]}}
flow = InstalledAppFlow.from_client_config(creds_local, scopes)
creds = flow.run_local_server(port=0)
status = "Everything good, google drive connected"
return build('drive', 'v3', credentials=creds), status
cliend_id and client_secret given I literally choose which drive I wanna connect to.
The client id and client secret identity your application to Google. That is all it has no relation to which account you connect to.
When you run your code the application will request authorization of a user. The user to access their data. A refresh token and an access token are returned.
Normally the developer would store this token token for later use. In the code below it is being stored in Token.json file. This code would only work for a single user as as long as the file exits it will be loaded from there.
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 accounts
I think you should consider looking into serice account authorization instead of installed application authorization.
If you create a service account and then share a directory on Account A the service account will have access to account A until you remove said access. Your code would then always be accessing that account.
scope = ['https://www.googleapis.com/auth/drive']
credentials = ServiceAccountCredentials.from_json_keyfile_name('creds.json' % path, scope)
service = build('drive', 'v3', credentials=credentials)
Should you be using a Google service account?
Related
I have a small python script that collects some data and sends it in an email using the gmail api. My goal is to put this script on my raspberry, create a daily cronjob that calls it and forget about it. However, the way I have done my google authentication prevents me from automating it. Currently I have to authenticate using my browser(user needs to press the allow button), then I can use the credentials for a few days and then they expire and user input is required again. How can I make it authenticate once and then start refreshing its credentials automatically?
Current code:
def get_creds():
creds = None
if os.path.exists(os.path.join(dir,'token.json')):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
print("refreshing")
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
os.path.join(dir,'credentials.json'), SCOPES)
creds = flow.run_local_server(port=0)
with open(os.path.join(dir,'token.json'), 'w') as token:
token.write(creds.to_json())
return creds
You can use a service account instead of token. It doesn't expire.
from google.oauth2 import service_account
from googleapiclient.discovery import build
credentials = service_account.Credentials.from_service_account_file(
CREDENTIALS_JSON_PATH,
SCOPES
)
service = build('gmail', 'v1', credentials=credentials)
print(service.users().getProfile(userId='me').execute())
If you create Google Workspace service account you need to specify user email address
credentials = credentials.with_subject(USER_EMAIL)
As Daniel mentioned the problem was that my GoogleCLoud app was in test mode. I had to publish it first and then it started refreshing the token successfully without my help.
avoid auth token to expire
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.
I'm currently working on an application requiring a gmail inbox. Currently i'm using the Gmail API with a credentials.json and a token.pickle and this is working fine although I want to login with other credentials each time the script is used. I'm quite new to this API and was wondering if it's possible to login with other credentials than the credentials used for the token.pickle.
Gmail API
To currently get a gmail inbox I use the following code:
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)
global service
service = build('gmail', 'v1', credentials=creds)
Imaplib
Another way I've tried to get a certain user's inbox is with the imaplib and email library since it lets you login with a certain email address and password. But to allow this to work a user needs to allow less secure apps to their google account which is a hassle for each user to enable that. The code I used for imaplib is the following code:
import imaplib, email
username = 'USERNAME'
password = 'PASSWORD'
imap_url = 'imap.gmail.com'
con = imaplib.IMAP4_SSL(imap_url)
con.login(username, password)
con.select('Inbox')
Question
My question is: Is it possible with the gmail api from google to login with multiple/other credentials to get the corresponding gmail inbox? If this is not possible and I have to use the Imaplib way, what are the steps I need to take so it doesn't require users to enable less secure apps?
Just change the .pickle file name. This code possibilite do this just changing the user variable. There will be different tokens to each account
def api_connection(user):
scopes = ['https://www.googleapis.com/auth/gmail.readonly']
creds = None
if os.path.exists(f'{user}.pickle'):
with open(f'{user}.pickle', 'rb') as token:
creds = pickle.load(token)
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)
with open(f'{user}.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
return service
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
I have headless software in Jetson Xavier. I am using Gmail API for sending mail. My code is working correctly in Laptop however GUI service is disabled in Jetson, browser cannot be opened. So, Authentication is failed when I try to use Gmail API in Jetson.
I have copied authenticated "token.pickle" to Jetson from laptop. It works fine that for a short time. Then, it wants an authentication again.
How can I solve this issue? How to block the browser to be opened for authentication in Jetson?
Thanks.
This part of code is for authentication:
class EmailSender:
def __init__(self):
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.compose']
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:
creds_dir = os.path.dirname(os.path.abspath(__file__)) + '/credentials.json'
flow = InstalledAppFlow.from_client_secrets_file(
creds_dir, 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)
self.service = build('gmail', 'v1', credentials=creds)
Unfortunately, gmail(and most modern auth service) will not allow you to login automatically directly for safety reason. In order to ensure that a third party service not to steal your account, google will ask you as a human to type in your account and password and google will return a cookie, or token, to the service you try to login.
However, since you are working on your own project and I assume you know the program will not put your account at risk, you can use selenium to simulate a browser and type your account automatically. You may refer to this post to learn how to do it.