Authentication & Login
Reviewed & published by Brayan K
Authentication is the process of verifying who a user is — confirming a person owns the account they claim — so your app can show private pages, save their data, and keep everyone else out.
Part of the free Flask course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
In this lesson you'll hash passwords securely with Werkzeug, manage user sessions with Flask-Login, and protect routes so only signed-in users can reach them.
What You'll Learn in This Lesson
- Why passwords must be hashed, never stored as plain text
- How to hash and verify passwords with Werkzeug
- Setting up Flask-Login and the LoginManager
- Building register, login, and logout routes
- Using login_user, logout_user, and current_user
- Protecting pages with the @login_required decorator
1 Hashing Passwords Securely
A password hash is a one-way scrambled version of a password. You can check whether a guess matches the hash, but you can never turn the hash back into the original password.
Werkzeug — the library Flask is built on — ships two helpers for this. Use generate_password_hash when a user registers, and check_password_hash when they log in. Never store the raw password.
# Hashing and verifying passwords with Werkzeug
from werkzeug.security import generate_password_hash, check_password_hash
# When a user registers, store the HASH, not the password.
raw_password = "hunter2"
hashed = generate_password_hash(raw_password)
print("Stored in database:", hashed)
# Notice the hash is long and unreadable — that is the point.
# When a user logs in, compare their guess to the stored hash.
print("Correct password:", check_password_hash(hashed, "hunter2"))
print("Wrong password: ", check_password_hash(hashed, "letmein"))2 Setting Up Flask-Login
Flask-Login tracks which user is signed in across requests. You create a LoginManager, mix UserMixin into your model, and tell Flask-Login how to load a user from their stored ID.
# Wiring up Flask-Login with a User model
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin
from werkzeug.security import generate_password_hash
app = Flask(__name__)
app.config["SECRET_KEY"] = "change-this-in-production"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///users.db"
db = SQLAlchemy(app)
# The LoginManager connects Flask-Login to your app.
login_manager = LoginManager(app)
login_manager.login_view = "login" # where to redirect anonymous users
# UserMixin adds is_authenticated, get_id, and friends for free.
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(255), nullable=False)
# Flask-Login calls this to reload a user from the session.
@login_manager.user_loader
def load_user(user_id):
return db.session.get(User, int(user_id))
print("User model and LoginManager configured.")
# ✅ Expected output:
# User model and LoginManager configured.3 Login, Logout & Protected Routes
With the model in place you can write the three core routes. login_user starts a session, logout_user ends it, and @login_required guards any page that needs a signed-in visitor.
# Register, login, logout, and a protected dashboard
from flask import Flask, request, redirect, url_for, render_template_string
from flask_login import login_user, logout_user, login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash
# (db, User, login_manager assumed configured as in section 2)
@app.route("/register", methods=["GET", "POST"])
def register():
if request.method == "POST":
user = User(
username=request.form["username"],
password_hash=generate_password_hash(request.form["password"]),
)
db.session.add(user)
db.session.commit()
return redirect(url_for("login"))
return render_template_string("<form method='post'>...</form>")
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
user = User.query.filter_by(username=request.form["username"]).first()
if user and check_password_hash(user.password_hash, request.form["password"]):
login_user(user) # start the session
return redirect(url_for("dashboard"))
return "Invalid credentials", 401
return render_template_string("<form method='post'>...</form>")
@app.route("/logout")
@login_required
def logout():
logout_user() # end the session
return redirect(url_for("login"))
@app.route("/dashboard")
@login_required # blocks anonymous visitors
def dashboard():
return f"Welcome back, {current_user.username}!"
print("Auth routes ready. Start the server with: flask run")Inside any template or view you can use current_user to read the logged-in user, or check current_user.is_authenticated to show different content to guests and members.
🎯 Your Turn: Fill in the Blanks
Complete the login route below. Replace each ___ so it verifies the password and starts a session.
# 🎯 YOUR TURN: replace each ___ to finish the login route
from flask_login import ___ # the function that starts a session
from werkzeug.security import ___ # the function that verifies a password
@app.route("/login", methods=["GET", "POST"])
def login():
user = User.query.filter_by(username=request.form["username"]).first()
# Check the submitted password against the stored hash
if user and ___(user.password_hash, request.form["password"]):
___(user) # log the user in
return redirect(url_for("dashboard"))
return "Invalid credentials", 401
print("Fill in the blanks to make login work!")Common Errors & Quick Reference
❌ RuntimeError: Missing user_loader or request_loader
You forgot to register a @login_manager.user_loader function. Flask-Login needs it to reload the user from the session on every request.
❌ The session secret key is not set
Set app.config["SECRET_KEY"] to a long random string. Without it, Flask cannot sign the session cookie and login will fail.
| Task | Command / Code |
|---|---|
| Install Flask-Login | pip install flask-login |
| Hash a password | generate_password_hash(pw) |
| Verify a password | check_password_hash(hash, pw) |
| Start a session | login_user(user) |
| Protect a route | @login_required |
🏆 Mini Challenge: A "My Profile" Page
Add a protected profile page that greets the logged-in user by name.
- Create a route at /profile
- Protect it with @login_required
- Return a message using current_user.username
# 🏆 Mini Challenge: a protected profile page
from flask_login import login_required, current_user
@app.route("/profile")
@login_required
def profile():
# Only signed-in users reach this line.
return f"This is the profile of {current_user.username}."
# Anonymous visitors are redirected to login_manager.login_view ("login").
print("Visit /profile while logged in to see your name.")❓ Frequently Asked Questions
Lesson complete — your app can sign users in!
You can now hash passwords with Werkzeug, manage sessions with Flask-Login, and lock pages behind @login_required. That is the foundation of every account-based web app.
Practice quiz
Which Werkzeug function creates a password hash when a user registers?
- generate_password_hash
- hashlib.md5
- encrypt_password
- make_hash
Answer: generate_password_hash. generate_password_hash creates an irreversible hash on registration.
Which function verifies a password guess at login?
- verify_hash
- check_password_hash
- compare_password
- match_hash
Answer: check_password_hash. check_password_hash(hash, guess) verifies the guess against the stored hash.
Why hash passwords instead of storing them plain?
- It saves storage space
- It speeds up login
- If the database leaks, hashes can't be reversed
- Flask requires it
Answer: If the database leaks, hashes can't be reversed. Hashing is irreversible, so a leaked database does not expose passwords.
What does the @login_required decorator do?
- Hashes the password
- Logs all requests
- Creates a session cookie
- Blocks the route unless a user is logged in
Answer: Blocks the route unless a user is logged in. @login_required blocks access and redirects anonymous visitors to the login page.
Which function starts a user session in Flask-Login?
- login_user(user)
- start_session(user)
- authenticate(user)
- session.login(user)
Answer: login_user(user). login_user(user) starts the session.
Which class adds default methods like is_authenticated to your model?
- LoginManager
- UserMixin
- AnonymousUser
- BaseModel
Answer: UserMixin. UserMixin adds is_authenticated, is_active, get_id, and more.
Why must SECRET_KEY be set for Flask-Login?
- To hash passwords
- To name the app
- To sign the session cookie so it can be trusted
- To enable debug mode
Answer: To sign the session cookie so it can be trusted. Flask-Login needs SECRET_KEY to sign the session cookie.
What does @login_manager.user_loader register?
- A logout handler
- A password hasher
- A route guard
- A function to reload the user from the session
Answer: A function to reload the user from the session. user_loader reloads the user from their stored id on each request.
How do you read the currently signed-in user in a view?
- current_user
- session.user
- g.login
- request.user
Answer: current_user. current_user gives the logged-in user (or an anonymous user).
Which function ends a user's session?
- session.clear()
- logout_user()
- end_login()
- current_user.logout()
Answer: logout_user(). logout_user() ends the session.
Continue this course
- Previous: JSON & Serialization
- Next: App Factory & Config — Structure production apps with the application factory pattern and config classes
- Quick reference: Flask cheat sheet
Frequently asked questions
What is Flask-Login?
Flask-Login is an extension that manages user sessions for you. It handles logging users in and out, remembering them across requests, and protecting routes that require a signed-in user.
Why should I hash passwords instead of storing them directly?
Storing plain-text passwords is dangerous — if your database leaks, every account is exposed. Hashing with werkzeug's generate_password_hash turns the password into an irreversible string, so even you cannot read the original.
What does @login_required do?
The @login_required decorator blocks access to a route unless a user is logged in. Anonymous visitors are redirected to your login page automatically.
What is UserMixin used for?
UserMixin is a helper class from Flask-Login that adds the default methods (is_authenticated, is_active, get_id, and so on) your User model needs to work with the login system.