How to send verification emails from Flask App?

Deepak Radhakrishnan
()

Today, we will walk you through sending verification mail from the flask app. As we know, sending account activation mail, password reset mail, verification mail, etc are indispensable to every modern web application. So without further ado, let’s get started!

Contents

Overview

Once a new user gets registered on a web application, it is crucial to confirm that the provided email address is valid and the user has access to it.

Installation

I hope you have an up and running flask application. If you are a beginner feel free to refer my Flask Tutorial for setting up a flask application.

Configuration

app.config['MAIL_SERVER']='smtp.gmail.com' #change this to your mail server Name/IP
app.config['MAIL_PORT'] = 465. #change to your mail server port
app.config['MAIL_USERNAME'] = 'yourmail@mail.com'
app.config['MAIL_PASSWORD'] = 'your_password'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
mail = Mail(app)

For Gmail Users

If you are using Gmail, the built-in security features in the Gmail service may block this login attempt. You may have to decrease the security level. Please log in to your Gmail account and visit this link to decrease the security.

Basic Code

from flask import Flask
from flask_mail import Mail, Message

app =Flask(__name__)
app.config['MAIL_SERVER']='smtp.gmail.com' #change this to your mail server Name/IP
app.config['MAIL_PORT'] = 465. #change to your mail server port
app.config['MAIL_USERNAME'] = 'yourmail@mail.com'
app.config['MAIL_PASSWORD'] = 'your_password'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
mail = Mail(app)


@app.route("/")
def index():
   msg = Message('This Mail sent From Flask', sender = 'yourId@gmail.com', recipients = ['id1@gmail.com'])
   msg.body = "Hello World. This mail sent from Flask Application."
   mail.send(msg)
   return "Mail Sent Successfully"

if __name__ == '__main__':
   app.run(debug = True)

HTML Formatting

You may have noticed some emails in your inbox with lots of styling. By adding your html tags to “msg.html“, HTML Formatted Emails can be created.

msg.html='<html><title></title><body><h1>This is a Test Mail</h1></html>'

It can be formatted using any online mail html formatter editor. You can also make an HTML template inside the template folder and load it using render_template. And I assure you, this is the most professional way of doing it.

Email Verification

Email verification is simple with Flask. You can make an extra boolean column to your User database named Is_verified and set is as False as default. Also, we need a string column to save your token. Then you can send an email to the User’s email address with a link that is redirecting to your Flask Application. Make sure to send some token for additional security.

Simple method to generate token

There are many ways to generate tokens. Here we are using UUID to generate unique numbers which we are using here as tokens. You have to install UUID using pip, if the module does not exist.

import uuid 

def pwtoken():
    order_no=str(uuid.uuid4().int>>64)[0:16]
    return order_no

Send verification Mail

For security purposes, I will be sending User Id, Token, and timestamp as the verification link. The timestamp is simply added so that even if someone manages to get that code, they will be confused with the parameters. Gotcha!


@app.route("/send_verifymail",methods=['POST'])
def verify():
        if request.method == 'POST':
        data=request.get_json(force=True, silent=True)
        if data:
            email=data['email']
            getuser=User.query.filter_by(Email=email).first()
            if getuser:
                token=pwtoken()
                uid=getuser.Cust_id
                clock=int(time.time())
                msg = Message('Verification Mail', sender ='yourmail@mail.com', recipients = [email])
                msg.body = 'Verify Account'
                msg.html='<html><head><title></title></head><body><h1>Activate Your Account<p>Just click on the link below. It's that easy!</p><a href="http://192.168.225.23:5000/verify?tk={0}&id={1}&status={2}" target="_blank">Verify</a></body></html>' .format(token,uid,clock)
#you can replace the href="" to your link for verification
                mail.send(msg)
                getuser.token=token #add token to the field of user table for verification
                db.session.commit()
                return "Verification Mail Sent"
            else:
                return {"message":"User with email address not registered.Kindly Check the email address entered","success":"false"}

Now we can create an endpoint to accept the verification link. Remember you have to use GET method to accept that email link.

@app.route("/verify",methods=['GET'])
def verify():
    if request.method == 'GET':
        uid=request.args.get('id')
        token=request.args.get('tk')
        check_token=User.query.filter_by(User_Id=uid).first()
        if check_token.token==token:

            check_token. Is_verified=True
            db.session.commit()
            return "Account Verified"
        else:

            return "Failed to verify Account" 

Password Reset Mail

How to send a password reset mail ? The answer is simple . The easiest way to do it is sending an email with a link that redirect to a URL of your application with an authentication token.

You can make an extra boolean column to your User database named is_ResetRequested with a default value of False.

Now add a route to send an email with UID and Token for receiving it.

