Belajar Gin - Autentikasi & Otentikasi Session
Series/Belajar Gin/Episode 13
Episode 13 of 23

Belajar Gin - Autentikasi & Otentikasi Session

Episode ini membangun autentikasi di Gin: BasicAuth middleware, JWT dengan golang-jwt, alur access token dan refresh token, session cookie, serta penyimpanan dan revocation token di Redis.

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

Pendahuluan

Setelah aplikasi bisa mengelola data, pertanyaan berikutnya: siapa pengguna ini dan apakah ia memang dia? Episode 13 ini membedah autentikasi di Gin — memverifikasi identitas pengguna. Kalian akan belajar BasicAuth untuk kasus sederhana, JWT dengan golang-jwt untuk API token-based, alur access token dan refresh token yang sehat, session cookie untuk aplikasi web, serta penyimpanan dan revocation token di Redis.

Autentikasi bukan tempat untuk menemukan ulang roda. Pola yang kalian pelajari di sini adalah pola industri yang sudah teruji: token berumur pendek untuk akses, token berumur panjang yang bisa dicabut untuk refresh. Episode 14 akan melanjutkan ke otorisasi (siapa boleh melakukan apa).

BasicAuth dan Kapan Memakainya

Middleware Bawaan Gin

Gin menyediakan middleware BasicAuth untuk kredensial sederhana:

BasicAuth bawaan
accounts := gin.Accounts{
    "arman": "rahasia123",
}
 
r.GET("/admin", gin.BasicAuth(accounts), func(c *gin.Context) {
    user := c.MustGet(gin.AuthUserKey).(string)
    c.JSON(200, gin.H{"user": user})
})

gin.BasicAuth(accounts) menolak request tanpa header Authorization yang valid. Username tervalidasi bisa dibaca lewat c.MustGet(gin.AuthUserKey). BasicAuth cocok untuk tooling internal, health check, atau tahap prototyping — bukan untuk aplikasi publik, karena kredensial bisa dengan mudah di-extract dari header.

JWT dengan golang-jwt

Membuat Token

Untuk API berbasis token, JWT adalah standar de facto. Install dan tanda tangani token HS256:

Install golang-jwt
go get github.com/golang-jwt/jwt/v5
Membuat access token
func generateAccessToken(userID int64) (string, error) {
    claims := jwt.MapClaims{
        "sub": userID,
        "iat": time.Now().Unix(),
        "exp": time.Now().Add(15 * time.Minute).Unix(),
    }
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(secret)
}

jwt.NewWithClaims(jwt.SigningMethodHS256, claims) membangun token dengan klaim sub (subject/user id) dan exp (expiry). SignedString(secret) menandatanganinya dengan secret rahasia. Simpan secret di environment, bukan di kode — kembali ke praktik episode 10.

Middleware Autentikasi

Bungkus verifikasi dalam middleware yang menyimpan identitas di context:

Middleware JWT
func authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        raw := c.GetHeader("Authorization")
        if !strings.HasPrefix(raw, "Bearer ") {
            c.AbortWithStatusJSON(401, gin.H{"error": "token wajib"})
            return
        }
        token, err := parseToken(strings.TrimPrefix(raw, "Bearer "))
        if err != nil || !token.Valid {
            c.AbortWithStatusJSON(401, gin.H{"error": "token tidak valid"})
            return
        }
        claims := token.Claims.(jwt.MapClaims)
        c.Set("user_id", int64(claims["sub"].(float64)))
        c.Next()
    }
}

c.GetHeader("Authorization") membaca token dari header. Setelah valid, c.Set("user_id", ...) menyimpan identitas yang bisa dibaca handler berikutnya dengan c.Get("user_id") — persis pola yang dipakai middleware di episode 6.

Access Token dan Refresh Token

Alur Dua Token

Access token berumur pendek mengurangi risiko jika bocor. Refresh token berumur panjang dipakai hanya untuk mendapatkan access token baru:

Endpoint login dan refresh
r.POST("/login", loginHandler)
r.POST("/refresh", refreshHandler)
 
func refreshHandler(c *gin.Context) {
    var req struct {
        RefreshToken string `json:"refresh_token" binding:"required"`
    }
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    // validasi refresh token, lalu:
    access, err := generateAccessToken(userID)
    if err != nil {
        c.JSON(500, gin.H{"error": "gagal membuat token"})
        return
    }
    c.JSON(200, gin.H{"access_token": access})
}

Alur refreshHandler memverifikasi refresh token yang dikirim klien, memastikan masih terdaftar, lalu menerbitkan access token baru. Klien memakai access token untuk request API dan menyimpan refresh token di tempat yang lebih aman.

Untuk aplikasi web klasik, session bisa disimpan dalam cookie yang terverifikasi:

Set dan baca session cookie
func loginHandler(c *gin.Context) {
    // verifikasi kredensial...
    sessionID := newSessionID()
    c.SetCookie("session", sessionID, 3600, "/", "", true, true)
    c.JSON(200, gin.H{"message": "login berhasil"})
}
 
func profileHandler(c *gin.Context) {
    sessionID, err := c.Cookie("session")
    if err != nil {
        c.JSON(401, gin.H{"error": "belum login"})
        return
    }
    userID, ok := sessionStore.Get(sessionID)
    if !ok {
        c.JSON(401, gin.H{"error": "session kadaluwarsa"})
        return
    }
    c.JSON(200, gin.H{"user_id": userID})
}

c.SetCookie("session", sessionID, 3600, "/", "", true, true) menulis cookie dengan flag Secure (hanya lewat HTTPS) dan HttpOnly (tidak bisa dibaca JavaScript). Nilai cookie hanyalah pengenal; data session disimpan di server (misalnya Redis), bukan di cookie — ini mencegah tamper.

Revocation dan Penyimpanan Token di Redis

Menyimpan Refresh Token di Redis

Refresh token harus bisa dicabut. Simpan di Redis dengan TTL sama dengan umur token:

Install go-redis
go get github.com/redis/go-redis/v9
Simpan refresh token di Redis
ctx := context.Background()
key := fmt.Sprintf("refresh:%d", userID)
 
rdb.Set(ctx, key, tokenHash, 7*24*time.Hour)

rdb.Set(ctx, key, tokenHash, 7*24*time.Hour) menyimpan hash refresh token selama 7 hari. Saat refresh, bandingkan token yang datang dengan nilai di Redis; jika cocok, terbitkan access token baru. Logout cukup menghapus key — token langsung tidak berlaku.

Penutup

Inti yang harus dibawa pulang:

  • gin.BasicAuth untuk kasus sederhana, bukan untuk aplikasi publik.
  • JWT dengan golang-jwt memakai sub dan exp, verifikasi algoritma tanda tangan.
  • Middleware JWT menyimpan identitas lewat c.Set.
  • Access token pendek, refresh token panjang, keduanya terpisah.
  • Session cookie dengan flag Secure dan HttpOnly.
  • Redis menyimpan refresh token dan memungkinkan revocation dengan Del.

Di episode 14 selanjutnya kita akan membedah RBAC, OAuth2 & security middleware — middleware permission dan RBAC, integrasi OAuth2 dan OpenID Connect dengan Keycloak, security headers, CSRF untuk form, serta rate limiting dengan golang.org/x/time/rate.

Belajar Gin - Autentikasi & Otentikasi Session | Belajar Gin