0
votes

I would like to use Google API to send emails. I copied the code from

https://developers.google.com/gmail/api/quickstart/

https://developers.google.com/gmail/api/guides/sending

However, I can only read the labels of my email, but I am not allowed to send emails. Here is the error it returned:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "required",
    "message": "Login Required",
    "locationType": "header",
    "location": "Authorization"
   }
  ],
  "code": 401,
  "message": "Login Required"
 }
}

This is my code

from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from google.auth.transport.requests import Request

SCOPES = [
    'https://www.googleapis.com/auth/userinfo.email',
    'https://www.googleapis.com/auth/userinfo.profile',
    # Add other requested scopes.
    'https://www.googleapis.com/auth/gmail.send'
]

def create_message(sender, to, subject, message_text):
    message = MIMEText(message_text)
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject
    return {'raw': message.as_string()}

def send_message(service, user_id, message):
    """
    Sends an email message.
    Arguments:
    service: an authorized Gmail API service instance.
    user_id: User's email address. To indicate the authenticated user, the special value "me" can be used.
    message: Message to be sent.
    """
    message = (service.users().messages().send(userId=user_id, body=message)
               .execute())
    print('Message Id: %s' % message['id'])
    return message

def main():
    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('gmail', 'v1', credentials=creds)
    # Call the Gmail API
    user_id = "me"
    results = service.users().labels().list(userId=user_id).execute()
    labels = results.get('labels', [])
    print("labels: ", labels)
    message = create_message(user_id, "[email protected]", "credit", "API test only!")
    message = send_message(service, user_id, message)
    print(message)

if __name__ == '__main__':
    main()
1
Unfortunately, in my environment, although create_message() is required to be modified, the error of Login Required cannot be replicated at send_message using your script. So can you confirm whether the script in your question is the same with one you tested?Tanaike
Are you sure you're using the correct credentials.json?Iamblichus
@iamblichus Yes, because I can access the labels of my email.Augustin Pan
@Tanaike I just complemented my code, and it should now reproduce the error.Augustin Pan
May I ask what is the purpose of these scopes?: 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'. Shouldn't these be enough for the calls you're doing?: 'https://www.googleapis.com/auth/gmail.send', 'https://www.googleapis.com/auth/gmail.labels'. If I use these two scopes, it works correctly.Iamblichus

1 Answers

1
votes

Did you remove the “token.pickle” file after adding the line in SCOPES (l9) to get the send authorization? You have to redo the authorization procedure, else you still have read access only.