Sending a mail from Flask-Mail (SMTPSenderRefused 530) - python

The app configuration used in a Flask Mail application (following Miguel Grinberg Flask developlemt book) :
app.config['MAIL_SERVER'] = 'smtp.googlemail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
The Mail Username and Password variables have been set correctly and rechecked. While trying to send a message using the following code,
from flask.ext.mail import Message
from hello import mail
msg = Message('test subject', sender='same as MAIL_USERNAME', recipients=['check#mail.com'])
msg.body = 'text body'
msg.html = '<b>HTML</b> body'
with app.app_context():
mail.send(msg)
While sending, the application is again and again resulting in the following error:
SMTPSenderRefused: (530, '5.5.1 Authentication Required. Learn more at\n5.5.1 http://support.google.com/mail/bin/answer.py?answer=14257 qb10sm6828974pbb.9 - gsmtp', u'configured MAIL_USERNAME')
Any workaround for the error?

While digging into the issues faced, I rechecked the SMTP settings for Google,
Changing the
app.config['MAIL_SERVER'] = 'smtp.googlemail.com'
to
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
did the trick.
Also make sure that the full username is used as Gmail SMTP username, i.e., example#gmail.com as shown in the image above.
Hope this helps!!!

I also followed this book and get the same problem, after some digging here and there, I found out the root cause of the problem. However, I am not sure whether it will be the same case for you or not.
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
As you can see your flask app gets the your email credentials through os.environ.get(), and if you set this environment variables temporarily in your system, in my case Mac OSX, after your terminal session they will be gone, so you need to set them again for the next time you enter the terminal, like below:
export MAIL_USERNAME=**YOUR EMAIL**
export PASSWORD=**YOUR PASSWORD**
I got this error because of this scenario, in order to set them permanently you need to include these variables into .bash_profile file in your home directory.

You need to change your Google account settings. On this page, turn on the option to "Allow less secure apps".
As that page says:
Some apps and devices use less secure sign-in technology, which makes your account more vulnerable. You can turn off access for these apps, which we recommend, or turn on access if you want to use them despite the risks. Learn more

Do these 2 things to resolve:
Use this link and turn on 'Allow less secure apps'- https://myaccount.google.com/lesssecureapps
Use hard-coded value for email and password and it worked fine.
Simply in the file 'init.py', edit the following section:
Don't use os.environ.get
app.config['MAIL_USERNAME'] = 'youremail#gmail.com'
app.config['MAIL_PASSWORD'] = 'yourpassword'

Related

Get Error 530 5.7.0 Authentication Required when sending email using Django Python

