HTTP Basic Auth

Go menyediakan package yang dapat digunakan untuk proses HTTP basic authentication.

Metode ini membutuhkan informasi username dan password untuk disisipkan dalam header request (dengan format tertentu) sehingga tidak memerlukan cookies maupun session. Lebih jelasnya silakan baca RFC-7617.

Data username dan password tidak langsung ditampilkan dalam header, namun data tersebut harus di-encode terlebih dahulu ke dalam format yg sudah ditentukan sesuai spesifikasi, sebelum dimasukan ke header.

Berikut adalah contoh penulisan basic auth:

// Request header
Authorization: Basic ZGF2aWR3aW5hbGRhOjEyMzQ1==

Informasi disisipkan dalam request header dengan key Authorizationdan value adalah Basic spasi hasil enkripsi dari data username dan password. Data username dan password digabung dengan separator tanda titik dua (:), lalu di-encode dalam format encoding Base 64.

// Username password encryption
base64encode("davidwinalda:12345")
// Hasilnya adalah ZGF2aWR3aW5hbGRhOjEyMzQ1==

Golang menyediakan fungsi untuk meng-handle request basic auth dengan cukup mudah, jadi kita tidak perlu untuk memparsing header request terlebih dahulu untuk mendapatkan informasi username dan password.

Studi Kasus

Kita akan membuat web service sederhana untuk memperlihatkan penggunaak http basic authentication.

Setup pada file main.go

Pada file main.go , kita akan membuat fungsi main yang berisi beberapa statement berikut:

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/student", ActionStudent)

	server := new(http.Server)
	server.Addr = ":8080"

	fmt.Println("server started at localhost:8080")
	server.ListenAndServe()
}

Pada fungsi di atas kita mendaftarkan 1 API endpoint /student yang nantinya kita akan melakukan 2 proses GET :

  1. GET /student dimana akan menampilkan seluruh data students

  2. GET /student?id=1 dimana akan menampilkan data student berdasarkan query string id

Lalu selanjutnya seperti yang kita lakukan sebelumnya untuk setup port dan server.

Kita akan membuat handler dari ActionStudent masih di file main.go yang berisi beberapa statement berikut ini:

func ActionStudent(w http.ResponseWriter, r *http.Request) {
	if !Auth(w, r) {
		return
	}
	if !AllowOnlyGET(w, r) {
		return
	}

	if id := r.URL.Query().Get("id"); id != "" {
		OutputJSON(w, GetStudentById(id))
		return
	}

	OutputJSON(w, GetStudent())
}

Kita bisa lihat pada kode di atas bahwa ada proses pengecekan terlebih dahulu sebelum user dapat mengakses API dari /student yaitu fungsi !Auth . Kita juga membuat pengecekan dari !AllowOnlyGET bahwa hanya method GET yang bisa diproses pada endpoint ini.

Selanjutnya kita akan membuat fungsi OutputJSON untuk encode data ke JSON:

func OutputJSON(w http.ResponseWriter, f interface{}) {
	w.Header().Set("Content-Type", "application/json")
	result, err := json.Marshal(f)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(result)
}

Setup pada file student.go

Pada root directory buatlah 1 file baru bernama student.go yang masih dalam package main . Pada file ini kita akan membuat struct dari data students dan membuat fungsi yaitu GetStudent dan GetStudentById untuk nantinya dipanggil di dalam handler yang telah dibuat. Kita juga akan mengisi data secara manual pada fungsi init

package main

var students = []*Student{}

type Student struct {
	Id       string
	Fullname string
	Age      int
	Batch    string
}

func GetStudent() []*Student {
	return students
}

func GetStudentById(id string) *Student {
	for _, each := range students {
		if each.Id == id {
			return each
		}
	}

	return nil
}

func init() {
	students = append(students, &Student{Id: "1", Fullname: "Wisma", Age: 25, Batch: "Adorable"})
	students = append(students, &Student{Id: "2", Fullname: "Yudis", Age: 22, Batch: "Brilliant"})
	students = append(students, &Student{Id: "3", Fullname: "Sukisno", Age: 35, Batch: "Creative"})
	students = append(students, &Student{Id: "4", Fullname: "Guntur", Age: 30, Batch: "Dilligent"})
}

Setup pada file middleware.go

Nah inilah tahapan proses authentication. Kita akan membuat 1 file lagi pada root directory bernama middleware.go .

Pada file ini, kita akan mendeklarasikan secara hardcode untuk data username dan password yang kita anggap telah terdaftar

const USERNAME = "davidwinalda"
const PASSWORD = "12345"

Selanjutnya kita akan membuat fungsi Auth untuk proses pengecekan Authentication. Kita menggunakan fungsi bawaan dari package http yaitu BasicAuth yang akan melakukan proses decode dari header dan mengembalikan 3 data yaitu username , password , dan ok. ok akan mengembalikan status apakah request yang dilakukan valid atau tidak.

func Auth(w http.ResponseWriter, r *http.Request) bool {
	username, password, ok := r.BasicAuth()

	if !ok {
		http.Error(w, "Something went wrong", http.StatusInternalServerError)
		return false
	}

	isValid := (username == USERNAME) && (password == PASSWORD)

	if !isValid {
		http.Error(w, "Authentication is failed", http.StatusInternalServerError)
		return false
	}

	return true
}

Jika valid maka kita bisa melakukan pengecekan untuk validasi username dan password :

isValid := (username == USERNAME) && (password == PASSWORD)

	if !isValid {
		http.Error(w, "Authentication is failed", http.StatusInternalServerError)
		return false
	}

Selanjutnya kita akan membuat fungsi AllowOnlyGET untuk membatas akses endpoint hanya bisa menggunakan method GET :

func AllowOnlyGET(w http.ResponseWriter, r *http.Request) bool {
	if r.Method != "GET" {
		http.Error(w, "Only allow GET method", http.StatusInternalServerError)
		return false
	}

	return true
}

Untuk kode lengkapnya bisa dilihat di bawah ini:

package main

import "net/http"

const USERNAME = "davidwinalda"
const PASSWORD = "12345"

func Auth(w http.ResponseWriter, r *http.Request) bool {
	username, password, ok := r.BasicAuth()

	if !ok {
		http.Error(w, "Something went wrong", http.StatusInternalServerError)
		return false
	}

	isValid := (username == USERNAME) && (password == PASSWORD)

	if !isValid {
		http.Error(w, "Authentication is failed", http.StatusInternalServerError)
		return false
	}

	return true
}

func AllowOnlyGET(w http.ResponseWriter, r *http.Request) bool {
	if r.Method != "GET" {
		http.Error(w, "Only allow GET method", http.StatusInternalServerError)
		return false
	}

	return true
}

Menjalankan Program

Untuk menjalankan program kita tidak bisa menggunakan go run main.go karena terdapat beberapa file sebagai package main . Oleh karena itu kita dapat menjalankan program dengan perintah berikut:

go run *.go

Kita akan mencoba HTTP Authentication Basic dan mengakses API endpoint yang telah kita buat menggunakan Postman:

Jika data authentication salah atau tidak sesuai maka pada postman akan mengembalikan data berikut:

Last updated