Home > Courses > Flask: Python Web Development > Python/Flask Authentication (Register, Login, Session). Part 1

Python/Flask Authentication (Register, Login, Session). Part 1

Subject: Flask: Python Web Development
Still using the "School Grading System" example, we have been able CRUD (Create, Read, Update and Delete) students record.

However as our web application begins to take shape it will be very necessary that we begin to limit some access to some of its functionalities. For example:

1. Not everybody should be able to access our student list and edit it delete it's record at will. (This access should be limit to an admin e.g school owner)
2. ONLY Admin should be able to setup subjects and grade students.
3. Admin should be able to see ALL students grade, edit and delete them.
4. Admin should be able to see ALL students record and DELETE them
5. Students should be able to register on the web app themselves (with their email and password validated) - rather than admin creating their profile record for them.
6. Registered students should be able to login and access ONLY their profile and EDIT it
7. Students should be able to see ONLY their grades as well.

These access controls or restrictions. That is, what a user can login to see or do is what is referred to as Authentication and authorization

Registration and Login
DATABASE:
First we will create a new table for admin user:

CREATE TABLE  IF NOT EXISTS admin (admin_id INTEGER PRIMARY KEY, username TEXT, password TEXT)'


And modify the student table to include the email and password fields, you can drop or delete the table and recreate:

CREATE TABLE  IF NOT EXISTS student (student_id INTEGER PRIMARY KEY, surname TEXT, other_name TEXT, location_address TEXT, email TEXT, password TEXT)


BACKEND-Python
Then we created the Authicication blueprint file, to handle the registration, login and logout functions.

authentication_blueprint.py

from flask import Blueprint, render_template, request, url_for, redirect, session
import re 
import sqlite3 

from database import connect_db
conn = connect_db()

auth_bp=Blueprint("authentication_blueprint", __name__, static_folder="static", template_folder="templates")


@auth_bp.route('/register', methods =['GET', 'POST'])
def register():
    msg = ''
    if request.method == 'POST':
        surname = request.form['surname']
        other_name = request.form['othernames']
        address = request.form['address']
        email = request.form['email']
        password = request.form['password']

        cursor = conn.cursor() 
        cursor.execute(f"SELECT * FROM student WHERE email = '{email}'  OR password = '{password}' ")
        record = cursor.fetchone()
        if record:
            msg = 'Email or password already registered try again!'
        elif not re.match(r'[^@]+@[^@]+\.[^@]+', email):
            msg = 'Invalid email address !'
        elif not surname or not other_name or not email or not password:
            msg = 'Please fill out the * form fields !'
        else:
            conn.execute(f"INSERT INTO student (surname, other_name, location_address, email, password) VALUES ('{surname}', '{other_name}', '{address}', '{email}', '{password}')")
            conn.commit()
            msg = 'You have successfully registered !'
            return render_template('register.html', msg = msg)
    else:
      return render_template('register.html')
      

@auth_bp.route('/login', methods =['GET', 'POST'])
def login():
    msg = ''
    
    if request.method == 'POST':
        email = request.form['email']
        password = request.form['password']
        cursor = conn.cursor() 
        cursor.execute(f"SELECT * FROM student WHERE email = '{email}' AND password = '{password}' ")
        record = cursor.fetchone()
        if record:
            session['loggedin'] = True
            session['id'] = record[0]
            session['login_sname'] = record[1]
            session['login_id'] = record[4] #student email
            session['login_pw'] = record[5] #student pw
            
            #check if login id is email
            if re.match(r'[^@]+@[^@]+\.[^@]+', session['login_id'] ):
              #use is student
               session['level'] = "student"
            else:
              #user is an admin
              session['level'] = "admin"
            
            #redirect to success login URL 
            return redirect(url_for('auth'))
        else:
          msg = 'Incorrect username / password !'
          return render_template('login.html', msg = msg)
    else:
      return render_template('login.html')
      
@auth_bp.route('/logout')
def logout():
    msg = ''
    session.pop('loggedin', None)
    session.pop('login_id', None)
    session.pop('login_pw', None)
    session.pop('level', None)
    msg = 'You have logout now'
    #return redirect(url_for('login'))
    return render_template('login.html', msg = msg)


In the app.py I have this new blueprint registered and another view or route (auth) to serve a welcome view if the login is successful else a user is redirect to login URL is the login attempt fails.

app.py

from flask import Flask, render_template, request, session, url_for, redirect
import re 

from database import connect_db
conn = connect_db()

from student_blueprint import student_bp
from table_create_blueprint import tables_bp
from authentication_blueprint import auth_bp

app = Flask(__name__) 
app.secret_key = 'your secret key'

#register blueprints
app.register_blueprint(student_bp)
app.register_blueprint(tables_bp)
app.register_blueprint(auth_bp)

#load the home or index page
@app.route('/home') 
@app.route('/') 
def home(): 
  return render_template('index.html')

#user loggedin URL
@app.route('/auth') 
def auth(): 
  if 'loggedin' in session:
      value=session['login_sname']
      return render_template('auth.html', logname = value)
  else:
      return redirect(url_for('home'))
        
if __name__ == '__main__': 
       app.run(debug=True)


FRONTEND-HTML, Jinja
Because the navigation links when a user has not logged in will be different from the menu of a logged in use.
For example before login a user will see "Login" in the navigation, but after a successful login the user is not supposed to see "Login" again, instead the "Logout" link.

To achieve this we modify the logout as follows:

templates/layout.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
School Grading System - {% block title %}{% endblock title %}
</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>

    <header>
