kootenpv / yagmail

Send email in Python conveniently for gmail using yagmail
MIT License
2.66k stars 265 forks source link

Do I have to manually regenerate the OAuth2 credentials file every few days with GMail? #244

Closed mircealungu closed 2 years ago

mircealungu commented 2 years ago

I've started using yagmail a while ago, and after being very happy at the beginning I discovered something that seems wrong.

After a few days of sending emails, Gmail starts to refuse to send them with the following as part of the stack trace:

eturn get_oauth_string(user, oauth2_info)                            
File "/usr/local/lib/p│ython3.9/site-packages/yagmail/oauth2.py", line 96, in get_oauth_string
  access_token, expires_in = refresh_authorization(**oauth2_info)
File "/usr/local/lib/python3.9/site-packages/yagmail/oauth2.py", line 91, in refresh_authorization
  response = call_refresh_token(google_client_id, google_client_secret, google_refresh_token)
File "/usr/local/lib/python3.9/site-packages/yagmail/oauth2.py", line 71, in call_refresh_token
 response = urlopen(request_url, encoded_params).read().decode('UTF-8')
File "/usr/local/lib/python3.9/urllib/request.py", line 214, in urlopen 
  return opener.open(url, data, timeout)                                
File "/usr/local/lib/python3.9/urllib/request.py", line 523, in open    
  response = meth(req, response)                                        
File "/usr/local/lib/python3.9/urllib/request.py", line 632, in http_response
  response = self.parent.error(
File "/usr/local/lib/python3.9/urllib/request.py", line 561, in error   
  return self._call_chain(*args)
File "/usr/local/lib/python3.9/urllib/request.py", line 494, in _call_chain
  result = func(*args)│Ju
  File "/usr/local/lib/python3.9/urllib/request.py", line 641, in http_error_default
 raise HTTPError(req.full_url, code, msg, hdrs, fp)                    
urllib.error.HTTPError: HTTP Error 400: Bad Request                       

I've solved it once in the past by regenerating the credentials.json locally on my machine and uploading to the server. But now i stopped working again and I'm wondering: am I doing something that's obviously wrong?

The code that I use is straightforward:

    def send_with_yagmail(self):
        yag = yagmail.SMTP(self.our_email, oauth2_file="/<path_to>/credentials.json")
        yag.send(self.to_email, self.message_subject, contents=self.message_body)

and the contents of the credentials.json file also seem standard:

{
  "email_address": "my.team@gmail.com",
  "google_client_id": "74<blabla>0e1feov.apps.googleusercontent.com",
  "google_client_secret": "GOCSP<blabla<kOz",
  "google_refresh_token": "1//09<blabla>-2_dxZH8"
}

Can anybody recommend something that can be done? Thanks!

kootenpv commented 2 years ago

better to just use an application specific password

mircealungu commented 2 years ago

Thanks for the answer @kootenpv! I started using yagmail for the OAuth2 flow. As far as I can tell Gmail discourages the use of application passwords and they want to move everybody to OAuth.

How does the refresh token work? Do you get a new one from Gmail and write it to the credentials file? I was wondering whether maybe in my deployment the file ends up somehow not writeable...

Cheers, M.

kootenpv commented 2 years ago

I'm not so familiar with oauth anymore.

Actually I think app specific password is fine, don't have to worry about it

mircealungu commented 2 years ago

From an email I received from Google that made me switch away from app passwords:

On May 30, you may lose access to apps that are using less secure sign-in technology.

To help keep your account secure, Google will no longer support the use of third-party apps or devices which ask you to sign in to your Google Account using only your username and password. Instead, you’ll need to sign in using Sign in with Google or other more secure technologies, like OAuth 2.0.

mircealungu commented 2 years ago

Or do you mean that app passwords are still a "more secure technology" and can still be used? I've mistaken OAuth for the only way when in fact it was only one example? Argh! Will try!

kootenpv commented 2 years ago

App passwords still work, I like it more

mircealungu commented 2 years ago

The bastards! You know how much time i wasted with OAuth 2!

seperman commented 2 years ago

Yeah seriously. It is so annoying that OAuth2 creds expire so often.

kootenpv commented 2 years ago

Would you guys suggest I just remove OAuth2 support in yagmail and make people go for app specific password?

Or did you find yagmail specifically by e.g. "gmail oauth2"?

seperman commented 2 years ago

I specifically found it for OAuth2 once the support for "less secure login" method was dropped by Gmail. I didn't even imagine they support "app passwords" when the "less secure login" is dropped. Is there a way to make the OAuth2 tokens live longer? Like authorize them for a year? I wouldn't drop the support for oauth.

kootenpv commented 2 years ago

Is there a way to make the OAuth2 tokens live longer? Like authorize them for a year? I wouldn't drop the support for oauth.

I have no idea

I didn't even imagine they support "app passwords" when the "less secure login" is dropped.

yea it confuses me too.... but I can tell you I'm very satisfied with app specific password and wouldn't consider OAuth2 ever again

GAEfan commented 2 years ago

Same. Don't know if I'm grandfathered in, or if it will shut down in the future, but for now, app-specific password works great!

1) Top-right on a Google user page, like Gmail > Manage your Google account > Security 2) Under "Signing in to Google", turn on 2-step verification 3) Also under "Signing in to Google": App passwords > generate a new one for Yagmail. You'll use this to auth below. (or go to https://myaccount.google.com/apppasswords)

yag = yagmail.SMTP(sender_email, 'your_app_specific_password', soft_email_validation=False)

twomed commented 2 years ago

Would you guys suggest I just remove OAuth2 support in yagmail and make people go for app specific password?

Or did you find yagmail specifically by e.g. "gmail oauth2"?

Please do not remove oauth2 I cannot use app password as I use this to send emails from a managed email.

YYLIZH commented 2 years ago

https://developers.google.com/identity/protocols/oauth2#expiration Here is the refresh token policy of google api. Need to manually register an app and publish it. Therefore the refresh token you get will be a long lived refresh token.

kootenpv commented 2 years ago

Please do not remove oauth2 I cannot use app password as I use this to send emails from a managed email.

mb I mostly meant remove it from README (as a recommended way), wouldn't remove the support completely, but just prefer people to use app-specific pws

Leo-vF commented 2 years ago

I, too, have the same problem. I will probably be switching to app specific Passwords, but I do miss the security of OAuth. After searching online, though, I found that google provided their own python package which does apparently support refreshing access tokes. It might be worth a look. If this were implemented into yagmail to automatically refresh the access token, I would be very happy.

seperman commented 2 years ago

@Leo-vF Which Google package are you referring to?

mircealungu commented 2 years ago

Maybe a note in the Readme that app-specific passwords are still possible, and people should not use OAuth would help. I know that I started using OAuth because I thought app-specific were considered not strong enough!