10 Menambahkan Authentication Sederhana
Pada tahapan ini kita akan mencoba menambahkan Authentication sederhana dengan menggunakan middleware pada Golang. Perlu teman-teman ketahui bahwa middleware adalah proses mencegat suatu API yang mana sebelum service ketika diakses menuju ke dalam handler layer maka akan melewati middleware layer ini (yang akan kita buat) untuk menangkap dan memproses sesuatu untuk kebutuhan tertentu. Misalkan, pada kasus ini kita akan mencekat proses service untuk melihat apakah API tersebut memiliki header dengan ketentuan key X-API-Key.
Maka, middleware yang akan kita buat ini memiliki aturan sebagai berikut ini.
- Melakukan pengecekan apakah memiliki API tersebut memiliki header
X-API-Key - Jika tidak ada, maka akan memberikan informasi error
Unauthorized - Dan jika ada dan sesuai dengan yang sudah ditentukan valuenya yaitu
s3cr3tdan sama, maka proses akan dilanjutkan.
Berikut kurang lebih flow middleware yang akan kita buat.
---
title: Authientication Middleware
---
stateDiagram-v2
[*] --> hasHeader
hasHeader --> unAuthorized
unAuthorized --> [*]
hasHeader --> HeaderXAPIKey
HeaderXAPIKey --> unAuthorized
HeaderXAPIKey --> SuccessContinue
SuccessContinue --> [*]
Pembuatan Chain Middleware
Pertama kita akan buat folder pkg/middleware-chain dengan nama middleware_chain.go. Berikut ini isi dari file tersebut.
1package middleware_chain
2
3import (
4 "net/http"
5
6 "github.com/julienschmidt/httprouter"
7)
8
9// Constructor is type of httprouter handler
10type Constructor func(httprouter.Handle) httprouter.Handle
11
12// Chain is struck for list of middleware
13type Chain struct {
14 constructors []Constructor
15}
16
17// New is for innitial new chain of
18func New(constructors ...Constructor) Chain {
19 return Chain{append(([]Constructor)(nil), constructors...)}
20}
21
22// Then is for http router handler
23func (c Chain) Then(h httprouter.Handle) httprouter.Handle {
24 if h == nil {
25 return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {}
26 }
27 for i := range c.constructors {
28 h = c.constructors[len(c.constructors)-1-i](h)
29 }
30
31 return h
32}
33
34// Append is for add chain router handler
35func (c Chain) Append(constructors ...Constructor) Chain {
36 newCons := make([]Constructor, 0, len(c.constructors)+len(constructors))
37 newCons = append(newCons, c.constructors...)
38 newCons = append(newCons, constructors...)
39
40 return Chain{newCons}
41}1 // inisialisasi http router
2 router := httprouter.New()
3
4 // inisialisasi chain middleware
5 m := middleware_chain.New(
6 // fungsi middleware yang akan kita tambahkan
7 )Membuat Middleware Authentication Sederhana
Pada fungsi yang akan kita buat ini yaitu middleware yang kebutuhannya untuk melakukan Authentication service agar tidak sembarangan orang mengakses service dan data kita sehingga lebih aman.
Sesuai dengan yang sudah dijelaskan diatas proses authentication middleware ini akan kita buat untuk service kita yang mana file-nya akan kita simpan di folder middleware/auth.go dan isi file tersebut dengan kode dibawah ini.
1func AuthenticationBasic(next httprouter.Handle) httprouter.Handle {
2 return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
3 if r.Header.Get(XApiKey) != Secret {
4 var statusCode = http.StatusUnauthorized
5 var response models.HeaderResponse
6 response.Code = statusCode
7 response.Status = "Unauthorized"
8 util.Response(w, response, statusCode)
9 return
10 }
11
12 next(w, r, params)
13 }
14}Sangat simpel karena kita ingin mencoba membuat middleware yang sederhana dan mudah terlebih dahulu agar teman-teman bisa paham proses dari middleware ini.
Mengubah Inisialisasi Router menjadi Fungsi Sendiri
Pada tahapan ini kita akan memisahkan inisialisasi Router API menjadi satu fungsi tersendiri agar mudah mengenal dan tidak terlalu panjang pada saat memiliki endpoint yang banyak mengakibatkan file main.go menjadi panjang dan besar. Sehingga akan kita separate kode inisialisasi Router API dengan kode seperti dibawah ini.
1func NewRouter(articleHandler *httpHandler.Delivery) *httprouter.Router {
2 // inisialisasi http router
3 router := httprouter.New()
4
5 // inisialisasi chain middleware
6 m := middleware_chain.New(
7 middleware.AuthenticationBasic,
8 )
9
10 // entrypoint
11 router.GET("/api/articles", m.Then(articleHandler.GetAll))
12 router.GET("/api/articles/:article_id", m.Then(articleHandler.GetByID))
13 router.POST("/api/articles/", m.Then(articleHandler.Store))
14 router.PUT("/api/articles/:article_id", m.Then(articleHandler.Update))
15 router.DELETE("/api/articles/:article_id", m.Then(articleHandler.Delete))
16
17 return router
18}Dan jangan lupa kita ubah inisialisasi Router API pada file main.go seperti ini.
1func main() {
2 fileEnv := ".env"
3 if os.Getenv("environment") == "development" {
4 fileEnv = "../.env"
5 }
6
7 err := godotenv.Load(fileEnv)
8 if err != nil {
9 log.Fatalf("error loading .env file")
10 }
11
12 // inisialisasi database
13 db := database.New()
14
15 // inisialisasi repository
16 repository := mysqlRepository.New(db)
17 // inisialisasi usecase
18 articleUsecase := articleUsecase.New(repository)
19 // inisialisasi handler
20 articleHandler := httpHandler.New(articleUsecase)
21 // inisialisasi new router
22 router := NewRouter(articleHandler)
23
24 server := http.Server{
25 Addr: "localhost:3000",
26 Handler: router,
27 }
28
29 err = server.ListenAndServe()
30 if err != nil {
31 panic(err)
32 }
33}Pengujian
Setelah kita memasang Authentication Middleware kita perlu mencobanya di setiap endpoint yang sudah kita buat. Pengujiannya itu berarti ada dua skenario yaitu
- Unauthorized ketika kita mengakses service tanpa mengirimkan Header
X-API-Keydengan values3cr3t - Sukses mengakses service dengan mengirimkan Header yang sesuai
- Unauthorized ketika kita mengirimkan Header
X-API-Keydengan value yang salah misalkansecret.
Berikut Hasil dari pengujian kita coba

X-API-Key terjadi error
X-API-Key dengan value yang sesuai