Back to Blog Posts

Sending emails from a Flask application via the SendGrid API

Blog

Saturday, 24 August 2019

Sites have all sorts of reasons for sending emails to their users; for account verification, marketing updates and the reason behind this post - password resets. Until now this site had no means of sending an email to a user, if they happened to forget their password then they were locked out for good.

This post will explain how the site has been developed to support sending password reset emails to users. Seeing as the aim of this site is to blog about the ongoing development of it - the code in the examples below reference some existing code. This code is not explained in detail if it is not relevant to the topic of the post.

I'm using SendGrid to get started with sending emails, the free tier supports 100 emails a day which is more than enough and the integration is straightforward thanks to their web API and the python-sendgrid module.

Below I've imported the modules required to send an email. I've also imported values from a configuration file, this includes a list of email addresses to use as the sender, along with the API key that was generated when signing up to the SendGrid service.


import sendgrid
from app import app, sendgrid_apikey
from sendgrid.helpers.mail import *
from flask import render_template

When sending a request to the SendGrid API, the request must be JSON formatted following a structure SendGrid refers to as Personalizations. This is an array of objects containing email metadata such as the subject, content, sender, etc - more details of this can be found in their documentation. Their module contains functions that will construct the personalizations by just providing the values for the various email fields. It will also post the request along with the required headers to the API.

The email_password_reset function will be called whenever a /reset_password_request is submitted. This function is passed all the user attributes associated with the email address provided by the user. The relevant attributes in this case are their username, email address and a method to generate a unique short-lived token to include as part of their password reset link.

The token is generated and stored in token and a basic password reset subject line is stored in subject. The from_email and to_email are arrays that contain the relevant email addresses which will be validated and JSON formatted by the Email() function. The content variable is also an array that contains the actual body of the email and its associated MIME type, this array will be JSON formatted by the Content() function. These variables are then passed as arguments to the Mail() function to create a SendGrid Personalization object.

SendGrid maintain another module named python-http-client designed to simplify API interactions. This module is used by the SendGridAPIClient to send the mail request to the SendGrid API containing the Personalization object. By logging the response it is possible to confirm if the request was successful as response will contain the status code, headers and body of the response.


sg = sendgrid.SendGridAPIClient(apikey=sendgrid_apikey)

def email_password_reset(user):

    token = user.generate_password_reset_token()
    subject = "Reset Your Password"
    from_email = Email(app.config['ADMINS'][0])
    to_email = Email([user.email][0])
    html_body = render_template('email/reset_password.html', user=user, token=token)

    content = Content("text/html", html_body)
    mail = Mail(from_email, subject, to_email, content)
    response = sg.client.mail.send.post(request_body=mail.get())

    app.logger.info("Password Reset Requested by: " + user.email)
    app.logger.info("Response Status: " + str(response.status_code))
    app.logger.info(response.headers)

Flask uses Jinja2 as a template engine, the render_template method is used by specifying a file and passing any variables to be written into the file. The values of these variables will be substituted into the template variables defined in {{ curly braces }} within said file.

The use of render_template in this case specifies a file named reset_password.html which contains a basic, generic body of a password reset email. The user attributes and generated token are passed to the template where the values will be substituted into the {{ user.username }} variable and the token will be inserted into the reset_password URL for the user.


<p>Dear {{ user.username }},</p>
<p>
    To reset your password
    <a href="{{ url_for('reset_password', token=token, _external=True) }}">
        click here
    </a>.
</p>
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
<p>{{ url_for('reset_password', token=token, _external=True) }}</p>
<p>If you have not requested a password reset then please ignore this message.</p>
<p>Regards,</p>
<p>admin@olirowan.xyz</p>


Leave a Comment

Comments (0)