loading...

Flask Login-Register-Logout Implementation

imdhruv99 profile image Dhruv Prajapati Updated on ・7 min read
  • 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")])
  • 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)

  • 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')

  • 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)
  • 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)

  • 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)
  • 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>
  • 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 %}
  • 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 %}

  • 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 %}
  • 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 %}

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;
}
  • 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 
  • 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 
  • 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.

Posted on by:

imdhruv99 profile

Dhruv Prajapati

@imdhruv99

Enthusiastic to Dive deep into Computer Science

Discussion

markdown guide
 

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')
 

Okay, Thank you sir👍🏻

 

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