Skip to main content

Flask: User Login and Logout

Flask: User Login and Logout

User login and logout are essential components of secure Flask web applications, enabling authenticated access to protected resources and safe session termination. Built on Flask Authentication, Flask User Registration, Flask Querying the Database, and Flask Relationships in Models, Flask uses extensions like Flask-Login and Flask-Bcrypt to manage user sessions and verify credentials. This tutorial explores Flask user login and logout, covering secure authentication, session management, and route protection, with practical applications in web development.


01. Why Implement User Login and Logout?

Login authenticates users to access personalized features, while logout ensures secure session termination to protect user data. Flask, combined with SQLAlchemy and secure extensions, provides a robust framework for managing user sessions. Leveraging NumPy Array Operations for efficient database queries, Flask supports scalable authentication workflows for applications like blogs, e-commerce platforms, or dashboards.

Example: Basic User Login and Logout

from flask import Flask, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user
from flask_bcrypt import Bcrypt

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///auth.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)

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

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    user = User.query.filter_by(username=username).first()
    if user and bcrypt.check_password_hash(user.password_hash, password):
        login_user(user)
        return "Login successful"
    return "Invalid credentials"

@app.route('/logout')
def logout():
    logout_user()
    return "Logged out"

with app.app_context():
    db.create_all()
    hashed_password = bcrypt.generate_password_hash('mypassword').decode('utf-8')
    db.session.add(User(username='Alice', password_hash=hashed_password))
    db.session.commit()

Output:

Login successful (on valid POST request with username='Alice' and password='mypassword')
Logged out (on GET request to /logout)

Explanation:

  • Flask-Login - Manages user sessions with login_user and logout_user.
  • Flask-Bcrypt - Verifies passwords against stored hashes.
  • UserMixin - Provides authentication methods for the User model.

02. Key Login and Logout Techniques

Flask offers tools to implement secure and user-friendly login and logout workflows. Below is a summary of key techniques and their applications in web applications:

Technique Description Use Case
Login Form Handling Process login credentials Authenticate users
Password Verification Check credentials securely Ensure valid logins
Session Management Maintain user sessions Track authenticated users
Logout Terminate user sessions Secure session closure
Protected Routes Restrict access to authenticated users Secure dashboards


2.1 Login Form Handling

Example: Processing Login Form

from flask import Flask, request, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user
from flask_bcrypt import Bcrypt

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///login.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)

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

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter_by(username=username).first()
        if user and bcrypt.check_password_hash(user.password_hash, password):
            login_user(user)
            return "Logged in"
        return "Invalid credentials"
    return render_template('login.html')

with app.app_context():
    db.create_all()
    hashed_password = bcrypt.generate_password_hash('testpass').decode('utf-8')
    db.session.add(User(username='Bob', password_hash=hashed_password))
    db.session.commit()

Output:

Logged in (on valid POST request with username='Bob' and password='testpass')

Explanation:

  • request.form - Retrieves username and password from the login form.
  • render_template - Serves a login form for GET requests.

2.2 Password Verification

Example: Secure Password Check

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user
from flask_bcrypt import Bcrypt

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///verify.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)

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

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    user = User.query.filter_by(username=username).first()
    if not user or not bcrypt.check_password_hash(user.password_hash, password):
        return "Invalid username or password"
    login_user(user)
    return "Login successful"

with app.app_context():
    db.create_all()
    hashed_password = bcrypt.generate_password_hash('securepass').decode('utf-8')
    db.session.add(User(username='Charlie', password_hash=hashed_password))
    db.session.commit()

Output:

Login successful (on valid POST request with username='Charlie' and password='securepass')

Explanation:

  • check_password_hash - Securely verifies the password against the stored hash.
  • Generic error message prevents username enumeration attacks.

2.3 Session Management

Example: Managing User Sessions

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, current_user
from flask_bcrypt import Bcrypt

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///session.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)

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

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    user = User.query.filter_by(username=username).first()
    if user and bcrypt.check_password_hash(user.password_hash, password):
        login_user(user)
        return f"Logged in as {current_user.username}"
    return "Invalid credentials"

@app.route('/status')
def status():
    if current_user.is_authenticated:
        return f"User {current_user.username} is logged in"
    return "No user logged in"

with app.app_context():
    db.create_all()
    hashed_password = bcrypt.generate_password_hash('pass123').decode('utf-8')
    db.session.add(User(username='David', password_hash=hashed_password))
    db.session.commit()

Output:

Logged in as David (on valid POST request with username='David' and password='pass123')
User David is logged in (on GET request to /status)

Explanation:

  • current_user - Tracks the authenticated user across requests.
  • is_authenticated - Checks if a user is logged in.

2.4 User Logout

Example: Secure Logout