@app.route("/resetpw",methods=['GET','POST'])
def resetpw():
    if request.method == 'POST':
        data=request.get_json(force=True, silent=True)
        if data:
            email=data['email']
            getuser=User.query.filter_by(Email=email).first()
            if getuser:
                token=pwtoken()
                uid=getuser.User_id
                clock=int(time.time())
                msg = Message('Password Reset', sender ='yourmail@gmail.com', recipients = [email])
                msg.body = 'Reset Password'
                msg.html='<html><head><title></title></head><body aria-readonly="false" autocomplete="off"><h1><span style="color:#0000CD"><strong>Forgot your password?</strong></span></h1><hr/> <div> </div><div>No worries. It happens.<br/>Just click on the link below. It's that easy!</div><div> </div><div><a href="http://192.168.225.23:5000/resetpw?tk={0}&id={1}&status={2}" target="_blank">Reset Password</a><hr/><hr/></div></body></html>' .format(token,uid,clock)
                mail.send(msg)
                getuser.token=token
                getuser.is_ResetRequested=True
                db.session.commit()
                return "Sent"
            else:
                return {"message":"User with email address not registered.Kindly Check the email address entered","success":"false"}
    if request.method=='GET':
        uid=request.args.get('id')
        token=request.args.get('tk')
        checkusr= User.query.filter_by(User_id=uid).first()
        if checkusr:
            if checkusr.is_ResetRequested:
                tk=checkusr.token
                if tk==token:
                    return render_template('resetpassword.html',uid=uid))
                else:
                    return "Invalid token"
            else:
                return "It seems like you have not requested for a reset password"
        else:
            return "User Not Found"
    else:
        return "Requested Operation not allowed"

Once the token matching is done, we have to display an HTML form to templates folder for entering a new password and to submit it.

<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Reset Password</title>

        <link rel="stylesheet" href="{{url_for('static' , filename='pwreset.css') }}">
        <link href='https://fonts.googleapis.com/css?family=Nunito:400,300' rel='stylesheet' type='text/css'>
        <link rel="stylesheet" href="css/main.css">
    </head>
    <body>
      <div class="row">
    <div class="col-md-12">
      <form action="{{url_for('reset')}}" method = "POST">
        <h1> Reset Password </h1>
        
        <fieldset>
          <input type="hidden" name="uid" value={{uid}}>
          <label for="password">Password:</label>
          <input type="password" id="password"       name="password">
          <label for="password">Confirm Password:</label>
          <input type="password" id="cnfpassword"       name="cnfpassword">

          
         </fieldset>
       
        <button type="submit">Reset Password</button>
        
       </form>
        </div>
      </div>
      
    </body>
</html>

You could add some validation, for checking if both passwords are the same and for entering blank passwords, weak password, etc. Now add required CSS for your resetpassword.html file. You have to create a folder named static and paste your CSS file into that.

*, *:before, *:after {
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
  }
  
  body {
    font-family: 'Nunito', sans-serif;
    color: #384047;
  }
  
  form {
    max-width: 300px;
    margin: 10px auto;
    padding: 10px 20px;
    background: #f4f7f8;
    border-radius: 8px;
  }
  
  h1 {
    margin: 0 0 30px 0;
    text-align: center;
  }
  
  input[type="text"],
  input[type="password"],
  input[type="date"],
  input[type="datetime"],
  input[type="email"],
  input[type="number"],
  input[type="search"],
  input[type="tel"],
  input[type="time"],
  input[type="url"],
  textarea,
  select {
    background: rgba(255,255,255,0.1);
    border: none;
    font-size: 16px;
    height: auto;
    margin: 0;
    outline: 0;
    padding: 15px;
    width: 100%;
    background-color: #e8eeef;
    color: #8a97a0;
    box-shadow: 0 1px 0 rgba(0,0,0,0.03) inset;
    margin-bottom: 30px;
  }
  
  input[type="radio"],
  input[type="checkbox"] {
    margin: 0 4px 8px 0;
  }
  
  select {
    padding: 6px;
    height: 32px;
    border-radius: 2px;
  }
  
  button {
    padding: 19px 39px 18px 39px;
    color: #FFF;
    background-color: #4bc970;
    font-size: 18px;
    text-align: center;
    font-style: normal;
    border-radius: 5px;
    width: 100%;
    border: 1px solid #3ac162;
    border-width: 1px 1px 3px;
    box-shadow: 0 -1px 0 rgba(255,255,255,0.1) inset;
    margin-bottom: 10px;
  }
  
  fieldset {
    margin-bottom: 30px;
    border: none;
  }
  
  legend {
    font-size: 1.4em;
    margin-bottom: 10px;
  }
  
  label {
    display: block;
    margin-bottom: 8px;
  }
  
  label.light {
    font-weight: 300;
    display: inline;
  }
  
  .number {
    background-color: #5fcf80;
    color: #fff;
    height: 30px;
    width: 30px;
    display: inline-block;
    font-size: 0.8em;
    margin-right: 4px;
    line-height: 30px;
    text-align: center;
    text-shadow: 0 1px 0 rgba(255,255,255,0.2);
    border-radius: 100%;
  }
  
  @media screen and (min-width: 480px) {
  
    form {
      max-width: 480px;
    }
  
  }
  

Now we have to create an endpoint to accept this form submission and saving the new password.

@app.route('/reset', methods=['GET','POST'])
def reset():
    if request.method == 'POST':
        uid = request.form['uid']
        usr=User.query.filter_by(User_id=uid).first()
        if usr:

            newpass= encrypt_password(request.form['password'])
            usr.Password=newpass
            db.session.commit()
            return "password saved successfully"
        else:
          return "No user Found"
     else:
        return "requested operation is not allowed"

Conclusion

So, this blog post looks at how to send verification emails from Flask App. As for most web applications, Once a user gets registered, the next important thing to get done is confirming that the provided email address is totally valid and that the user has complete access over it. This will aid in providing an auxiliary layer of security for your application, plus helps in averting spammers from the creation of fake accounts.

How useful was this post?

Click on a starts to rate it!

Average rating / 5. Vote count:

No votes so far! Be the first to rate this post.

As you found this post useful...

Follow me on social media!

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

%d bloggers like this: