Belajar Chi - Autentikasi & Otorisasi
Series/Belajar Chi/Episode 13
Episode 13 of 23

Belajar Chi - Autentikasi & Otorisasi

Episode ini membangun lapisan autentikasi dan otorisasi: JWT dengan golang-jwt sebagai middleware, BasicAuth bawaan chi, session cookie, serta pola refresh token. Kalian juga belajar middleware RBAC berbasis peran dan integrasi OAuth2 dan OpenID Connect dengan Keycloak.

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

Pendahuluan

Autentikasi menjawab "siapa kalian"; otorisasi menjawab "apa yang boleh kalian lakukan". Episode 13 membangun keduanya di atas chi dengan pola yang umum dipakai di production: JWT untuk API token, Basic Auth untuk admin sederhana, session cookie untuk web, dan RBAC untuk kontrol berbasis peran. Karena semua mekanisme ini adalah middleware func(http.Handler) http.Handler, kalian bisa menempelkannya ke seluruh router, subrouter, atau satu route saja — persis pola yang sudah dipelajari di episode 6.

JWT sebagai Middleware

Membangkitkan Token

Install golang-jwt
go get github.com/golang-jwt/jwt/v5
Membuat token JWT
func issueToken(userID int, role string) (string, error) {
    claims := jwt.MapClaims{
        "sub":  userID,
        "role": role,
        "exp":  time.Now().Add(time.Hour).Unix(),
    }
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString([]byte(jwtSecret))
}

jwt.NewWithClaims(jwt.SigningMethodHS256, claims) membuat token yang memuat subjek (user ID), role, dan waktu kedaluwarsa. token.SignedString(...) menandatanganinya dengan secret.

Middleware Verify

JWT verify middleware
func authenticate(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        header := req.Header.Get("Authorization")
        tokenStr, _ := strings.CutPrefix(header, "Bearer ")
 
        token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (any, error) {
            if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, jwt.ErrSignatureInvalid
            }
            return []byte(jwtSecret), nil
        })
        if err != nil || !token.Valid {
            http.Error(w, "token tidak valid",
                http.StatusUnauthorized)
            return
        }
        next.ServeHTTP(w, req)
    })
}

strings.CutPrefix(header, "Bearer ") melepas skema Bearer dari header. Middleware menolak request dengan 401 Unauthorized bila token rusak atau kedaluwarsa.

BasicAuth Bawaan

Autentikasi Sederhana

Untuk area internal atau admin, middleware.BasicAuth cukup:

Basic auth bawaan
r.Route("/admin", func(admin chi.Router) {
    admin.Use(middleware.BasicAuth("chi-service",
        map[string]string{"admin": "s3cret"}))
    admin.Get("/stats", statsHandler)
})

middleware.BasicAuth("chi-service", map[string]string{...}) memvalidasi username dan password dari header Authorization. Seluruh route di dalam subrouter /admin terlindungi.

Untuk aplikasi web, simpan identitas di cookie yang di-tandatangani:

Set session cookie
func loginHandler(w http.ResponseWriter, req *http.Request) {
    token, _ := issueToken(userID, role)
    http.SetCookie(w, &http.Cookie{
        Name:  "session",
        Value: token,
        Path:  "/",
        HttpOnly: true,
        Secure:   true,
        SameSite: http.SameSiteStrictMode,
    })
    writeJSON(w, http.StatusOK, map[string]string{"login": "sukses"})
}

http.Cookie{HttpOnly: true, Secure: true} mencegah akses dari JavaScript dan hanya dikirim lewat HTTPS. Cookie dibaca di middleware untuk mengambil token tanpa header manual.

Refresh Token

Access token berumur pendek (misal 15 menit) dan refresh token berumur panjang disimpan aman:

Endpoint refresh
r.Post("/auth/refresh", func(w http.ResponseWriter, req *http.Request) {
    newAccess, _ := issueToken(userID, role)
    writeJSON(w, http.StatusOK,
        map[string]string{"access_token": newAccess})
})

r.Post("/auth/refresh", Handle(...)) menerbitkan access token baru setelah refresh token terverifikasi — pola ini meminimalkan waktu token valid bocor bila terjadi kebocoran.

RBAC Middleware

Otorisasi Berbasis Peran

RBAC middleware
u, ok := currentUser(req)
if !ok || u.Role != role {
    http.Error(w, "akses ditolak", http.StatusForbidden)
    return
}

requireRole("admin") membungkus route yang hanya boleh diakses admin. Middleware ini dipasang setelah authenticate, sehingga user sudah teridentifikasi sebelum dicek perannya.

OAuth2 dan OpenID Connect

Integrasi Keycloak

Untuk SSO enterprise, chi bisa menerima token dari identity provider:

Verifikasi token dari Keycloak
jwksURL := "https://auth.example.com/realms/myrealm/protocol/openid-connect/certs"
 
provider, _ := oidc.NewProvider(context.Background(), jwksURL)
verifier := provider.Verifier(&oidc.Config{
    ClientID: "chi-service",
})

provider.Verifier(...) memvalidasi signature JWT terhadap kunci publik dari Keycloak. ID token yang valid otomatis membawa klaim sub, email, dan scope untuk otorisasi lanjutan.

Scope di dalam Token

Klaim scope membawa izin granular:

Cek scope
scopes := strings.Fields(tokenScopes(req))
if !slices.Contains(scopes, scope) {
    http.Error(w, "scope tidak dimiliki",
        http.StatusForbidden)
    return
}

slices.Contains(scopes, scope) memeriksa apakah token memiliki scope yang diminta. Pola scopes membuat API bisa diakses klien dengan izin berbeda-beda.

Penutup

Inti yang harus dibawa pulang:

  • JWT dibangkitkan dengan golang-jwt dan diverifikasi sebagai middleware.
  • middleware.BasicAuth cukup untuk area admin sederhana.
  • Session cookie dipasang dengan HttpOnly dan Secure.
  • Refresh token memperpendek masa hidup access token.
  • requireRole dan requireScope menambah lapisan otorisasi.
  • OIDC dari Keycloak divalidasi lewat JWKS public keys.

Di episode 14 selanjutnya kita memperkuat pertahanan: security middleware dan input hardening — CORS dengan rs/cors, security headers CSP dan HSTS, CSRF, body limit, rate limiting, validasi input, serta pencegahan injection dan SSRF dengan penanganan X-Forwarded-For yang aman.

Belajar Chi - Autentikasi & Otorisasi | Belajar Chi