DEV Community

Dhruv Prajapati
Dhruv Prajapati

Posted on • Edited on

Flask Login-Register-Logout Implementation

  • This is the Part-2 of the Flask Series. I have explained What is Rest API and Implementation in Flask. Please refer that article first for better understanding of rest apis.

  • Today we are going to implement Login-Register Flow in Flask.So without any further let's jump into it. You can check whole code on Github Repository.

  • First of all, we will install dependencies which we are going to use are listed below.

    Flask = Building Web Application
    Flask-SQLAlchemy = Provide support for SQLAlchemy to your application
    Flask-WTF = To Create Interactive User Interface
    SQLAlchemy = Provide ORM for our Application
    Werkzeug = WSGI Web Application Library, it will provide security to encrypt password and matching Password
    WTForms = Form Validation and Generation Library

  • To install dependencies in your project use pip command.

pip install <library-name>

  • Now Create directory named as Flask-Login-Register. our project structure is like below image.

Alt Text

  • Now we create Login and Registration Forms using wtforms.
  • create forms.py file inside the directory.
# Importing Require Module

from wtforms import Form, BooleanField, StringField, PasswordField, validators, TextAreaField, IntegerField

from wtforms.validators import DataRequired

# Creating Login Form contains email and password
class LoginForm(Form):

    email = StringField("Email", validators=[validators.Length(min=7, max=50), validators.DataRequired(message="Please Fill This Field")])

    password = PasswordField("Password", validators=[validators.DataRequired(message="Please Fill This Field")])

# Creating Registration Form contains username, name, email, password and confirm password.

class RegisterForm(Form):

    name = StringField("Ad", validators=[validators.Length(min=3, max=25), validators.DataRequired(message="Please Fill This Field")])

    username = StringField("Username", validators=[validators.Length(min=3, max=25), validators.DataRequired(message="Please Fill This Field")])

    email = StringField("Email", validators=[validators.Email(message="Please enter a valid email address")])

    password = PasswordField("Password", validators=[

        validators.DataRequired(message="Please Fill This Field"),

        validators.EqualTo(fieldname="confirm", message="Your Passwords Do Not Match")
    ])

    confirm = PasswordField("Confirm Password", validators=[validators.DataRequired(message="Please Fill This Field")])
Enter fullscreen mode Exit fullscreen mode
  • This file will create Forms when we will use Jinja2 Template to create our templates. Here, DataRequired function will throw an error if field is empty.

  • For password and confirm password we have used PasswordField function and to check that password and confirm password is same we have used EqualTo function.

  • Let's, generate our User model and API Endpoints. For the simplicity purpose i will write whole code in one file.

  • Create app.py under the directory and place below code inside the file.

# Importing require libraries
from flask import Flask, render_template, flash, redirect, request, session, logging, url_for

from flask_sqlalchemy import SQLAlchemy

from forms import LoginForm, RegisterForm

from werkzeug.security import generate_password_hash, check_password_hash

# Now create flask application object

app = Flask(__name__)

# Database Configuration and Creating object of SQLAlchemy

app.config['SECRET_KEY'] = '!9m@S-dThyIlW[pHQbN^'

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@localhost/auth'

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

# Create User Model which contains id [Auto Generated], name, username, email and password

class User(db.Model):

    __tablename__ = 'usertable'

    id = db.Column(db.Integer, primary_key=True)

    name= db.Column(db.String(15), unique=True)

    username = db.Column(db.String(15), unique=True)

    email = db.Column(db.String(50), unique=True)

    password = db.Column(db.String(256), unique=True)

Enter fullscreen mode Exit fullscreen mode
  • Now create our first route, which will render the home page of our flask web application. put below code inside the app.py.
@app.route('/')
def home():

    return render_template('index.html')

Enter fullscreen mode Exit fullscreen mode
  • It's time to create actual functionality for our web application. In our case it is endpoint for login and register.

  • put below code inside app.py. it will provide use User Registration Functionality.