<h1>School Grading System</h1>
    </header>

    <nav>
        <a href="{{ url_for('home') }}">Home</a>        
        
         <a href="{{ url_for('authentication_blueprint.login') }}">Login</a>
    </nav>
 
    <div class="container">
   <h2> {% block subheading %}{% endblock subheading %}</h2>

{% block content %}
<p>No messages.</p>
{% endblock content %}
    </div>
<footer>
Designed and Developed by Ben Onuorah
</footer>
</body>
</html>


And we created another layout for authenticated templates.

templates/auth_layout.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
School Grading System - {% block title %}{% endblock title %}
</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>

    <header>
<h1>School Grading System</h1>
    </header>

    <nav>
        <a href="{{ url_for('auth') }}">My Home</a>      
        
        <a href="{{ url_for('student_blueprint.seestudents') }}">Students</a>
        <a href="#">Subjects</a>              
        <a href="#">Grades</a>
        
        <a href="{{ url_for('authentication_blueprint.logout') }}">Logout</a>
    </nav>
 
    <div class="container">
   <h2> {% block subheading %}{% endblock subheading %}</h2>

{% block content %}
<p>No messages.</p>
{% endblock content %}
    </div>
<footer>
Designed and Developed by Ben Onuorah
</footer>
</body>
</html>



And we have the login templates file:

templates/login.html

{% extends 'layout.html' %}
{% block title %}Login{% endblock title %}
<h2>
{% block subheading %}
    Login
{% endblock subheading %}
</h2>

{% block content %}
<p align='center'>
  <a href="{{ url_for('authentication_blueprint.register') }}">Register Here</a> 
</p>

{% if msg %}
  <p><b>{{ msg }}</b></p>
{% endif %}
     <form method="POST" action="{{ url_for('authentication_blueprint.login') }}">
      
      Email: <br/>
      <input type="email" name="email" required="true" />
      <br/>
      Password: <br/>
      <input type="password" name="password" required="true" />
      
       <br/>
      <input type="submit" name="login" value="Login"/>
      
  </form>

{% endblock content %}



The register templates file:

templates/register.html

{% extends 'layout.html' %}
{% block title %}Register{% endblock title %}
<h2>

{% block subheading %}
Student Register Form
{% endblock subheading %}
</h2>

{% block content %}

<p align='center'>
  <a href="{{ url_for('authentication_blueprint.login') }}">Login Here</a>  
</p>

{% if msg %}
  <p><b>{{ msg }}</b></p>
{% endif %}
    <form method="POST" action ="{{ url_for('authentication_blueprint.register') }}">
      
      *Email: <br/>
      <input type="email" name="email" required="true" />
      <br/>
      
      *Password: <br/>
      <input type="password" name="password" required="true" />
      <br/>
      
      *Surname: <br/>
      <input type="text" name="surname" required="true" />
      <br/>
      Other names: <br/>
      <input type="text" name="othernames" />
      <br/>
      Address: <br/>
      <textarea name="address"></textarea>
      

      <br/>
      <input type="submit" name="register" value="Register"/>
  </form>

{% endblock content %}


templates/index.html

{% extends 'layout.html' %}
{% block title %}Welcome{% endblock title %}
<h2>
{% block subheading %}Home{% endblock subheading %}</h2>
{% block content %}
<p>
 Welcome to my web app
</p>
{% endblock content %}


templates/auth.html

{% extends 'auth_layout.html' %}
{% block title %}Welcome{% endblock title %}
<h2>
{% block subheading %}Hello:{{logname}} {% endblock subheading %}</h2>
{% block content %}
<p>
Login was successfull
</p>
{% endblock content %}


templates/seestudents.html

{% extends 'auth_layout.html' %}

{% block title %}See Students{% endblock title %}
<h2>
{% block subheading %}See Students{% endblock subheading %}</h2>
{% block content %}

<p align='center'>
  <a href="{{ url_for('student_blueprint.student') }}">Add New Record</a>  
</p>

<table width="100%" border=1> 
            <tr> 
              <th>Id</th> 
              <th>Surname</th> 
              <th>Othernames</th> 
              <th>Address</th>
              <th>Edit</th>
              <th>Delete</th>
            </tr> 

            {%for rec in students%} 
               <tr> 
                 <td>{{rec[0]}}</td> 
                 <td>{{rec[1]}}</td> 
                 <td>{{rec[2]}}</td> 
                 <td>{{rec[3]}}</td> 
                 <td>
                    <a href="delete_student?value={{rec[0]}}" title="Delete" onclick="return confirm('Are you sure you want to delete?')">
                      Delete
                    </a>
                 </td> 
                 <td>                   
                    <a href="student?task=edit&value={{rec[0]}}" title="Edit">
                      Edit
                    </a>
                 </td> 
              </tr> 
            {%endfor%} 
          </table>   

{% endblock content %}


Now our program can allow students registration with email and password validation (i.e check if someone else has used either of the two), Login and logout.

In the next lesson we will setup admin and create it's functions and access, as we also restrict the students user access the more.

When the app.py is run here is my testing interections

Gitup link to download this full code:

https://github.com/BenOnuorah/tealearn_python_flask_auth.git



home route using the logout.html template




From login link you will see the link to Register as a student




Completing the registration form




Registration was successful




About to login




Login was successful now in auth welcome URL




Logout successful




After logout you cannot access auth





By: Benjamin Onuorah

Comments

No Comment yet!

Login to comment or ask question on this topic


Previous Topic

Supported by