116 Menggunakan Context untuk Autentikasi di gqlgen
Mengelola autentikasi adalah bagian penting dalam membangun API modern, termasuk GraphQL. Di ekosistem Go, gqlgen telah menjadi salah satu library yang sangat populer untuk membangun GraphQL API, karena fleksibilitas dan kemampuannya yang sesuai dengan gaya native Go. Salah satu fitur powerful yang sering digunakan untuk authentikasi di gqlgen adalah penggunaan context.Context. Pada artikel ini, saya akan membedah bagaimana menggunakan Context untuk autentikasi di gqlgen, dilengkapi dengan contoh kode, simulasi alur, beserta best practice yang telah terbukti di lapangan.
Mengapa Context di Go itu Istimewa?
Sebelum masuk ke implementasi, mari kita pahami mengapa context.Context sering digunakan sebagai “kendaraan” utama untuk menyimpan dan membawa data autentikasi pada setiap permintaan.
- Thread-safe: Data pada Context hanya berlaku per-request (bukan global mutable state).
- Hierarki/scoping: Setiap request memiliki context sendiri, yang bisa di-extend (turunan).
- Cancelation-aware: Context bisa digunakan untuk membatalkan permintaan (timeout/token).
- Transparansi: Data bisa diambil dari mana saja selama punya referensi context.
Inilah mengapa, pada GraphQL API di Go—termasuk ketika menggunakan gqlgen—Context adalah tempat terbaik untuk menyimpan data user yang sudah diverifikasi, tanpa harus mengorbankan keamanan dan concurrency.
Bagaimana Alur Autentikasi di gqlgen?
Secara umum, proses autentikasi di gqlgen dengan Context berjalan sebagaimana berikut:
- Client mengirim query/mutasi beserta token (biasanya JWT) di header HTTP
- Server middleware membaca Authorization Header
- Middleware memverifikasi token dan menaruh data user ke context
- Resolver mengambil user dari context dan melanjutkan processing
Mari kita ilustrasikan alur ini dengan diagram Mermaid:
sequenceDiagram
participant Client
participant Server(Middleware)
participant Application(Resolver)
Client->>Server: Kirim Query + Header Authorization
Server->>Server: Validasi & parsing JWT
alt Token Valid
Server->>Server: Taruh user ke Context
Server->>Application: Panggil Resolver (dengan context)
Application->>Application: Ambil user dari Context
Application-->>Client: Kirim hasil
else Token Tidak valid
Server-->>Client: Kirim error Unauthorized
end
Step-by-Step Implementasi
Langkah implementasi bisa kita bagi menjadi tiga bagian utama:
- Membuat Middleware untuk Autentikasi
- Menyimpan & Mengambil User di Context
- Mengakses User di Resolver
Mari kita bedah satu per satu.
1. Membuat Middleware Autentikasi (HTTP Layer)
Pada umumnya, GraphQL endpoint di-serve lewat HTTP handler seperti mux, gin, atau bahkan net/http standar. Di sini kita pasang middleware yang akan melakukan check Authorization header.
Contoh implementasi middleware sederhana dengan net/http:
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tokenString := r.Header.Get("Authorization")
if tokenString == "" {
http.Error(w, "missing token", http.StatusUnauthorized)
return
}
// Misal: verifikasi JWT dan extraction user ID/email
user, err := verifyAndExtractUser(tokenString)
if err != nil {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
// Simpan ke context
ctx := context.WithValue(r.Context(), "currentUser", user)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
})
}
Disini, kita membaca JWT dari header, memverifikasinya dengan fungsi (misal) verifyAndExtractUser, dan menyimpan objek user ke context request. Selanjutnya, HTTP handler yang membungkus gqlgen akan menerima context yang sudah ada currentUser.
Tabel simulasi bagaimana Context terbentuk:
| Step | Context State |
|---|---|
| Request masuk | Kosong |
| Auth header dibaca | Kosong |
| JWT diverifikasi | - |
| User ditemukan | Ada currentUser |
| Handler diteruskan | Ada currentUser |
2. Menyimpan dan Mengakses User di Context gqlgen
Di gqlgen, setiap resolver akan menerima argument pertama yaitu context.
Definisikan terlebih dahulu tipe user yang kita simpan di context:
// models/user.go
type User struct {
ID string
Email string
Role string
}
Untuk mengambil data user dari context, kita buat helper function:
// auth/context.go
type contextKey string
const userCtxKey = contextKey("currentUser")
func ForContext(ctx context.Context) *User {
raw, ok := ctx.Value(userCtxKey).(*User)
if !ok {
return nil
}
return raw
}
Pada middleware, kita simpan dengan key yang sama:
ctx := context.WithValue(r.Context(), userCtxKey, user)
3. Mengakses User di Resolver gqlgen
Pada waktu implementasi resolver, kita cukup ambil user dari context:
func (r *queryResolver) Me(ctx context.Context) (*model.User, error) {
user := auth.ForContext(ctx)
if user == nil {
return nil, errors.New("unauthorized")
}
return user, nil
}
Dengan demikian, setiap resolver apapun bisa mengambil informasi user dari Context tanpa harus tahu mekanisme autentikasi di bawahnya. Separation of concerns terjaga sangat baik.
Penambahan: Role-based Authorization
Setelah user didapat di Context, kita bisa extend skenario menjadi authorization berbasis role, masih menggunakan Context sebagai sumber user.
func (r *mutationResolver) DeleteUser(ctx context.Context, userId string) (bool, error) {
user := auth.ForContext(ctx)
if user == nil || user.Role != "admin" {
return false, errors.New("forbidden")
}
// lanjutkan proses
...
}
Tabel Simulasi Respon API
Mari kita lihat beberapa skenario simulasi:
| Token | Middleware | Resolver | Output |
|---|---|---|---|
| Kosong / tidak ada | Unauthorized | - | 401 Unauthorized |
| Invalid format | Unauthorized | - | 401 Unauthorized |
| Valid JWT, user tidak ditemukan DB | Unauthorized | - | 401 Unauthorized |
| Valid JWT, user normal | OK | user != nil | Data user |
| Valid JWT, user role != admin | OK | Forbidden | 403 Forbidden |
| Valid JWT, user role = admin | OK | Lanjut/OK | Success |
Best Practices & Tips
- Simpan data minimal di Context (cukup ID/email/role), untuk efisiensi dan keamanan.
- Gunakan type-safe key untuk context, hindari collision dengan key string bebas.
- Pisahkan concerns: Middleware untuk autentikasi, resolver untuk authorization/business logic.
- Log aktivitas login secara terpusat, jika perlu trace.
- Kombinasi dengan dataloaders jika ingin efisien fetch data user.
Penutup
Menggunakan Context untuk autentikasi di gqlgen adalah metode yang idiomatic, aman, dan scalable di ekosistem Go. Pattern ini sudah sangat battle-tested, dan scalable dari single instance, microservices, hingga monolith playground.
Semoga dengan contoh kode, simulasi, dan penjelasan di atas, kamu bisa mengimplementasikan GraphQL API Go dengan autentikasi kuat dan clean separation of concerns. Jika ada pertanyaan atau pengalaman menarik, jangan ragu untuk share di komentar!
Happy hacking 🚀