Excellent for email verification in user authentication

Send emails with Flask + MailerToGo

Rinaldo Nani
Variance Digital
Published in
7 min readOct 13, 2022

--

Easy setup of a mailer service on Heroku with Flask

Who doesn’t need a fast and easy way to send confirmation emails for users’ sign-up/login? We do, so we decided to set up an HTTP service based on Flask, capable of sending transaction emails on demand.

Feel free to download the fully functioning mailer code from this public GitHub repo.

Section 5 briefly describes the installation steps to make our code run locally on your machine.

To programmatically send emails to our recipients, we use MailerToGo, a service that makes it easy to send emails using code. MailerToGo is an add-on available on Heroku: so our Variance Mailer Flask application is deployed on the Heroku platform (FYI, Variance is the name of our company).

Note: MailerToGo is NOT sponsoring us by any means. We only use and appreciate their services.

1. The confirmation email and the “confirmation” link

The structure of a confirmation email is straightforward; you have probably received this kind of mail many times. Apart from some blah-blah, the mail usually displays a “click here to confirm your email” link; this link is the fundamental part of the email content.

The “confirmation” HTML link points to the website signed up by the user in the first place; furthermore, the link has a parameter that carries the information needed to recognize the user (as the site somehow needs to save the user confirmation). Figure 1 shows a typical “confirmation” link setup.

Fig. 1 —The “confirmation” link in the confirmation email

One way to pack the needed user info in the link’s parameter (red in the above picture) is to encode the data in a JWT token: the token will represent the complete information payload as a random-looking string. [For a short intro about JWT tokens, see section 6 below].

2. Pass the user’s info to the Mailer in a secure way

So: whenever one of our apps or websites needs to send a confirmation email via the Variance Mailer, it does two things:

  1. pack all relevant user data (essential for sending the confirmation e-mail) in an ad-hoc JWT token;
  2. call the remote Variance Mailer Flask service using an HTTP GET.

For example, our demo CatLoader” Flask application (you can play with it here) sends confirmation emails via the Variance Mailer. It uses a) the PyJwt package to pack up all user’s info in a token and b) the requests package to send this info to the Mailer.
Here is the code snippet.

The requests.get() command in the last line makes an HTTP GET request: it’s like pasting the complete URL (along with its parameter) in the browser’s search bar.

The encoded_payload is the JWT token containing all necessary information the Mailer uses (including the URL needed to craft the confirmation link, sent with the email!), while os.environ["MAILER_URL"] is the mailer service URL. Note that this “wrapping” JWT is encoded using a dedicated secret (os.environ["WT_MAILER_SECRET"]).

But wait! The funny thing about this new “wrapping” token is that part of its payload is already a JWT token! The “wrapped-up” token is, in fact, the parameter of the confirmation link that we saw in Figure 1. The flow may seem convoluted, but it… flows: go on reading to get the full explanation.

3. How the Variance Mailer works

As they say, a diagram is worth a thousand words. Figure 2 shows the most common use case for our Variance Mailer, flowing in the yellow task pool.

Fig.2 — Task flow involving the Variance Mailer to send the confirmation email.

Let’s follow the flow. When a user signs up to the caller app (or website), the app carries out the following tasks:

  • data validation,
  • record creation for the subscribing user,
  • token encoding to create the “confirmation” link parameter;
  • creation of another token, the “wrapping” token, containing all that is needed to craft the confirmation email [including the previous token].

Finally, it makes a GET call to the Mailer, passing the encoded “wrapping” token containing all the relevant information needed to craft the email.

Now comes the exciting part, the yellow section in Figure 2. The Variance Mailer receives the “packed” token; then:

  1. it decodes the token and unpacks the information contained in it;
  2. with the obtained info, it sets up the email to be sent;
  3. it then sends the email via MailerToGo;
  4. if everything is ok, it responds to the caller app.

Here is the code snippet performing the four steps seen above.

@bp.route('/<incoming_token>')
def emailservice(incoming_token):
error =0
msg = "All ok"

user_email, user_aut_key, email_link_url, email_link_token =
get_data_from_token(incoming_token)
if len(user_email)>0:
error, msg = send_email(user_email,
user_aut_key,email_link_url, email_link_token)
else:
error = 1
msg = "Problem with token"
return { "error": error, "msg":msg }

Please look at the Python file bl_emails.py of our Mailer project for the implementation of the functions called in the code snippet, i.e. get_data_from_token() and send_email().

Important clue: the secret string used to encode all the needed info into the JWT token that is then passed to the Mailer must be the exact secret string used by the Mailer to decode the token — and unwrap all its contents.

The final part of the flow is prominent: the user receives the confirmation email and eventually clicks on the confirmation link contained in the email. By the effect of this action, the caller app receives the original JWT “link” token: the one holding the primary user’s info.

Our CatLoader Flask demo app implements this last step in this way:

@bp.route('/emailconfirmationhtml/<email_link_token>')
def emailconfirmationhtml(email_link_token):
error, aut_id = db_check_email_link_token(email_link_token,
os.environ["JWT_SECRET_HTML"])
if error==0:
db_set_user_confirmed(aut_id)
flash('Your email is confirmed, have fun')
else:
flash('Problems with your confirmation email')
return redirect(url_for('bl_backoffice.listuploads'))

The code snippet shows that the email_link_token, once it arrives at the Flask app, is checked by the db_check_email_link_token() function. If everything is ok, the user’s database entry is updated to record her confirmation.

And that’s it!

4. Heroku deploy details

If you want to deploy [a modified version of] our Mailer on Heroku, you may follow the instructions described in the article below.

To get your Mailer operational, you must provision the MailerToGo add-on for your app. Go to your Heroku app section, click the Resources tab and follow the instructions to add MailerToGo to your app.

This will add a set of environment variables containing your credentials, which are used in the Mailer’s code: to see them go to “Settings” and press the “Reveal Config Vars” button.

5. Not so fast: MailerToGo takes its time…

There are a couple of things that you should know about MailerToGo before using this online service.

  • You must be able to configure your domain’s DNS settings, adding a bunch of given CNAME records. In this way, MailerToGo ensures that you are the owner of the domain used to send emails.
  • A MailerToGo team member will kindly ask you in which way you are planning to use their service. This prevents malicious companies from using MailerToGo for spam campaigns.
  • There is a trial period in which only a limited number of emails can be sent. MailerToGo will unlock the total number of mailings at your disposal after a few weeks.

6. Q&A for TL;DR :)

> Where can I get the complete working code of the Flask Variance Mailer?

  • Please feel free to follow me on Medium and then fetch the code in this public GitHub repo.

> Is there an example app using this Flask mailer — so it’s clear how to make calls?

  • Our Flask mailer is used in different demo sites. To see it working in a simple yet complete Flask application that implements user subscription patterns, go to our “Minimal+User” demo site. You can download the complete code of the “Minimal+User” project in this GitHub Repo. Please read the related article, specifically section 5.2.

> Ok, but what is a JWT token?

  • The JWT (Jason Web Token) technology lets one take any data payload and encode it into an encrypted string token. The encoding process requires a secret to craft the token; the same secret will then be used to decode the token — you’ll get back the original data payload. For Python, you can find further info here.

> Why do your Flask projects have that peculiar structure?

  • All our projects stem from our “Minimal” Flask template projects, which are described here:

Don't hesitate to contact us for any issues regarding the Variance Mailer: we are eager to keep improving this living document.

--

--

Rinaldo Nani
Variance Digital

Algorithmist ▪ Software Engineer ▪ Project manager. I love maths and music + solving hard problems.