from flask import Flask, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, logout_user, login_required
from flask_bcrypt import Bcrypt

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///logout.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)

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

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('home'))

@app.route('/home')
def home():
    return "Welcome to the homepage"

with app.app_context():
    db.create_all()

Output:

Welcome to the homepage (on GET request to /logout for authenticated user)

Explanation:

  • logout_user - Clears the user session.
  • login_required - Ensures only authenticated users can log out.

2.5 Incorrect Login/Logout Setup

Example: Insecure Login without Hashing

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///insecure.db'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)  # Plain text

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    user = User.query.filter_by(username=username).first()
    if user and user.password == password:  # Insecure comparison
        return "Logged in"
    return "Invalid credentials"

with app.app_context():
    db.create_all()

Output:

Logged in (but with insecure password storage)

Explanation:

  • Plain-text password storage and comparison are insecure.
  • Solution: Use Flask-Bcrypt for hashing and verification.

03. Effective Usage

3.1 Recommended Practices

  • Use Flask-Login for session management and Flask-Bcrypt for password verification.

Example: Comprehensive Login and Logout

from flask import Flask, request, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask_bcrypt import Bcrypt

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///complete.db'
app.config['SECRET_KEY'] = 'secure-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)

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

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter_by(username=username).first()
        if user and bcrypt.check_password_hash(user.password_hash, password):
            login_user(user)
            return redirect(url_for('dashboard'))
        return "Invalid credentials"
    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('home'))

@app.route('/dashboard')
@login_required
def dashboard():
    return f"Welcome, {current_user.username}, to your dashboard"

@app.route('/home')
def home():
    return "Welcome to the homepage"

with app.app_context():
    db.create_all()
    hashed_password = bcrypt.generate_password_hash('secure123').decode('utf-8')
    db.session.add(User(username='Eve', password_hash=hashed_password))
    db.session.commit()

Output:

Welcome, Eve, to your dashboard (on valid login with username='Eve' and password='secure123')
Welcome to the homepage (after logout)
  • login_manager.login_view - Redirects unauthenticated users to the login page.
  • Combines login, logout, and protected routes for a seamless workflow.
  • Uses redirects for better user experience.

3.2 Practices to Avoid

  • Avoid insecure password verification or missing session management.

Example: Missing Login Manager

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///wrong.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    user = User.query.filter_by(username=username).first()
    if user and user.password == password:
        return "Logged in"  # No session management
    return "Invalid credentials"

with app.app_context():
    db.create_all()

Output:

Logged in (but no session tracking)
  • Lacks session management and secure password storage.
  • Solution: Use Flask-Login and Flask-Bcrypt.

04. Common Use Cases in Web Development

4.1 Secure Blog Access

Authenticate users to access blogging features.

Example: Blog Login

from flask import Flask, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required
from flask_bcrypt import Bcrypt

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)
    posts = db.relationship('Post', backref='author', lazy=True)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

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

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    user = User.query.filter_by(username=username).first()
    if user and bcrypt.check_password_hash(user.password_hash, password):
        login_user(user)
        return redirect(url_for('dashboard'))
    return "Invalid credentials"

@app.route('/dashboard')
@login_required
def dashboard():
    return "Blog dashboard"

with app.app_context():
    db.create_all()

Output:

Blog dashboard (on valid login)

Explanation:

  • Integrates with Flask Relationships in Models to link users to posts.
  • Secures access to blogging features.

4.2 E-commerce User Login

Authenticate users to manage orders and profiles.

Example: E-commerce Login

from flask import Flask, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required
from flask_bcrypt import Bcrypt

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///ecommerce.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)
    orders = db.relationship('Order', backref='customer', lazy=True)

class Order(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    item = db.Column(db.String(100), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

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

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    user = User.query.filter_by(username=username).first()
    if user and bcrypt.check_password_hash(user.password_hash, password):
        login_user(user)
        return redirect(url_for('orders'))
    return "Invalid credentials"

@app.route('/orders')
@login_required
def orders():
    return "Your orders"

with app.app_context():
    db.create_all()

Output:

Your orders (on valid login)

Explanation:

  • Links authenticated users to their orders via relationships.
  • Secures access to e-commerce features.

Conclusion

Flask user login and logout, powered by Flask-Login, Flask-Bcrypt, and integration with Flask Authentication and NumPy Array Operations, provide a secure and scalable way to manage user sessions. Key takeaways:

  • Use Flask-Login for session management and Flask-Bcrypt for password verification.
  • Protect routes with login_required and redirect users appropriately.
  • Apply login/logout in blogs, e-commerce, or dashboards.
  • Avoid insecure practices like plain-text passwords or missing session management.

With these techniques, you can build secure Flask applications that provide seamless and protected user experiences!

Comments