I am trying to send emails with G-Suite account using python in Django. As Google stoped the less secure App option for the new applications, I have to use Oauth2.
But when I start to send emails via smtplib, the ERROR:
smtplib.SMTPSenderRefused: (530, b'5.7.0 Authentication Required. Learn more at\n5.7.0 https://support.google.com/mail/?p=WantAuthError q4sm8418287pfl.175 - gsmtp'
And after looking up the reference, it means "530, "5.7.0", Must issue a STARTTLS command first."
However, I have added "server.starttls()". Could someone help me? Many thanks.
server = smtplib.SMTP('smtp.gmail.com', port=587)
server.ehlo('test')
server.starttls()
server.docmd('AUTH', 'XOAUTH2 ' + base64.b64encode(auth_string.encode()).decode("utf-8"))
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()
Per this https://stackabuse.com/how-to-send-emails-with-gmail-using-python/#:~:text=As%20for%20the%20actual%20Python,com'%2C%20465)%20server., you probably have 2-step verification turned on.
To enable your web apps to send SMTP's from/to your gmail, you will need to create an app-specific password for less secure apps.
Go to your Google Account.
Select Security.
Under "Signing in to Google," select App Passwords. You may need to
sign in. If you don’t have this option, it might be because: 2-Step
Verification is not set up for your account. 2-Step Verification is
only set up for security keys. Your account is through work, school,
or other organization. You turned on Advanced Protection.
At the bottom, choose Select app and choose
the app you are using and then Select device and choose the device
you’re using and then Generate.
Take this 16-character code in the yellow bar and place it in your
environment variables as your "EMAIL_HOST_PASSWORD"
EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST_USER ='youremail#gmail.com'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_PASSWORD = 'yourpassword'
EMAIL_USE_TLS = True
Add this on your settings. After that :
python manage.py shell
You will enter to Python shell. Then:
from django.core.mail import send_mail
Write this command on shell and after that:
send_mail(
"django test mail",
"this is django test mail",
"your.email#gmail.com",
"recipient.email#example.com",
fail_silently=False
)
If output is equal to:
1
, then you send email through your Gmail.

Customize a sender in Flask-Mail?

Flask-Mail allows you to customize who sends the email, but when I try to do so, it just uses my login email that I used to authenticate to the server.
Example.
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = 't********3#gmail.com'
app.config['MAIL_PASSWORD'] = '********'
app.config['MAIL_DEFAULT_SENDER'] = None
app.config['DEBUG'] = True
mail = Mail(app)
#app.route('/')
def index():
msg = Message("Hello",
sender='johndoe#gmail.com',
recipients=["t*********3#gmail.com"])
mail.send(msg)
return 'message sent!'
Even though i put a sender, it still uses the ['MAIL_USERNAME'] email. Any input would be much appreciated. Thanks.
I believe this is something Google's SMTP does for you. I was able to reproduce the issue and got the same results as you: sender name was kept, but sender email was set to the google account email, no matter what the sender or default sender is.
However using a different SMTP (sendgrid) worked just fine and I can see my sender email displaying correctly.
I did not test it, but you can probably work around the issue with gmail by configuring 'Send As' in account settings and adding the email you want to be the sender here is how to do it. Or just use a different smtp.
Update: I did test it after all and it works if you add your new sender email in gmail account settings.

Gmail blocks login attempt from Python with app specific password

I'm trying to send an email from a Google account using Python's smtplib, but getting an error, and now I'm kind of at a loss. Google responds with the following: Please log in via your web browser and then try again. Learn more at https://support.google.com/mail/answer/78754.
The account has two factor authentication enabled, so I'm using an app specific password for my login. To my understanding, this should then work without enabling the setting for less secure apps, shouldn't it? I've been doing the same with another account while testing without a problem, but now I finally got the credentials for the proper account and there it won't accept the authentication.
I'm aware that there is a Python Gmail API thingy to use with OAuth, but if at all possible I don't want to include more packages and rewrite much, and I don't really want to enable the "less secure apps" setting either.
Is there a way to get this working without either?
If it makes a difference, here is the code I use for sending email. As said before, this was working fine with another account, so I'm not sure if it's actually relevant.
def send_mail(to_address, subject, body):
smtp_user = "myaccount#domain.com"
smtp_password = "MyAppPasswordFromGoogle"
server = "smtp.gmail.com"
port = 587
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
msg["From"] = smtp_user
msg["To"] = to_address
msg.attach(MIMEText(body, "html"))
s = smtplib.SMTP(server, port)
s.connect(server, port)
s.ehlo()
s.starttls()
s.ehlo()
s.login(smtp_user, smtp_password)
s.sendmail(smtp_user, to_address, msg.as_string())
s.quit()
Edit:
There is an interesting difference between the two accounts: on https://myaccount.google.com/lesssecureapps, my old (working) one says "this setting isn't available for accounts that have two factor authentication enabled", while the new one says "this setting is managed by your domain administrator", even though both use 2FA and it's also forced in both domains. So I suppose there is some setting that the domain admin has to change, but I don't know which one that would be.
I tried to replicate exactly your case (with an account that has a two factor authentication enabled). After creating my app password, I used it in the code.
Anyway, I think your problem is the following:
s = smtplib.SMTP(server, port)
s.connect(server, port)
You execute the connection twice.
Try with
s = smtplib.SMTP()
s.connect(server, port)
or just this
s = smtplib.SMTP(server, port)
The entire code:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
smtp_user = 'myUser#gmail.com'
smtp_password = 'my16charactersAppPassword'
server = 'smtp.gmail.com'
port = 587
msg = MIMEMultipart("alternative")
msg["Subject"] = 'Why,Oh why!'
msg["From"] = smtp_user
msg["To"] = "destinationUser#gmail.com"
msg.attach(MIMEText('\nsent via python', 'plain'))
s = smtplib.SMTP(server, port)
s.ehlo()
s.starttls()
s.login(smtp_user, smtp_password)
s.sendmail(smtp_user, "destinationUser#gmail.com", msg.as_string())
s.quit()
As of now, google will remove the "Allow unsecure apps options" as suggested by other answers till May 30, 2022. Now you would need to generate specific app passwords.
Go to Google Accounts Page
Turn on the 2-step verification.
Security > App passwords
Select "Other apps" from the app menu.
This gives you app password. You could use the same email and this password to login with your python script

Sending mail via smtp in Django

I'm trying to send a simple email using python/django shell and have been strugling with this problem for the last few hours:
in django shell i do the following:
from django.core.mail import send_mail
send_mail('django mail', 'this was sent with django', 'myaddress#gmail.com',['myaddress#gmail.com'], fail_silently=False)
the returned result should be 1, meaning that the mail is sent successfully, but insead, i get this:
raise SMTPAuthenticationError(code, resp)
smtplib.SMTPAuthenticationError: (534, b'5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbu9\n5.7.14 0cv5jjPsAITCLvsSIKoDuJcz5I18H7PMX8Nsxz2ajtgAJfxls4wIKIVMUENCrFmoXNHdgM\n5.7.14 NpSKlFYuaGHtwqDodV09jIf_GaDklCUUzJLY7oSJITQqXADDWxYRU7LUbVRFPxwpd2cKzl\n5.7.14 g70grCboTaCtEofq3-5edwoRC0ukZT-z97AgOelTTvSteaEjuf5n7F417VvFFE1hXcBnyg\n5.7.14 n2NWXBFMlV_74532aXU0vguceCC84> Please log in via your web browser and\n5.7.14 then try again.\n5.7.14 Learn more at\n5.7.14 https://support.google.com/mail/answer/78754 2sm2181268wrn.24 - gsmtp')
I read the url suggested in the error, and also a few similar questions on the web, and there were no good it them for me.
also, in the settings.py file, the required code is included:
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'me#gmail.com'
EMAIL_HOST_PASSWORD = 'mypassword'
EMAIL_PORT = 587
as i mentioned above, the other questions/problems on this site weren't the same. I've already enable the access for less secure apps on my gmail account, and also i have no two step verification.
IIRC to send email using gmail SMTP from third-party apps (your own Django app), you need to enable what they call Less Secure Apps.
You can read more here: https://support.google.com/accounts/answer/6010255

Why does Django's send_mail not work during testing?

I have an app that imports a number of user email addresses and creates accounts for them. To have them set their own password, I tried to use django's PasswordResetForm (in django.contrib.auth.forms). The password reset is called as soon as a user account has been created:
def reset_password(person):
form = PasswordResetForm({'email': person.email})
if form.is_valid():
form.save(from_email='myname#myserver.com')
I haven't gotten any further with testing than including a unit test that does this:
import password_reset_module
class TestPasswordReset(TestCase):
def setUp(self):
p = Person(email='test#test.com')
def test_send(self):
password_reset_module.reset_password(p)
No assertions, right now I just want to see if there is mail sent at all by monitoring the console in which I run:
python -m smtpd -n -c DebuggingServer localhost:1025
Saving the form calls django's send_mail. When running the testcase, the send_mail method returns 1. However, no mails show up in the console. The strange thing is that calling send_mail from django's interactive shell:
python manage.py shell
works fine. Mail shows up in the console. Clicking the forgot my password link in a browser also result in sent mails.
I have also tried the file based email backend to no avail.
Current settings.py email settings:
EMAIL_USE_TLS = False
EMAIL_HOST = 'localhost'
DEFAULT_FROM_EMAIL = 'myname#myserver.com'
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_PORT = 1025
Now wondering if I am missing something when calling the password reset, or is there a mailserver configuration issue at hands?
Your config is being overruled
In Section "Email Services", the
Django testing documentation says:
Django's test runner automatically redirects all
Django-sent email to a dummy outbox. [...]
During test running, each outgoing email is saved in
django.core.mail.outbox.
This is a simple list of all EmailMessage instances
that have been sent.
Huh?
The Django test runner will actually configure a different email backend for you (called locmem).
It is very convenient if you want to do unit-testing only
(without integration with an actual email server), but
very surprising if you don't know it.
(I am not using the Django test runner manage.py test,
but it happens anyway, presumably because I have
pytest-django installed which magically modifies my py.test.)
If you want to override the overriding and use the email configuration
given in your settings module, all you need to re-set is the
setting for the email backend, e.g. like this:
#django.test.utils.override_settings(
EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend')
def test_send_email_with_real_SMTP(self):
...
It is probably worthwhile trying to turn this into a proper unit test, which of course you can then run as part of your automated test suite. For unit testing, probably the easiest way to check whether the mail was sent (and verify the contents of the mail if required) is to use Django's built-in in memory email backend - you can simply use the outbox attribute on this to get a list of sent mails:
https://docs.djangoproject.com/en/dev/topics/email/#in-memory-backend
This has the advantage of not requiring any infrastructure setup to support testing email sending, makes it very simple to assert the contents of your email, and this should also make the tests fast (when compared to actually sending the emails to an SMTP server).
Hope this helps.
From what I understand, you are running the following command while testing the unit.
python -m smtpd -n -c DebuggingServer localhost:1025
This command starts up a "dumb" SMTP server that recieves your emails and displays them on the terminal. Try running your site without this DebuggingServer set up and see if the mails are sent.
Here is the reference to the docs page
You can use the mailoutbox fixture of pytest-django:
A clean email outbox to which Django-generated emails are sent.
Example
from django.core import mail
def test_mail(mailoutbox):
mail.send_mail('subject', 'body', 'from#example.com', ['to#example.com'])
assert len(mailoutbox) == 1
m = mailoutbox[0]
assert m.subject == 'subject'
assert m.body == 'body'
assert m.from_email == 'from#example.com'
assert list(m.to) == ['to#example.com']

Categories

Resources