# User Registration Api End Point
@app.route('/register/', methods = ['GET', 'POST'])
def register():
    # Creating RegistrationForm class object
    form = RegisterForm(request.form)

    # Cheking that method is post and form is valid or not.
    if request.method == 'POST' and form.validate():

        # if all is fine, generate hashed password
        hashed_password = generate_password_hash(form.password.data, method='sha256')

        # create new user model object
        new_user = User(

            name = form.name.data, 

            username = form.username.data, 

            email = form.email.data, 

            password = hashed_password )

        # saving user object into data base with hashed password
        db.session.add(new_user)

        db.session.commit()

        flash('You have successfully registered', 'success')

        # if registration successful, then redirecting to login Api
        return redirect(url_for('login'))

    else:

        # if method is Get, than render registration form
        return render_template('register.html', form = form)
Enter fullscreen mode Exit fullscreen mode
  • Above API endpoint will check that is http request method is get, than render registration form but if the http request method is post, than it will check that form is valid or not, if form is valid, it will generate hash password from password and create new user object with that password and then it will save that object inside the Database.

  • Here, one thing to notice that, we do not need to check that password and confirm password is same or not by making login, we have checked that in our registration form generation class.

  • Now, Let's create our Login and Logout APIs. Put below code inside the app.py file.

# Login API endpoint implementation
@app.route('/login/', methods = ['GET', 'POST'])
def login():
    # Creating Login form object
    form = LoginForm(request.form)
    # verifying that method is post and form is valid
    if request.method == 'POST' and form.validate:
        # checking that user is exist or not by email
        user = User.query.filter_by(email = form.email.data).first()

        if user:
            # if user exist in database than we will compare our database hased password and password come from login form 
            if check_password_hash(user.password, form.password.data):
                # if password is matched, allow user to access and save email and username inside the session
                flash('You have successfully logged in.', "success")

                session['logged_in'] = True

                session['email'] = user.email 

                session['username'] = user.username
                # After successful login, redirecting to home page
                return redirect(url_for('home'))

            else:

                # if password is in correct , redirect to login page
                flash('Username or Password Incorrect', "Danger")

                return redirect(url_for('login'))
    # rendering login page
    return render_template('login.html', form = form)

Enter fullscreen mode Exit fullscreen mode
  • Here, if method is post and form is valid, than it will search user in Database using user's email. if user exist than it will compare the hashed password which is stored inside the database and simple password which is entered by user.

  • If both password matched then allow user to access and redirect user to home while saving username and email inside the session.

  • Let's write our last backend logic for Logout API and Running Server. Write below code inside the app.py.

@app.route('/logout/')
def logout():
    # Removing data from session by setting logged_flag to False.
    session['logged_in'] = False
    # redirecting to home page
    return redirect(url_for('home'))

if __name__ == '__main__':
    # Creating database tables
    db.create_all()
    # running server
    app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode
  • Now, let's make our frontend templates and static.

  • Create templates directory inside the Flask-Login-Register directory.

  • Let's create our layout file named as base.html inside the templates directory. It will provide basic layout for our front end.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
    <link rel="stylesheet" href="../static/main.css">
    <title>Flask</title>
</head>
<body>

    <div class="container">

        {% block body %}

        {% endblock body %}
    </div>

    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
    <hr>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • Create includes directory inside our templates directory and create formhelpers.html inside the includes directory.

  • formhelpers.html uses jinja2 macros functionality, through this we can dynamically render our error & success messages and html fields. write below html code inside the formhelpers.html.

{% macro render_field(field) %}
  <dt>{{ field.label }}
  <dd>{{ field(**kwargs)|safe }}
  {% if field.errors %}
    <ul class=errors>
    {% for error in field.errors %}
      <li>{{ error }}</li>
    {% endfor %}
    </ul>
  {% endif %}
  </dd>
{% endmacro %}
Enter fullscreen mode Exit fullscreen mode
  • Now, create our index.html, login.html and register.html inside templates directory.

  • put below code inside the index.html.

{% extends "base.html" %}

{% block body %}

<div class="topnav">
    {% if session.logged_in %}
        <a href="/logout/">Logout</a>
    {% else %}
        <a href="/login/">Login</a>
        <a href="/register/">Sign up</a>
    {% endif %}
</div>
<hr>

{% if session['logged_in'] %}
    <h5>Welcome, {{session['username'] }}</h5>
{% endif %}

