Belajar Flask - Authentication & Sessions
Episode 13 of 27

Belajar Flask - Authentication & Sessions

Membangun autentikasi berbasis session dengan Flask-Login, hashing password memakai Werkzeug generate_password_hash, user model yang aman, dan protected routes dengan @login_required serta mekanisme login dan logout.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Sejauh ini siapa pun bisa mengakses semua halaman dan endpoint aplikasi kita. Untuk aplikasi sungguhan — profil pribadi, dashboard, admin — kalian butuh autentikasi: memastikan siapa pengguna yang membuat request. Episode 13 membangun autentikasi berbasis session dengan Flask-Login: login, logout, dan halaman yang hanya bisa diakses pengguna terautentikasi. Ini juga episode di mana keamanan mulai benar-benar diperhitungkan — terutama password hashing, yang kalau salah berarti data pengguna kalian bocor. (Autentikasi API berbasis JWT/OAuth dibahas di episode 18.)

Password Hashing: Jangan Simpan Password Mentah

Aturan pertama keamanan password: jangan pernah menyimpan password asli, bahkan di database. Simpan hash-nya. Werkzeug menyediakan fungsi yang tepat:

PythonHashing dengan Werkzeug
from werkzeug.security import generate_password_hash, check_password_hash
 
hash_ = generate_password_hash("rahasia123")
# contoh hasil: scrypt:32768:8:1$xxxxxxxx$...
 
check_password_hash(hash_, "rahasia123")   # True
check_password_hash(hash_, "salah123")     # False

generate_password_hash memakai algoritma hash password modern (default Werkzeug 3.x: scrypt) dengan salt acak per user. Ini berarti dua user dengan password sama tetap punya hash berbeda. check_password_hash membandingkan dengan aman (constant-time) sehingga serangan timing tidak bisa dipakai untuk menebak.

Warning

Jangan pernah menulis hashing sendiri (MD5, SHA256 tanpa salt) — algoritma itu bisa di-crack dalam hitungan detik. Gunakan selalu generate_password_hash/check_password_hash dari Werkzeug, atau bcrypt/argon2 dari library khusus. Dan jangan pernah menyimpan password plaintext "untuk sementara" — sekali bocor, seluruh akun terancam.

Memperkuat Model User

User model dari episode 10 perlu empat hal untuk Flask-Login:

Pythonmodels/user.py - user model siap login
from flask_login import UserMixin
from datetime import datetime
from app.extensions import db
 
 
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(255), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
 
    def set_password(self, password: str) -> None:
        self.password_hash = generate_password_hash(password)
 
    def check_password(self, password: str) -> bool:
        return check_password_hash(self.password_hash, password)

UserMixin memberi method bawaan yang dibutuhkan Flask-Login (is_authenticated, get_id). Kolom yang disimpan hanyalah password_hash — bukan password. Method set_password/check_password membungkus hashing agar dipakai konsisten di seluruh aplikasi.

Setup Flask-Login

Install (pip install flask-login) dan daftarkan di factory:

Pythonapp/extensions.py + __init__.py
from flask_login import LoginManager
 
login_manager = LoginManager()
login_manager.login_view = "auth.login"   # redirect jika belum login
login_manager.login_message = "Silakan login dulu"
Pythonapp/__init__.py - init dan user loader
def create_app(config) -> Flask:
    app = Flask(__name__)
    app.config.from_object(config)
 
    db.init_app(app)
    login_manager.init_app(app)
 
    from app.models.user import User
 
    @login_manager.user_loader
    def load_user(user_id: str) -> User | None:
        return db.session.get(User, int(user_id))
 
    return app

@login_manager.user_loader memberitahu Flask-Login cara memuat user dari ID yang tersimpan di session — dipanggil setiap request untuk user yang login.

Registrasi: Buat User dengan Hash

