DEV Community

Cover image for Building a Webapp with User Authentification using Flask
Kitibwa Rich
Kitibwa Rich

Posted on

Building a Webapp with User Authentification using Flask

In this article, I will be showing you how to build a simple web app with user authentification or login using flask.

This app will have the following features:

  1. Users will be able to sign up using their email addresses and also create a password.
  2. Once a user is signed in, they are registered in the database.
  3. If any user tries to sign in with credentials that already exist in the database, an error message will be returned notifying them.
  4. The app will have a remember me function to remember users so they don't always have to enter their passwords to login to the app.
  5. When logged in, a user will be able to see other routes of the app like profile and sign out.
  6. When signed out, the user can only see the home, Login and signup routes.

Alright lets get coding.

The first step is to fire up the IDE of choice.
Then create a project folder and then set up a virtual environment in which we will add the different dependencies for the app.

Install the necessary packages such as flask, flask-login(which is used for the user login and authentication), flask-sqlalchemy which is used to create a database to store the data.

Once all that is set up, create a new python file and call it init.py. This will initialise the app.

Open the init.py file and import the following packages;

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
Enter fullscreen mode Exit fullscreen mode

Flask provides a lightweight framework for building our web app.
SQLAlchemy is used to create the database to store user data such as emails and passwords.
Checkout the official documentation here to know about flask-SQLAlchemy.
flask-login provides user session management for flask. It handles common tasks such as logging in, logging out and remembering users' sessions . Be sure to check out the official flask-login documentation here for more information about it.

Next, we will create a function to initialize the database using SQLAlchmemy as shown in the code snippet below.

Initialise SQLAlchemy so we can use it later.

db = SQLAlchemy()

Create a function called create_app and create the flask instance and also add other configurations for the database.

def create_app():
    app = Flask(__name__)
    app.secret_key = b'_5#y2L"F4Qz\k\xec]/'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
    db.init_app(app)
Enter fullscreen mode Exit fullscreen mode

create a variable called login_manager to create a Login Manager instance to manage user login.

login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app) 
Enter fullscreen mode Exit fullscreen mode

Then create another python file called models.py to store information about the users.

The model created is constituted of class which is translated into tables in our database. Each of the attributes are transformed into columns for the table.

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(100))
    name = db.Column(db.String(1000)
Enter fullscreen mode Exit fullscreen mode

Open the init.py file again and import class User from models.py

Open a new python file called auth.py. This file will contain all the logic for our auth routes for example login and signup.

Import the neccesary packages first.

from flask import Blueprint, render_template, redirect, url_for, request, flash
from werkzeug.security import generate_password_hash, check_password_hash
from models import User
from flask_login import login_user, logout_user, login_required
from init import db
Enter fullscreen mode Exit fullscreen mode

Flash is used to show messages to the user for example error messages.
Blueprint is a python package that can help structure a flask application by grouping its functionality into reusable components. Read more about Blueprint package here.

In this file we will put all the auth routes with logic to add new users to the database ,remember the user and logout the user.

Here is the full file below

auth.py

from flask import Blueprint, render_template, redirect, url_for, request, flash
from werkzeug.security import generate_password_hash, check_password_hash
from models import User
from flask_login import login_user, logout_user, login_required
from init import db

auth = Blueprint('auth', __name__)


@auth.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        email = request.form.get('email')
        password = request.form.get('password')
        remember = True if request.form.get('remember') else False
        user = User.query.filter_by(email=email).first()

        if not user:
            flash('Please sign up first!')
            return redirect(url_for('auth.signup'))
        elif not check_password_hash(user.password, password):
            flash('Please check your login details and try again.')
            return redirect(url_for('auth.login'))
        login_user(user, remember=remember)
        return redirect(url_for('main.profile'))


@auth.route('/signup', methods=['GET', 'POST'])
def signup():
    if request.method == 'GET':
        return render_template('signup.html')
    else:
        email = request.form.get('email')
        name = request.form.get('name')
        password = request.form.get('password')
        user = User.query.filter_by(email=email).first()

        if user:
            flash('Email already exists')
            return redirect(url_for('auth.signup'))
        new_user = User(email=email, name=name, password=generate_password_hash(password, method='SHA256'))

        db.session.add(new_user)
        db.session.commit()
        return redirect(url_for('auth.login'))


@auth.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('main.index'))
Enter fullscreen mode Exit fullscreen mode

This is the full init.py file.

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager

db = SQLAlchemy()


def create_app():
    app = Flask(__name__)
    app.secret_key = b'_5#y2L"F4Qz\k\xec]/'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
    db.init_app(app)
    login_manager = LoginManager()
    login_manager.login_view = 'auth.login'
    login_manager.init_app(app)

    from models import User

    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))

    # blueprint for auth routes in the app
    from auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint)

    # blueprint for non auth routes in the app
    from main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    return app
Enter fullscreen mode Exit fullscreen mode

The main.py file

from flask import Blueprint, render_template
from flask_login import login_required, current_user
from init import create_app, db

main = Blueprint('main', __name__)


@main.route('/')
def index():
    return render_template('index.html')


@main.route('/profile')
@login_required
def profile():
    return render_template('profile.html', name=current_user.name)


app = create_app()

