Belajar Flask - Authentication Lanjutan: JWT & OAuth
Episode 18 of 27

Belajar Flask - Authentication Lanjutan: JWT & OAuth

Mengamankan API modern dengan Flask-JWT-Extended: access token dan refresh token, RBAC untuk kontrol akses berbasis peran, serta social login dengan Authlib OAuth untuk autentikasi via Google dan GitHub yang dikonsumsi SPA dan mobile.

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

Pendahuluan

Session-based auth (episode 13) cocok untuk halaman web klasik, tetapi API yang dikonsumsi SPA dan mobile butuh pendekatan berbeda: token. Episode 18 membahas JWT (JSON Web Token) dengan Flask-JWT-Extended — access token dan refresh token, kontrol akses berbasis peran (RBAC) — lalu menutup dengan OAuth social login menggunakan Authlib. Prinsipnya: token stateless (server tidak menyimpan session), dikirim client di header Authorization, dan bisa diverifikasi tanpa database query per request.

Setup Flask-JWT-Extended

Install dan konfigurasi:

Install Flask-JWT-Extended
pip install flask-jwt-extended
Pythonapp/__init__.py - init JWT
from flask_jwt_extended import JWTManager
 
jwt = JWTManager()
 
 
def create_app(config) -> Flask:
    app = Flask(__name__)
    app.config.from_object(config)
    jwt.init_app(app)
    return app

Praktik penting pada konfigurasi: access token pendek (15 menit) sehingga jika bocor, jendela penyalahgunaannya sempit; refresh token panjang untuk memperoleh access token baru. Set JWT_SECRET_KEY kuat dari environment (episode 8), serta JWT_ACCESS_TOKEN_EXPIRES dan JWT_REFRESH_TOKEN_EXPIRES sesuai kebijakan.

Login: Membuat Access dan Refresh Token

Pythonroutes/auth.py - login JWT
from flask_jwt_extended import create_access_token, create_refresh_token
 
 
@api_bp.post("/auth/login")
def login() -> tuple[dict, int]:
    data = request.get_json(silent=True) or {}
    user = User.query.filter_by(username=data.get("username")).first()
    if not user or not user.check_password(data.get("password", "")):
        return api_error("kredensial salah", 401)
 
    access_token = create_access_token(identity=str(user.id))
    refresh_token = create_refresh_token(identity=str(user.id))
    return api_ok({"access_token": access_token, "refresh_token": refresh_token})

Access Token: Melindungi Endpoint

Endpoint API dilindungi dengan @jwt_required():

PythonEndpoint terproteksi JWT
from flask_jwt_extended import jwt_required, get_jwt_identity
 
 
@api_bp.get("/users/me")
@jwt_required()
def me() -> tuple[dict, int]:
    user_id = get_jwt_identity()
    user = db.session.get(User, int(user_id))
    return api_ok({"id": user.id, "username": user.username, "email": user.email})

@jwt_required() memeriksa dan memvalidasi access token; get_jwt_identity() mengembalikan identitas (user id) yang disimpan saat login. Token tidak valid/kadaluarsa otomatis menghasilkan 401.

Refresh Token: Memperbarui Akses

Saat access token kadaluarsa, client meminta yang baru dengan refresh token:

PythonEndpoint refresh
from flask_jwt_extended import jwt_required, create_access_token
 
 
@api_bp.post("/auth/refresh")
@jwt_required(refresh=True)
def refresh() -> tuple[dict, int]:
    user_id = get_jwt_identity()
    access_token = create_access_token(identity=user_id)
    return api_ok({"access_token": access_token})

@jwt_required(refresh=True) menerima refresh token, bukan access token. Dengan pola ini, client otomatis menyegarkan access token tanpa login ulang selama refresh token belum kadaluarsa.

RBAC: Kontrol Akses Berbasis Peran

Token bisa membawa klaim peran untuk kontrol akses. Di endpoint login, tambahkan klaim saat membuat token:

PythonLogin dengan klaim peran
access_token = create_access_token(
    identity=str(user.id),
    additional_claims={"role": user.role},   # "admin" | "user"
)

Lalu verifikasi peran di endpoint yang butuh hak admin:

PythonEndpoint khusus admin
from functools import wraps
from flask_jwt_extended import get_jwt
 
 
def admin_required(f):
    @wraps(f)
    @jwt_required()
    def wrapper(*args, **kwargs):
        claims = get_jwt()
        if claims.get("role") != "admin":
            return api_error("butuh hak admin", 403)
        return f(*args, **kwargs)
    return wrapper

Note

Ingat bahwa JWT adalah stateless — pencabutan (revocation) tidak instan seperti menghapus session. Jika user harus dicabut aksesnya seketika (akun di-ban), gunakan daftar hitam (JWT blocklist) yang dicek @jwt_required, atau pakai session-based auth untuk kasus yang butuh revoke cepat. Flask-JWT-Extended menyediakan blocklist hooks.

OAuth: Social Login dengan Authlib

Untuk login "via Google/GitHub", aplikasi memakai OAuth 2.0: aplikasi meminta akses atas nama user tanpa melihat password-nya. Authlib menyederhanakannya:

Install Authlib
pip install authlib
PythonSetup OAuth provider
from authlib.integrations.flask_client import OAuth
 
oauth = OAuth()
 
oauth.register(
    name="google",
    client_id=os.environ.get("GOOGLE_CLIENT_ID"),
    client_secret=os.environ.get("GOOGLE_CLIENT_SECRET"),
    server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
    client_kwargs={"scope": "openid email profile"},
)

Alur loginnya:

PythonAlur OAuth
@auth_bp.get("/login/google")
def google_login():
    return oauth.google.authorize_redirect(url_for("auth.google_callback", _external=True))
 
 
@auth_bp.get("/callback/google")
def google_callback():
    token = oauth.google.authorize_access_token()
    userinfo = oauth.google.userinfo()
    user = find_or_create_user(userinfo["email"], userinfo["name"])
    login_user(user)
    return redirect(url_for("main.index"))

User yang sudah terverifikasi oleh Google tidak perlu password lagi di aplikasi kalian — alur ini memakai OpenID Connect (OAuth 2.0 + profile). Client ID/secret dari penyedia (Google, GitHub) di-set sebagai environment variable (episode 8) — jangan pernah di-commit.

Common Pitfalls JWT & OAuth

  • Access token lama sekali kadaluarsa: kebocoran = jendela panjang — pakai 15 menit + refresh.
  • JWT_SECRET_KEY sama dengan SECRET_KEY lemah: kunci harus acak & rahasia (episode 8).
  • RBAC hanya di frontend: otorisasi selalu dicek di server per endpoint.
  • OAuth callback tanpa _external=True: URL callback salah di produksi.
  • Client ID/secret di source code: wajib environment variable.

Penutup

Pada episode 18 ini, kalian telah mengamankan API dengan JWT dan OAuth.

Inti yang harus dibawa pulang:

  • JWT stateless: access token pendek (15 menit) + refresh token panjang.
  • @jwt_required() melindungi endpoint; get_jwt_identity() mengenali user.
  • RBAC via additional_claims + verifikasi get_jwt() per endpoint.
  • JWT tidak bisa di-revoke instan — pakai blocklist jika butuh.
  • Jangan simpan access token di localStorage.

Di episode 19 selanjutnya, kita menangani salah satu fitur paling rawan: upload, file & content security — validasi file, secure filenames, batas ukuran, dan praktik aman untuk avatar dan attachment. Sampai jumpa di episode 19!