{% endblock body %}

Enter fullscreen mode Exit fullscreen mode
  • In above code, it will check for logged_in flag. it it is true than it will show logout on navbar and if it is false than it will show login and signup on navbar.

Alt Text

  • Now, createlogin.html file.
{% extends "base.html" %}

{% block body %}

{% from "includes/formhelpers.html" import render_field %}

<h4>
    <strong>
        Login
    </strong>
</h4>
<hr>

<form method="POST">

    {{ render_field(form.email, class="form-control") }}
    {{ render_field(form.password, class="form-control") }}

    <button type="submit" class="btn btn-primary">Login</button>
</form>

<hr>

<a href="/register">Create a New Account!</a>

{% endblock body %}
Enter fullscreen mode Exit fullscreen mode
  • Here, render field will render email and password by using jinja2 macro functionality.

Alt Text

  • Now, Create register.html.
{% extends "base.html" %}

{% block body %}

{% from "includes/formhelpers.html" import render_field %}

<h4>
    <strong>
        Register
    </strong>
</h4>
<hr>

<form method="POST">

    {{ render_field(form.name, class="form-control") }}
    {{ render_field(form.username, class="form-control") }}
    {{ render_field(form.email, class="form-control") }}
    {{ render_field(form.password, class="form-control") }}
    {{ render_field(form.confirm, class="form-control") }}

    <button type="submit" class="btn btn-primary">Register</button>
</form>

<hr>

<a href="/login">Login into existing account!</a>

{% endblock body %}
Enter fullscreen mode Exit fullscreen mode

Alt Text

  • Now, let's create simple navigation bar for out project by applying css to the class.
  • Create static director inside Flask-Login-Register and create main.css file. Our main.css file will look like below.
/* Add a black background color to the top navigation */
.topnav {
    background-color: #172944;
    overflow: hidden;
}

  /* Style the links inside the navigation bar */
.topnav a {
    float: left;
    color: #f2f2f2;
    text-align: center;
    padding: 14px 16px;
    text-decoration: none;
    font-size: 17px;
}

  /* Change the color of links on hover */
.topnav a:hover {
    background-color: #ddd;
    color: black;
}

  /* Add a color to the active/current link */
.topnav a.active {
    background-color: #4CAF50;
    color: white;
}
Enter fullscreen mode Exit fullscreen mode
  • Here. our login-registration flow is completed.

  • Let's run our project.

  • To run project in windows use below command inside the Flask-Login-Register path of command prompt.

set FLASK_APP=app.py

set FLASK_DEBUG=1

flask run --port=8080 
Enter fullscreen mode Exit fullscreen mode
  • To run project in MacOs/Linux use below command inside the Flask-Login-Register path of terminal.
export FLASK_APP=app.py

export FLASK_DEBUG=1

flask run --port=8080 
Enter fullscreen mode Exit fullscreen mode
  • Here, --port is optional flag.

  • In next post i will explain what is jwt and implement jwt with flask.

Thank you for Reading. Give like and follow me for more amazing tutorials.

Top comments (6)

Collapse
 
bhansa profile image
Bharat Saraswat • Edited

Hey, One Tip: you can give code language in markdown when you are adding code in your post, it will render much cleaner. Check the markdown guide, you'll understand :)
Like below:

@app.route('/')
def home():
    return render_template('index.html')
Collapse
 
imdhruv99 profile image
Dhruv Prajapati

Okay, Thank you sir👍🏻

Collapse
 
bhansa profile image
Bharat Saraswat

Haha, please no "sir" :)

Thread Thread
 
imdhruv99 profile image
Dhruv Prajapati

I have updated it, look at now. Thank you for your reference 👍

Collapse
 
jjlatval profile image
Joni Latvala

Hey a small comment since I just fixed a CSRF issue related to /logout in a Flask web app. Please do not advise to do it with GET method, as that means that if any service has a JS or e.g. an IMG with src that forces you to go to /logout, then any service can force your users to logout at will. The best way is to use a POST request with CSRF token to check that a logout request is made by the service in question.

Collapse
 
si2sa profile image
Si2sa • Edited

hello, how can i fix this plz? TypeError: 'Markup' object is not callable