Creating a Simple User Authentication System Using Flask (Python) and MongoDB
Welcome to this tutorial where I will walk you through building a basic user authentication system with Flask, MongoDB, and email-based OTP verification. This project is perfect for beginners who want to learn backend development. The code we’re using is written by me, Visahl Samson, and I’ll explain every important part in a clear way. Let’s get started!
Prerequisites
Before starting, make sure you have:
Basic Python knowledge.
Flask installed.
MongoDB database ready to use.
SMTP credentials for sending emails.
Python libraries like
bcrypt
,pymongo
, andflask
installed.
You can install the required libraries using this command:
pip install Flask Flask-PyMongo python-dotenv bcrypt
Step 1: Setting Up the Flask App
First, import the necessary libraries and initialize the Flask application:
from flask import Flask, request, redirect, url_for, flash, render_template, session
from flask_pymongo import PyMongo
import bcrypt
import random
from datetime import datetime, timedelta
from dotenv import load_dotenv
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
# Load environment variables
load_dotenv()
# Initialize Flask app
app = Flask(__name__)
app.config[‘SECRET_KEY’] = os.getenv(‘SECRET_KEY’)
app.config[‘MONGO_URI’] = os.getenv(‘MONGO_URI’)
app.config[‘MAIL_SERVER’] = os.getenv(‘SMTP_SERVER’)
app.config[‘MAIL_PORT’] = int(os.getenv(‘SMTP_PORT’))
app.config[‘MAIL_USERNAME’] = os.getenv(‘SMTP_USERNAME’)
app.config[‘MAIL_PASSWORD’] = os.getenv(‘SMTP_PASSWORD’)
app.config[‘SENDER_EMAIL’] = os.getenv(‘EMAIL_FROM’)
# Initialize MongoDB connection
mongo = PyMongo(app)
Step 2: Creating the Signup Route
The /signup
route collects user details like username, email, mobile number, and password. It performs basic validation before storing data in MongoDB. If the user provides valid details, an OTP is generated and sent to their email.
@app.route(‘/signup’, methods=[‘GET’, ‘POST’])
def signup():
if request.method == ‘POST’:
username = request.form.get(‘username’)
email = request.form.get(’email’)
mobile = request.form.get(‘mobile’)
password = request.form.get(‘password’)
# Validation
if not username or len(username) < 3 or len(username) > 15 or ‘ ‘ in username or not username.isalnum() or username.isdigit():
flash(‘Invalid username.’, ‘danger’)
return redirect(url_for(‘signup’))
if not email or ‘@’ not in email or ‘.’ not in email.split(‘@’)[-1]:
flash(‘Invalid email address.’, ‘danger’)
return redirect(url_for(‘signup’))
if not mobile or not mobile.isdigit() or len(mobile) < 6 or len(mobile) > 15:
flash(‘Invalid mobile number.’, ‘danger’)
return redirect(url_for(‘signup’))
if len(password) < 8 or not any(char.isdigit() for char in password) or not any(char.isupper() for char in password):
flash(‘Invalid password.’, ‘danger’)
return redirect(url_for(‘signup’))
# Check if user already exists
if mongo.db.user.find_one({’email’: email}):
flash(‘Email is already registered.’, ‘danger’)
return redirect(url_for(‘signup’))
# Generate and store OTP
otp = str(random.randint(100000, 999999))
expiration_time = datetime.utcnow() + timedelta(minutes=5)
mongo.db.otps.insert_one({’email’: email, ‘otp’: otp, ‘expires_at’: expiration_time})
send_otp_email(email, otp)
session[’email’] = email
session[‘username’] = username
session[‘password’] = password
return render_template(‘verify_otp.html’)
return render_template(‘signup.html’)
Step 3: Sending OTP via Email
The send_otp_email
function sends the OTP to the user’s email. Here is the code:
def send_otp_email(email, otp):
sender_email = app.config[‘SENDER_EMAIL’]
sender_password = app.config[‘MAIL_PASSWORD’]
smtp_server = app.config[‘MAIL_SERVER’]
smtp_port = app.config[‘MAIL_PORT’]
subject = “Your OTP Code”
body = f”Your OTP code is {otp}. It is valid for 5 minutes.”
message = MIMEMultipart()
message[‘From’] = sender_email
message[‘To’] = email
message[‘Subject’] = subject
message.attach(MIMEText(body, ‘plain’))
try:
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(sender_email, sender_password)
server.sendmail(sender_email, email, message.as_string())
print(f”OTP email sent to {email}”)
except Exception as e:
print(f”Error sending email: {e}”)
Step 4: Verifying OTP
Once the user receives the OTP, they can enter it to verify their email. If the OTP is valid, the user account is created in MongoDB:
@app.route(‘/verify_otp’, methods=[‘POST’])
def verify_otp():
email = session.get(’email’)
user_otp = request.form.get(‘otp’)
username = session.get(‘username’)
password = session.get(‘password’)
otp_record = mongo.db.otps.find_one({’email’: email, ‘otp’: user_otp})
if otp_record and otp_record[‘expires_at’] > datetime.utcnow():
hashed_password = bcrypt.hashpw(password.encode(‘utf-8’), bcrypt.gensalt())
mongo.db.user.insert_one({‘username’: username, ’email’: email, ‘password’: hashed_password.decode(‘utf-8’)})
mongo.db.otps.delete_one({‘_id’: otp_record[‘_id’]})
session.clear()
flash(‘Account created successfully! Please proceed to log in.’, ‘success’)
return redirect(url_for(‘login’))
else:
flash(‘Invalid or expired OTP’, ‘danger’)
return redirect(url_for(‘signup’))
Conclusion
In this tutorial, you learned how to build a user authentication system in Flask using MongoDB and email-based OTP verification. This system includes essential features like input validation, password hashing, and secure email communication.
Please find the full Flask and HTML code for the signup and login verification app at the following GitHub repository: Signup and Login Verification App.