122 Studi Kasus: Sistem Registrasi & Login JWT dengan gqlgen
Membangun sistem autentikasi yang aman dan efisien adalah salah satu pondasi hampir seluruh aplikasi backend modern. Di era API-first development dan microservices, JSON Web Token (JWT) menjadi salah satu solusi authentication yang paling populer. Di kasus kali ini, saya akan membagikan studi kasus membangun sistem registrasi & login menggunakan JWT dengan GraphQL di Go, memanfaatkan library powerful gqlgen.
Kita akan membahas arsitektur, contoh kode, hingga flow diagram alur request – lengkap dengan simulasi login dan proteksi endpoint.
Gambaran Umum Sistem
Sistem yang akan kita buat mencakup fitur berikut:
- Registrasi User
- Login User
- Proteksi Endpoint dengan JWT
Komponen utama yang digunakan:
- Go sebagai backend language
- gqlgen sebagai GraphQL code generator
- gorilla/mux (opsional) sebagai HTTP router
- JWT (github.com/golang-jwt/jwt/v5) untuk pembuatan dan validasi token
- bcrypt untuk hashing password
Database dan Struktur User
Untuk simplicity, kita gunakan in-memory storage berupa map (map[string]*User). Di dunia nyata, Anda bisa dengan mudah menggantinya dengan Postgres atau MongoDB.
type User struct {
ID string
Username string
Password string // Hashed!
}
Secara sederhana, struktur storage-nya:
| Field | Tipe | Keterangan |
|---|---|---|
| ID | string | UUID |
| Username | string | Unique, digunakan login |
| Password | string | Sudah di-hash dengan bcrypt |
Skema GraphQL
Untuk kebutuhan registrasi dan login, cukup dua mutation sederhana:
type User {
id: ID!
username: String!
}
type AuthPayload {
token: String!
user: User!
}
type Mutation {
register(username: String!, password: String!): AuthPayload!
login(username: String!, password: String!): AuthPayload!
}
type Query {
me: User
}
Proses Registrasi
Langkah-langkah:
- Input username & password user.
- Cek apakah username sudah dipakai.
- Hash password: bcrypt.
- Simpan user baru ke database.
- Generate JWT token.
- Kembalikan token & data user.
func (r *mutationResolver) Register(ctx context.Context, username string, password string) (*model.AuthPayload, error) {
if _, exists := users[username]; exists {
return nil, errors.New("username already exists")
}
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
user := &User{
ID: uuid.NewString(),
Username: username,
Password: string(hashed),
}
users[username] = user
token, err := generateJWT(user)
if err != nil {
return nil, err
}
return &model.AuthPayload{
Token: token,
User: &model.User{ID: user.ID, Username: user.Username},
}, nil
}
Proses Login
Simulasinya sangat mirip, hanya tahap validasi password diganti menjadi compare hash.
func (r *mutationResolver) Login(ctx context.Context, username string, password string) (*model.AuthPayload, error) {
user, exists := users[username]
if !exists {
return nil, errors.New("invalid credentials")
}
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
return nil, errors.New("invalid credentials")
}
token, err := generateJWT(user)
if err != nil {
return nil, err
}
return &model.AuthPayload{
Token: token,
User: &model.User{ID: user.ID, Username: user.Username},
}, nil
}
Pembuatan & Validasi JWT
JWT dibuat saat register/login, dan di-validasi setiap kali akses endpoint yang protected.
Func generateJWT():
func generateJWT(user *User) (string, error) {
claims := jwt.MapClaims{
"sub": user.ID,
"username": user.Username,
"exp": time.Now().Add(time.Hour * 24).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(SECRET_KEY))
}
Func parseJWT():
func parseJWT(tokenStr string) (*User, error) {
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
return []byte(SECRET_KEY), nil
})
if err != nil || !token.Valid {
return nil, errors.New("invalid token")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, errors.New("invalid token claims")
}
username := claims["username"].(string)
user, exists := users[username]
if !exists {
return nil, errors.New("user not found")
}
return user, nil
}
Middleware Proteksi Endpoint
Di gqlgen, kita inject user ke context, lalu di resolver tertentu, kita bisa cek authentication.
func AuthMiddleware() func(ctx context.Context) context.Context {
return func(ctx context.Context) context.Context {
token := extractBearerToken(ctx)
if token == "" {
return ctx
}
user, err := parseJWT(token)
if err != nil {
return ctx
}
return context.WithValue(ctx, "user", user)
}
}
Kemudian, misalnya, di query me:
func (r *queryResolver) Me(ctx context.Context) (*model.User, error) {
user := ctx.Value("user")
if user == nil {
return nil, errors.New("unauthenticated")
}
u := user.(*User)
return &model.User{ID: u.ID, Username: u.Username}, nil
}
Diagram Alur
Mari ilustrasikan proses alur registrasi dan login menggunakan Mermaid:
sequenceDiagram
participant Client
participant Server
Client->>Server: register(username, password)
Server->>Server: cek username unik?
alt username sudah ada
Server-->>Client: error
else username OK
Server->>Server: hash password
Server->>Server: simpan user ke DB
Server->>Server: generate JWT
Server-->>Client: {token, user}
end
Client->>Server: login(username, password)
Server->>Server: ambil data user dari DB
alt user tidak ditemukan
Server-->>Client: error
else ada user
Server->>Server: verifikasi hash password
alt invalid credentials
Server-->>Client: error
else sukses
Server->>Server: generate JWT
Server-->>Client: {token, user}
end
end
Simulasi Client
Misal mutation GraphQL yang di-post:
1. Registrasi
mutation {
register(username: "andi", password: "rahasia") {
token
user { id username }
}
}
Respon:
{
"data": {
"register": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI... (JWT)",
"user": { "id": "c780...", "username": "andi" }
}
}
}
2. Login
mutation {
login(username: "andi", password: "rahasia") {
token
user { id username }
}
}
3. Query Data Diri (Proteksi)
Sertakan header: Authorization: Bearer {token}
query {
me { id username }
}
Tabel Perbandingan: REST vs GraphQL + JWT
| Fitur | REST + JWT | GraphQL + JWT |
|---|---|---|
| Endpoint Registrasi/Login | /register, /login | 1 endpoint (/graphql), mutation berbeda |
| Proteksi Auth | Middleware per route | Middleware per field/resolver |
| Extensibilitas | Needs new endpoint | Tambahkan saja di schema |
| Transport | JSON | GraphQL Query |
Catatan Keamanan
- Jangan pernah menyimpan password tanpa hash.
- JWT sebaiknya beri expiry (
exp) yang wajar. - Gunakan HTTPS!
- Validasi input user (XSS, SQLI, dll.)
Penutup
Dengan pattern seperti di atas, Anda bisa deploy aplikasi GraphQL + JWT authentication dengan cepat dan aman, memanfaatkan keunggulan type safety Go plus performa gqlgen. Di dunia nyata, Anda tinggal menambah koneksi ke Postgres atau MongoDB serta menerapkan security yang lebih ketat seperti rate limiting dan monitoring.
Semoga studi kasus sederhana registrasi & login JWT dengan gqlgen ini bisa menjadi fondasi implementasi authentication modern di project Anda berikutnya.
Happy hacking! 🚀
Kode Demo Lengkap:
github.com/youruser/gqlgen-jwt-example (dummy repository, silakan ganti dengan repo sendiri)