Episode ini membangun kontrol akses: JWT dengan middleware.JWT, Basic Auth, session cookie, refresh token, middleware RBAC untuk otorisasi berbasis peran, serta integrasi OAuth2 dan OpenID Connect dengan Keycloak beserta konsep scopes.

Autentikasi menjawab "siapa kamu", otorisasi menjawab "apa yang boleh kamu lakukan". Keduanya adalah garis pertahanan pertama API kalian. Echo menyediakan middleware yang memangkas banyak pekerjaan manual, tapi memahami alurnya tetap wajib.
Episode 13 ini membangun kontrol akses: JWT dengan middleware.JWT, Basic Auth, session cookie, refresh token, middleware RBAC untuk kontrol peran, serta integrasi OAuth2 dan OpenID Connect dengan Keycloak.
middleware.JWT memvalidasi token di setiap request yang kalian lindungi. Token diambil dari header Authorization dalam format Bearer <token>:
e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
SigningKey: []byte(cfg.JWTSecret),
ContextKey: "user",
}))Echo v5 mendukung github.com/golang-jwt/jwt/v5. Saat token valid, klaim token disimpan di context dengan key user dan bisa diambil lewat c.Get("user").
Buat token saat login, lalu baca klaim di handler:
func loginHandler(c echo.Context) error {
claims := jwt.MapClaims{
"sub": user.ID,
"role": user.Role,
"exp": time.Now().Add(time.Hour).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signed, err := token.SignedString([]byte(cfg.JWTSecret))
if err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{"token": signed})
}Di handler lain, ambil identitas user:
func profileHandler(c echo.Context) error {
user := c.Get("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
role := claims["role"].(string)
return c.JSON(http.StatusOK, map[string]string{"role": role})
}c.Get("user") mengambil token yang sudah divalidasi middleware JWT. Klaim seperti role tersedia untuk keputusan otorisasi.
Untuk service internal atau endpoint administrasi, Basic Auth cukup:
admin := e.Group("/admin", middleware.BasicAuth(func(user, pass string, c echo.Context) (bool, error) {
return user == "admin" && pass == cfg.AdminPassword, nil
}))middleware.BasicAuth membandingkan kredensial dari header. Ingat: Basic Auth mengirim kredensial dalam bentuk base64 — hanya aman di atas HTTPS.
Untuk aplikasi web klasik, session berbasis cookie adalah pilihan alami. Simpan session ID di cookie, dan datanya di store (memori, database, atau Redis):
c.SetCookie(&http.Cookie{
Name: "session",
Value: sessionID,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
})HttpOnly mencegah JavaScript membaca cookie, Secure memastikan hanya dikirim lewat HTTPS, dan SameSiteStrictMode mencegah serangan CSRF.
Access token berumur pendek; refresh token berumur panjang digunakan untuk mendapatkan access token baru:
func refreshHandler(c echo.Context) error {
refresh := c.Get("user").(*jwt.Token)
claims := refresh.Claims.(jwt.MapClaims)
sub := claims["sub"].(string)
newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": sub,
"exp": time.Now().Add(time.Hour).Unix(),
})
signed, err := newToken.SignedString([]byte(cfg.JWTSecret))
if err != nil {
return err
}
return c.JSON(http.StatusOK, map[string]string{"token": signed})
}Simpan refresh token di tempat aman (misal di penyimpanan server), batasi masa berlakunya, dan beri kemampuan revoke bila dicurigai bocor.
JWT sudah membawa peran; kini buat middleware yang memeriksa peran sebelum handler berjalan:
func requireRole(roles ...string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
user := c.Get("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
role := claims["role"].(string)
for _, r := range roles {
if role == r {
return next(c)
}
}
return echo.NewHTTPError(http.StatusForbidden, "peran tidak diizinkan")
}
}
}Pasang middleware ini di route yang butuh hak khusus:
adminOnly := e.Group("/admin")
adminOnly.Use(requireRole("admin"))
adminOnly.DELETE("/users/:id", deleteUser)Route di group /admin hanya bisa diakses user dengan peran admin; lainnya mendapat 403.
Untuk ekosistem enterprise, serahkan autentikasi ke Identity Provider seperti Keycloak lewat OAuth2 dan OpenID Connect. Arahkan middleware JWT Echo ke JWKS Keycloak:
e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
SigningKey: []byte(cfg.KeycloakPublicKey),
Claims: jwt.MapClaims{},
}))Konsep scopes menentukan hak akses yang diberikan: token dari Keycloak membawa claim scope berisi cakupan seperti profile atau email. Middleware RBAC di atas bisa diperluas untuk memeriksa scopes selain peran.
Episode 13 membangun kontrol akses lengkap: JWT dengan middleware.JWT untuk API stateless, Basic Auth untuk kasus sederhana, session cookie untuk aplikasi web, refresh token untuk memperpanjang sesi, middleware RBAC untuk kontrol peran, dan integrasi OAuth2 dengan Keycloak untuk skala enterprise.
Inti yang harus dibawa pulang:
middleware.JWT memvalidasi token dan menyimpan klaim di context.HttpOnly, Secure, dan SameSite.scope.Di episode 14 selanjutnya kita akan membahas security middleware & input hardening — konfigurasi CORS ketat, security headers seperti CSP dan HSTS, CSRF, BodyLimit, rate limiting, validasi input ketat, sanitasi, serta pencegahan injection, SSRF, dan proteksi sensitive headers.