Mengaktifkan sistem autentikasi Django: model User bawaan, alur login/logout, permissions & groups, serta implementasi RBAC. Membangun custom user model sebagai best practice sejak awal, lengkap dengan praktik login + role-based access control di blog devblog.

Form di episode 9 sudah menerima input, tetapi belum tahu siapa yang mengirim. Di episode ini kita menghidupkan Authentication & User Management: siapa pengguna, bagaimana mereka login, dan apa yang boleh mereka lakukan. Django menyediakan sistem auth lengkap di django.contrib.auth — model User, hashing password, session, permission, dan group.
Mengapa topik ini penting? Autentikasi adalah garis batas antara "sistem terbuka" dan "sistem yang bisa diandalkan". Instalasi auth Django yang benar menentukan kualitas keamanan seluruh aplikasi, dan keputusan custom user model yang diambil sekarang akan menentukan fleksibilitas sistem di masa depan.
Django punya model User bawaan di django.contrib.auth dengan field esensial: username, password (selalu hash, tidak pernah plaintext), email, first_name, last_name, is_active, is_staff, is_superuser, plus relasi groups dan user_permissions.
python manage.py shell -c "from django.contrib.auth.models import User; print(User.objects.first())"Password disimpan sebagai hash dengan algoritma yang dapat di-upgrade (default PBKDF2, bisa diarahkan ke Argon2). Django tidak pernah menyimpan password asli — karena itu form reset password butuh alur tersendiri (episode 13 memakai email async).
Ini poin paling penting di episode ini: Django merekomendasikan custom user model sejak project pertama dibuat, bahkan jika tidak butuh field tambahan. Alasannya: mengganti AUTH_USER_MODEL setelah migrasi pertama adalah operasi sangat menyakitkan (episode 8 sudah kita singgung). Custom user model membuat penggantian nanti tinggal konfigurasi, bukan migrasi data.
Buat app accounts dan definisikan custom user:
python manage.py startapp accountsfrom django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
bio = models.TextField(blank=True)
website = models.URLField(blank=True)
def __str__(self):
return self.usernameLalu beri tahu Django bahwa User di accounts adalah model auth:
AUTH_USER_MODEL = "accounts.User"Warning
AUTH_USER_MODEL hanya bisa diubah sebelum migrasi pertama dijalankan. Setelah database dibuat dengan user model default, mengganti model berarti operasi migrasi yang sangat kompleks. Untuk lab devblog, kita sudah beruntung karena belum ada migrasi auth yang diterapkan ke produksi — ubah sekarang sebelum terlambat.
Jangan lupa mendaftarkan admin custom user agar bisa dikelola dari panel episode 8:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
@admin.register(User)
class CustomUserAdmin(UserAdmin):
fieldsets = UserAdmin.fieldsets + (
("Info Tambahan", {"fields": ("bio", "website")}),
)View login/logout bisa memakai bawaan Django atau versi custom. Untuk fleksibilitas, kita tulis sendiri dengan authenticate dan login:
from django.contrib.auth import authenticate, login
from django.shortcuts import redirect, render
def login_view(request):
if request.method == "POST":
username = request.POST["username"]
password = request.POST["password"]
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect("blog:home")
return render(request, "accounts/login.html", {"error": "Kredensial salah."})
return render(request, "accounts/login.html")authenticate memverifikasi kredensial (dan menjalankan backend auth), login menulis session. Alur logout dengan method POST (bukan link GET) untuk mencegah CSRF logout:
from django.contrib.auth import logout
from django.shortcuts import redirect
def logout_view(request):
if request.method == "POST":
logout(request)
return redirect("blog:home")Tip
Selalu gunakan POST untuk aksi yang mengubah state (login/logout). Logout lewat link GET rawan CSRF — halaman jahat bisa memaksa browser korban logout. Django's LoginView/LogoutView bawaan sudah melakukan ini dengan benar; memakai versi bawaan adalah pilihan yang aman dan hemat waktu.
Untuk keperluan protection view, Django menyediakan decorator dan mixin:
from django.contrib.auth.decorators import login_required
@login_required
def dashboard(request):
return render(request, "accounts/dashboard.html")@login_required mengalihkan user anonim ke halaman login dan mengembalikan mereka ke halaman tujuan setelah login (?next=). Di template, context processor auth dari episode 5 menyediakan {{ user }}:
{% if user.is_authenticated %}
<p>Halo, {{ user.username }}!</p>
<form method="post" action="{% url 'accounts:logout' %}">
{% csrf_token %}
<button type="submit">Logout</button>
</form>
{% else %}
<a href="{% url 'accounts:login' %}">Login</a>
{% endif %}Django membedakan tiga level otoritas:
is_superuser — akses penuh tak terbatas.is_staff — boleh masuk ke admin (/admin/).blog.can_publish_post.Permission otomatis dibuat dari setiap model: add_, change_, delete_, view_. Cek di shell:
python manage.py shell -c "from django.contrib.auth.models import Permission; print(list(Permission.objects.filter(codename__startswith='post')))"Permissions diberikan langsung ke user atau — lebih rapi — dikelompokkan ke Group. Buat group dan assign lewat script:
from django.contrib.auth.models import Group, Permission
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = "Buat group editor dan beri permission"
def handle(self, *args, **options):
editor, _ = Group.objects.get_or_create(name="editor")
perms = Permission.objects.filter(content_type__app_label="blog")
editor.permissions.set(perms)
self.stdout.write(self.style.SUCCESS("Group editor siap."))Pola "management command" ini bisa dijalankan di CI/provisioning (episode 23). Memberi permission per-group jauh lebih mudah dikelola daripada per-user saat tim bertumbuh.
RBAC (Role-Based Access Control) menggabungkan permission + group. Cek di view:
from django.contrib.auth.decorators import permission_required
@permission_required("blog.change_post", raise_exception=True)
def edit_post(request, pk):
...raise_exception=True mengembalikan 403 (bukan redirect login) bagi user yang sudah login tapi tidak punya izin. Untuk cek permission di template, perms dari context processor episode 5:
{% if perms.blog.change_post %}
<a href="{% url 'blog:edit_post' post.pk %}">Edit</a>
{% endif %}Di view yang memakai mixin CBV, kombinasikan LoginRequiredMixin + PermissionRequiredMixin:
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import UpdateView
class PostUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
model = Post
permission_required = "blog.change_post"
template_name = "blog/post_form.html"Perhatikan: permission model-level (change_post) belum membedakan siapa pemilik objek. Kontrol kepemilikan ("hanya author yang bisa edit post miliknya") disebut object-level permission dan kita implementasikan di episode 20.
Login menulis sessionid di cookie. Alurnya: SessionMiddleware (baca cookie → session data) → AuthenticationMiddleware (session → request.user). Inilah mengapa request.user selalu tersedia di view. Detail session — database, Redis, dan ekspirasi — kita bahas lengkap di episode 12, karena di sanalah performance auth ditentukan.
Inti yang harus dibawa pulang:
User bawaan sudah lengkap (hash password, groups, permissions) — tetapi custom user model sejak awal menghindari migrasi menyakitkan nanti.AUTH_USER_MODEL hanya bisa diganti sebelum migrasi pertama.authenticate + login untuk masuk; POST untuk logout.@login_required / @permission_required / mixin untuk membatasi view; perms di template.Di episode 11 selanjutnya kita menyajikan data blog sebagai API: Django REST Framework (DRF) — Serializers, ViewSets, Routers, autentikasi & permission, pagination, plus REST API CRUD lengkap dengan JWT SimpleJWT. Sampai jumpa di episode 11!