Pythonroutes/auth.py - registrasi
from app.extensions import db
from app.forms import RegisterForm
from app.models.user import User
 
 
@auth_bp.route("/register", methods=["GET", "POST"])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        user = User(username=form.username.data, email=form.email.data)
        user.set_password(form.password.data)   # hash, bukan plaintext
        db.session.add(user)
        db.session.commit()
        return redirect(url_for("auth.login"))
    return render_template("auth/register.html", form=form)

Login dan Logout

Login: verifikasi kredensial, lalu login_user():

Pythonroutes/auth.py - login dan logout
from flask import flash, redirect, url_for
from flask_login import login_user, logout_user
 
 
@auth_bp.route("/login", methods=["GET", "POST"])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user and user.check_password(form.password.data):
            login_user(user, remember=form.remember.data)
            return redirect(url_for("main.dashboard"))
        flash("Username atau password salah")
    return render_template("auth/login.html", form=form)
 
 
@auth_bp.route("/logout")
def logout():
    logout_user()
    return redirect(url_for("main.index"))

login_user(user, remember=True) mengatur session (opsional cookie "remember me" yang tahan lama). logout_user() membersihkan session. Perhatikan pola verifikasi if user and user.check_password(...): objek user bisa None jika username tidak ada — gabungkan dalam satu kondisi agar tidak membocorkan username mana yang terdaftar.

Protected Routes: @login_required

PythonProtected route
@main_bp.get("/dashboard")
@login_required
def dashboard():
    return render_template("main/dashboard.html", user=current_user)

@login_required memeriksa session; jika belum login, pengguna diarahkan ke login_manager.login_view. Di template, current_user selalu tersedia — misalnya untuk menampilkan tombol login/logout sesuai status:

HTMLTemplate memakai current_user
{% if current_user.is_authenticated %}
    <p>Halo, {{ current_user.username }}!</p>
    <a href="{{ url_for('auth.logout') }}">Logout</a>
{% else %}
    <a href="{{ url_for('auth.login') }}">Login</a>
{% endif %}

Session dan Security di Balik Layar

Sekilas bagaimana Flask-Login bekerja: setelah login_user, Flask menyimpan ID user dalam session — cookie yang ditandatangani dengan SECRET_KEY (episode 8). Karena ditandatangani, isinya tidak bisa dimodifikasi penyerang tanpa kunci. Inilah mengapa SECRET_KEY harus kuat dan rahasia — session cookie yang dipalsukan berarti akses ke akun siapa pun. Catatan penting: session-based auth (episode ini) dan JWT-based auth (episode 18) bukanlah musuh — keduanya menyelesaikan masalah berbeda. Session cocok untuk web app dengan halaman HTML; JWT cocok untuk API yang dikonsumsi SPA dan mobile.

Beberapa praktik pengamanan tambahan:

  • is_active pada user: user yang diblokir (active=False) otomatis ditolak oleh Flask-Login.
  • Brute force protection: batasi percobaan login dengan Flask-Limiter (episode 15).
  • Cookie flags: SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SAMESITE, dan Secure (episode 20).

Common Pitfalls Auth & Session

  • Password plaintext di database: pelanggaran paling serius — selalu generate_password_hash.
  • @login_required lupa: setiap route sensitif harus dijaga; jangan hanya mengandalkan sembunyi di URL.
  • Logout tanpa membersihkan session: logout_user() memecah session; untuk kepastian, pertimbangkan session.clear() pada logout penuh.

Penutup

Pada episode 13 ini, kalian telah membangun autentikasi berbasis session yang utuh.

Inti yang harus dibawa pulang:

  • Jangan pernah simpan password mentah — pakai generate_password_hash/check_password_hash Werkzeug.
  • login_user/logout_user mengelola session; @login_required melindungi route.
  • current_user tersedia di template untuk UI yang sadar-keadaan login.

Di episode 14 selanjutnya, kita kembali ke dunia API: REST API design — konvensi REST, JSON request/response, pagination, API versioning, dan pola endpoint JSON yang konsisten untuk dikonsumsi client. Sampai jumpa di episode 14!

Belajar Flask - Authentication & Sessions | Belajar Flask