When I try to send an email using Flask-Mail to Gmail's SMTP server using the settings below, I get [Errno -2] Name or service not known. How do I fix my configuration to send email with Gmail?
from flask import Flask, render_template, redirect, url_for
from flask_mail import Mail, Message
app = Flask(__name__)
app.config.update(
MAIL_SERVER='smtp#gmail.com',
MAIL_PORT=587,
MAIL_USE_SSL=True,
MAIL_USERNAME = 'ri******a#gmail.com',
MAIL_PASSWORD = 'Ma*****fe'
)
mail = Mail(app)
#app.route('/send-mail/')
def send_mail():
msg = mail.send_message(
'Send Mail tutorial!',
sender='ri******a#gmail.com',
recipients=['ri*********07#msn.com'],
body="Congratulations you've succeeded!"
)
return 'Mail sent'
The server is "smtp.gmail.com".
The port must match the type of security used.
If using STARTTLS with MAIL_USE_TLS = True, then use MAIL_PORT = 587.
If using SSL/TLS directly with MAIL_USE_SSL = True, then use MAIL_PORT = 465.
Enable either STARTTLS or SSL/TLS, not both.
Depending on your Google account's security settings, you may need to generate and use an app password rather than the account password. This may also require enabling 2-step verification. You should probably set this up anyway.
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USERNAME = 'username#gmail.com'
MAIL_PASSWORD = 'app password generated in step 3'
A small but important addition to davidism's answer:
You must have '2-step verification' enabled on your Google account before you're able to set up app-specific passwords.
Related
When I try to send an email using Flask-Mail to Gmail's SMTP server using the settings below, I get [Errno -2] Name or service not known. How do I fix my configuration to send email with Gmail?
from flask import Flask, render_template, redirect, url_for
from flask_mail import Mail, Message
app = Flask(__name__)
app.config.update(
MAIL_SERVER='smtp#gmail.com',
MAIL_PORT=587,
MAIL_USE_SSL=True,
MAIL_USERNAME = 'ri******a#gmail.com',
MAIL_PASSWORD = 'Ma*****fe'
)
mail = Mail(app)
#app.route('/send-mail/')
def send_mail():
msg = mail.send_message(
'Send Mail tutorial!',
sender='ri******a#gmail.com',
recipients=['ri*********07#msn.com'],
body="Congratulations you've succeeded!"
)
return 'Mail sent'
The server is "smtp.gmail.com".
The port must match the type of security used.
If using STARTTLS with MAIL_USE_TLS = True, then use MAIL_PORT = 587.
If using SSL/TLS directly with MAIL_USE_SSL = True, then use MAIL_PORT = 465.
Enable either STARTTLS or SSL/TLS, not both.
Depending on your Google account's security settings, you may need to generate and use an app password rather than the account password. This may also require enabling 2-step verification. You should probably set this up anyway.
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USERNAME = 'username#gmail.com'
MAIL_PASSWORD = 'app password generated in step 3'
A small but important addition to davidism's answer:
You must have '2-step verification' enabled on your Google account before you're able to set up app-specific passwords.
I'm developing a Flask application that implements a user registration system. The application uses Flask-Mail and itsdangerous to confirm a user's registration and reset their password via email. I configured Flask-Mail to use the recommended server settings provided by the email host that I'm using.
MAIL_PORT = 587
MAIL_USE_SSL = False
MAIL_USE_TLS = True
At first, things were working fine; I could submit emails without any issues. However, seemingly without changing any configuration settings, I now receive the following error when attempting to submit an email using Flask-Mail:
[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)
I'm not sure where the problem is, and I am wondering if something has changed on the email provider's end? I have tried setting MAIL_PORT = 25 with MAIL_USE_SSL=False and MAIL_USE_TLS=False; and, MAIL_PORT = 465 with MAIL_USE_SSL=True and MAIL_USE_TLS=False as well. Using the former, I receive the same error as with port 587, but using the latter I'm receiving STARTTLS extension not supported by server.
I'm running the Flask app in development mode at localhost:5000. Here's some of my configuration settings and code:
config.py
SECRET_KEY = 'verysecret'
MAIL_SERVER = "smtp.mymailservice.com"
MAIL_PORT = 587
MAIL_USE_SSL = False
MAIL_USE_TLS = True
MAIL_USERNAME = "myemail#myhostname.com"
MAIL_PASSWORD = "mypassword"
MAIL_DEFAULT_SENDER = 'Brand <noreply#myhostname.com>'
app/mailing.py
from flask_mail import Message
from flask import current_app
from .extensions import mail
def send_email(to, subject, template):
msg = Message(
subject,
recipients=[to],
html=template,
sender=current_app.config["MAIL_DEFAULT_SENDER"]
)
mail.send(msg)
app/users/routes.py
(One of the routes where I receive the error)
from flask import (
render_template, session, request, redirect, url_for, g, jsonify, flash
)
import uuid
from passlib.hash import sha256_crypt
from app.mailing import send_email
from app.extensions import db
from app.users import bp
from app.users.forms import *
from app.users.models import *
from app.users.token import *
#bp.route('/register', methods=['POST', 'GET'])
def register():
# Initialize the Register Form
form = RegisterForm()
# If the submitted form is valid
if form.validate_on_submit():
# Check to see if a user already exists with this email address
user = User.query.filter_by(email=form.email.data).first()
# If there is not a user with this email address, create a new user
if not user:
new_user = User(public_id=str(uuid.uuid4()),
email=form.email.data,
password=sha256_crypt.encrypt(
(form.password.data)),
first_name=form.firstname.data,
last_name=form.lastname.data
)
db.session.add(new_user)
db.session.commit()
token = generate_confirmation_token(new_user.email)
confirm_url = url_for("users.confirm_email",
token=token, _external=True)
html = render_template('confirm_email.html',
confirm_url=confirm_url)
subject = "Please confirm your email"
try:
send_email(new_user.email, subject, html)
flash("A confirmation email has been sent to you. Please verify your email address to activate your account.", category="success")
except Exception as e:
flash(
"There was a problem sending the confirmation email. Please try again later.", category="danger")
print(e)
session["user_id"] = new_user.public_id
session["email"] = new_user.email
session["name"] = new_user.first_name
flash("Thanks for registering!", category="success")
return redirect(url_for('users.unconfirmed'))
else:
flash("There is already an account associated with this email address. Log in, or use a different email address.")
return render_template("register_user.html", form=form)
app/extensions.py
from flask_mail import Mail
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
bootstrap = Bootstrap()
mail = Mail()
app/init.py
from flask import Flask
from config import Config, DevelopmentConfig
from .errors import (
page_not_found, forbidden, internal_server_error
)
from .extensions import (
db, mail, bootstrap
)
def create_app(config_class=DevelopmentConfig):
app = MyFlask(__name__)
# Set Configuration
app.config.from_object(config_class)
# Register extensions
# Initialize Boostrap-Flask
bootstrap.init_app(app)
# Initialize Flask-SQLAlchemy
db.init_app(app)
# Initialize Flask-Mail
mail.init_app(app)
# Register error views
app.register_error_handler(404, page_not_found)
app.register_error_handler(403, forbidden)
app.register_error_handler(500, internal_server_error)
with app.app_context():
# register blueprints
from app.main import bp as bp_main
app.register_blueprint(bp_main)
from app.users import bp as bp_users
app.register_blueprint(bp_users)
return app
This answer was almost there but not quite for me. Even more TL:DR.
Put this in your config.py file and forget about the rest...
class Config:
MAIL_USE_TLS = True
MAIL_USE_SSL = False
More details...
You probably have a config.py file that looks like this:
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = 'sqlite:///site.db' # os.environ.get('DATABASE_URI')
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_PORT = os.environ.get('MAIL_PORT')
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS')
MAIL_USE_SSL = os.environ.get('MAIL_USE_SSL')
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
MAIL_DEFAULT_SENDER = os.environ.get('MAIL_DEFAULT_SENDER')
And then you think you can have something like this in your VSCode debug config or the environment of your server:
"env": {
"MAIL_USE_SSL":"true",
"MAIL_USE_TLS":"true",
Well that doesn't work because of the answer from #serrobit because "true" in VSCode turns into a str instead of a Python True.
So back to the start, hard code TLS to True and SSL to False in the config.py file and go spend time on something useful.
I figured out what was going on. Evidently, you can pass in non-boolean types for MAIL_USE_TLS and MAIL_USE_SSL when initializing the Mail object from Flask-Mail. This becomes a problem when the Connection object calls configure_host() and conditionally checks if self.mail.use_ssl.
Thus, as long as self.mail.use_ssl is not None, the method will set host = smtplib.SMTP_SSL(self.mail.server, self.mail.port), which in my case, lead to [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123) because mail.port was set to 587.
tl;dr
Ensure the configuration variables for your Flask app are set to the appropriate type, especially if you are using environment variables, as those will always be of type str when accessing them via the os.environ dict.
flask_mail.py
class Connection(object):
"""Handles connection to host."""
def __init__(self, mail):
self.mail = mail
def __enter__(self):
if self.mail.suppress:
self.host = None
else:
self.host = self.configure_host()
self.num_emails = 0
return self
def __exit__(self, exc_type, exc_value, tb):
if self.host:
self.host.quit()
def configure_host(self):
## PROBLEM OCCURRED HERE BECAUSE type(self.mail.use_ssl) = <class 'str'> ##
if self.mail.use_ssl:
host = smtplib.SMTP_SSL(self.mail.server, self.mail.port)
else:
host = smtplib.SMTP(self.mail.server, self.mail.port)
host.set_debuglevel(int(self.mail.debug))
if self.mail.use_tls:
host.starttls()
if self.mail.username and self.mail.password:
host.login(self.mail.username, self.mail.password)
return host
Change this in your config.py:
class Config:
MAIL_USE_TLS = bool(strtobool(os.environ.get('MAIL_USE_TLS', 'False')))
MAIL_USE_SSL = bool(strtobool(os.environ.get('MAIL_USE_SSL', 'False')))
I have tried what I can think of but can't get the email to be sent from my application using flask-mail with Zoho mail.
I've tried setting up an app password and I have tried the following examples of configuration using some of the information from their site:
https://www.zoho.com/mail/help/pop-access.html
app.config['MAIL_SERVER'] = 'smtp.zoho.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = '_#whatever.com'
app.config['MAIL_PASSWORD'] = 'XXXXXXXXXXXX'
app.config['MAIL_SERVER'] = 'smtp.zoho.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = '__#whatever.com'
app.config['MAIL_PASSWORD'] = 'XXXXXXXXXXX'
I would expect to be able to send an email using flask-mail with my custom domain which is setup with zoho.
Check which host of Zoho you are registered with, if you are based in Europe you will have to edit the MAIL_SEVER parameter to:
app.config['MAIL_SERVER'] = 'smtp.zoho.eu'
This should solve the SMTPAuthenticationError.
Similar question link
For secure access head on to general setting on Zoho mail(https://mail.zoho.com/zm/#settings/general)
My account>Security Questions
Application-Specific Passwords > Generate new password
name your app. It'll give you a password
export MAIL_PASSWORD=<generated-password>
then in config.py
import os
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
And when you use TLS Zoho on time of writing this uses the port 587\
Difficult to phrase this question and what I've done as I don't know where the error is occuring.
I'm using Django hosted on AWS Elastic Beanstalk and SES to send emails. (Only for password reset) However the password reset emails don't seem to be sending.
When I try to send an email as below however it works.
send_mail('Test', 'Email content', 'email#email.com',['email#email.com',])
Also locally the password reset email sends (its put in spam but that's a different issue)
My email settings are:
EMAIL_HOST = 'smtp.gmail.com' # mail service smtp
EMAIL_HOST_USER = 'email#email.com' # email id
EMAIL_HOST_PASSWORD = '****' #password
EMAIL_PORT = 587
# EMAIL_USE_TLS = True
EMAIL_BACKEND = 'django_ses.SESBackend'
AWS_ACCESS_KEY_ID = 'ACCESSKEY'
AWS_SECRET_ACCESS_KEY = 'SECRETKEY'
AWS_SES_REGION_NAME = 'eu-west-1'
AWS_SES_REGION_ENDPOINT = 'email.eu-west-1.amazonaws.com'
DEFAULT_FROM_EMAIL= 'email#email.com'
If it's relevant, anywhere I've written email#email.com is actually the same email.
Any help would be much appreciated.
Thanks
I am using flask mail to send messages from app. I am doing something like this:
app = Flask(__name__)
app.config.update(
MAIL_SERVER='smtp.gmail.com',
MAIL_PORT=465,
MAIL_USE_SSL=True,
MAIL_USERNAME = 'your#gmail.com',
MAIL_PASSWORD = 'yourpassword'
)
mail = Mail(app)
But I don't want my password be visible in my code. I want to encrypt it, for example. I can store in database but I still doesn't get how to keep it encrypted because I need to decrypt it to set MAIL_PASSWORD property.
Your app must be able to access your cleartext password, so encrypting it is useless in the scenario where the server gets compromised.
Encryption may be useful (but I don't suggest it) if you want to save it on a public place (e.g. code on github) and then you give the key only to the application (for example with a non tracked file or in a environment variable).
Since you must give your application some secret, I suggest you to simply give the email password as a secret.
For example using a file called local_settings.py you can do this:
# local_settings.py
MAIL_PASSWORD = 'mypassword'
# app.py
...
import local_settings
app = Flask(__name__)
app.config.update(
MAIL_SERVER='smtp.gmail.com',
MAIL_PORT=465,
MAIL_USE_SSL=True,
MAIL_USERNAME = 'your#gmail.com',
MAIL_PASSWORD = local_settings.MAIL_PASSWORD
)
mail = Mail(app)
Using environment variable is a good option (for mail password, database password, ...). Example:
import os
app = Flask(__name__)
app.config.update(
MAIL_SERVER='smtp.gmail.com',
MAIL_PORT=465,
MAIL_USE_SSL=True,
MAIL_USERNAME = 'your#gmail.com',
MAIL_PASSWORD = os.environ['MAIL_PASSWORD']
)
mail = Mail(app)
You can set value for MAIL_PASSWORD environment variable every time run script, using
In Linux: export MAIL_PASSWORD="{your_mail_password}"
In Windows: set MAIL_PASSWORD="{your_mail_password}"
Shouldn't use plain text in file to store password