if __name__ == '__main__':
    db.create_all(app=create_app())
    app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

This is the main entry point of the app.

The models.py file

from flask_login import UserMixin
from init import db


class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(100))
    name = db.Column(db.String(1000))

    def __repr__(self):
        return'<User %r>' % self.id

    def __init__(self, email, password, name):
        db.create_all()
        self.email = email
        self.password = password
        self.name = name
Enter fullscreen mode Exit fullscreen mode

After creating the logic for the app, we now create the templates of the different pages.

Create a templates folder in the main project folder and create a new html file called base.html

This is the main html file from which all the other html file will inherit. We use jinja templates to extend the base.html to the other templates.

Here is the full base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask Login</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" />
    <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">

</head>
<body>
<section class="hero is-fullheight" style="background-image:#C4FCEF;">

    <div class="hero-head">
        <nav class="navbar">
            <div class="container">
                <div class="navbar-brand">
                    <a href="{{ url_for('main.index') }}" class="navbar-item" style="color:white">
                        CampNight
                    </a>
                </div>

                <div id="navbarMenuHeroA" class="navbar-menu">
                    <div class="navbar-end">
                        <a href="{{ url_for('main.index') }}" class="navbar-item">
                            Home
                        </a>
                        {% if current_user.is_authenticated %}
                        <a href="{{ url_for('main.profile') }}" class="navbar-item">
                            Profile
                        </a>
                        {% endif %}

                        {% if not current_user.is_authenticated %}
                        <a href="{{ url_for('auth.login') }}" class="navbar-item">
                            Login
                        </a>
                        <a href="{{ url_for('auth.signup') }}" class="navbar-item">
                            Sign Up
                        </a>
                        {% endif %}

                        {% if current_user.is_authenticated %}
                        <a href="{{ url_for('auth.logout') }}" class="navbar-item">
                            Logout
                        </a>
                        {% endif %}
                    </div>
                </div>
            </div>
        </nav>
    </div>

    <div class="hero-body">
        <div class="container has-text-centered">
            {% block content %}{% endblock %}
        </div>
    </div>
</section>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Notice that for the style we use Bulma css which is a nice framework. Read more about it here.

We then create an index.html which is the homepage.

index.html

{% extends "base.html" %}

{%block content%}
<h1 class ="title">Welcome to CampNight </h1>
<h2 class="subtitle">Are you ready for an adventure?</h2>
{%endblock%}
Enter fullscreen mode Exit fullscreen mode

Check out the jinja templates used here in curly brackets. You can read more about jinja templates here.

We then create the login.html template

login.html

{% extends "base.html" %}

{%block content%}
<div class="column is-4 is-offset-4">
    <h3 class="title">Login</h3>
    <div class="box">
        {% with messages = get_flashed_messages() %}
            {% if messages %}
                <div class="notification is-danger">
                     {{ messages[0] }}
                 </div>
            {% endif %}
        {% endwith %}
        <form method="POST" action="/login">
            <div class="field">
                <div class="control">
                    <input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus="">
                </div>
            </div>
            <div class="field">
                <div class="control">
                    <input class="input is-large" type="password" name="password" placeholder="Your Password">
                </div>
            </div>
            <div class="field">
                <label class="checkbox">
                    <input type="checkbox">
                    Remember me
                </label>
            </div>
            <button class="button is-block is-info is-large is-fullwidth">Login</button>
        </form>
    </div>
</div>
{%endblock%}
Enter fullscreen mode Exit fullscreen mode

Then the profile.html to display the name of the particular user once they login.

{% extends 'base.html' %}

{% block content %}
<h1 class="title">
    Welcome, {{ name }}!
</h1>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

We then add the signup.html for users to sign up.

{% extends "base.html" %}

{% block content %}
<div class="column is-4 is-offset-4">
    <h3 class="title">Sign Up</h3>
    <div class="box">
        {% with messages = get_flashed_messages() %}
            {% if messages %}
                <div class="notification is-danger">
                    {{ messages[0] }}<br> Go to <a href="{{ url_for('auth.login') }}">login page</a>
                </div>
            {% endif %}
        {% endwith %}
        <form method="POST" action="/signup">
            <div class="field">
                <div class="control">
                    <input class="input is-large" type="email" name="email" placeholder="Email" autofocus="">
                </div>
            </div>
            <div class="field">
                <div class="control">
                    <input class="input is-large" type="text" name="name" placeholder="Name" autofocus="">
                </div>
            </div>
            <div class="field">
                <div class="control">
                    <input class="input is-large" type="password" name="password" placeholder="Password">
                </div>
            </div>
            <button class="button is-block is-info is-large is-fullwidth">Sign Up</button>
        </form>
    </div>
</div>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Add another folder in the project folder and name it static.
This folder contains resources for the app such as customised css sheets and images added to the app.

The app is now finally ready and you can run it by running python main.py in the terminal.

Run the app on local host 5000 and it will displayed in the browser.
Sample webapp

Feel free to run the app hosted on here

Resources

Be sure to check out this amazing medium article for more on how to implement user authentification in your web app using flask.

Discussion (2)

Collapse
asapsonter profile image
Sam Sonter

well done

Collapse
asapsonter profile image
Sam Sonter

would you like to send the github like of the web app ?