To prevent bot signups and reduce spam, you can require that users click a link in an email they receive when they register to your web apps. Flask-Security-Too can take care of this with some configuration.

This is article 2 of 3 in this series covering Flask-Security-Too:

  1. User authentication in Flask with Flask-Security-Too
  2. User email confirmations with Flask-Security-Too (this article)
  3. Customising templates and emails of Flask-Security-Too

Let's get started!

The tricky bit about user confirmation is the email delivery. To actually send emails and have them reach the user's inbox (and not end up in spam), you need a few things: a domain name, an email delivery service, and proper configuration. We talk about this in depth in our REST APIs with Flask and Python course, and you can read that section of the course e-book here.

I like the email delivery service Mailgun. Create an account with them, and you'll be given a sandbox domain. You can use this domain to send emails to "authorised recipients". You can add yourself as an authorised recipient, so you can test out the email confirmation functionality of Flask-Security-Too. A step-by-step guide on how to do this is here. Read the "Setting up for Mailgun" section before continuing!

In the future, if you want to purchase a domain, you can use it to send emails with Mailgun (though that isn't free).

Now that you've got your Mailgun account, let's get the SMTP details. You can do this by clicking the "Select" button under "SMTP":

Screenshot showing the Mailgun sandbox domain page with an arrow labeled "1" pointing at the "Select" button in the "SMTP" section, and another arrow labeled "2" pointing to the SMTP credentials

Let's add these to our .env file:

DATABASE_URL="sqlite:///data.db"
MAIL_SERVER=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=

Now we can tell Flask-Security-Too to use email confirmation with one configuration setting:

app.config["SECURITY_CONFIRMABLE"] = True

Doing this makes it so user accounts aren't valid until they are marked as confirmed (this is a column in our UserModel class, which you can see in models/auth.py). User accounts will be confirmed when the user clicks a link in an email they receive after registration.

Next, we'll configure the Flask-Mailman library, which Flask-Security-Too uses to send emails. In app.py:

# At top of file
from flask_mailman import Mail

# After 'Create app'
app.config["MAIL_SERVER"] = os.getenv("MAIL_SERVER")
app.config["MAIL_PORT"] = os.getenv("MAIL_PORT")
app.config["MAIL_USE_SSL"] = False
app.config["MAIL_USE_TLS"] = True
app.config["MAIL_USERNAME"] = os.getenv("MAIL_USERNAME")
app.config["MAIL_PASSWORD"] = os.getenv("MAIL_PASSWORD")
mail = Mail(app)

Note that the MAIL_USE_SSL and MAIL_USE_TLS values will depend on which provider you use. Mailgun uses TLS, so that's why we set SSL to False and TLS to True.

Now, when you register an account, you'll get a confirmation email with a link you must click. If you don't click the link, the account will not count as valid.

We can test this out by creating a protected endpoint (one that requires login). We can then register but not click the email link, and see if we can access it.

To create a protected endpoint, we use the @login_required decorator:

# At top of file
from flask_security import login_required

# At bottom of file
@app.route("/protected")
@login_required
def protected():
    return "You're logged in!"

Now let's try it out. Delete your data.db file and re-create it using flask shell:

>>> from app import app, db
>>> with app.app_context():
	 	db.create_all()

Then start the app with flask run and fill in your registration form. It's interesting to note that when we enable email confirmation, Flask-Security-Too automatically disables password confirmation in the signup form. Now there's just one password field instead of two:

Screenshot showing the registration page with two fields, "email address" and "password". Below it, a menu with "login", "register", and "confirm account" links.

Fill this in, making sure to use the email address that you used in your "authorised recipients" for Mailgun.

Then you should get an email (which might be in your Spam folder since we're using the Mailgun sandbox domain):

Screenshot showing the confirmation email received, with a link to confirm your account.

Don't click the link yet. Instead, navigate to the /protected endpoint with your browser: http://127.0.0.1:5000/protected. You should get redirected to the login page and you should see an error:

Screenshot showing the login page of our application with two bullet points at the top

These errors use Flask's flashed messages, and you can see there are two messages:

  • Thank you. Confirmation instructions have been sent to (my email).
  • Please log in to access this page.

They both look like errors, but the first one is just a message that Flask-Security-Too flashes when registration is successful. Flashed messages must be displayed once, and since there was no place for it to be shown until now, it stuck around. Ideally, you would use message flashing in the page that you redirect the user to after registration to show this message.

The second message is the error we were expecting!

Now click the confirmation link in your email, and try again. It should work!

Screenshot showing the contents of the protected endpoint, "You're logged in!"

This is how you can use Flask-Security-Too to easily add use registration and confirmation to your Flask applications. Naturally, you'd want to style the login, register, and confirmation pages to match the rest of your application. We'll look at that in part 3 of this series.

Thank you for reading! If you'd like to learn more about web development using Flask, consider enrolling in our Web Developer Bootcamp with Flask and Python! It's a complete video course that covers building multiple web apps and deploying them, all using Flask.