> For the complete documentation index, see [llms.txt](https://davidwinalda94.gitbook.io/mastering-golang/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://davidwinalda94.gitbook.io/mastering-golang/go-basic-for-web-development/http-basic-auth.md).

# HTTP Basic Auth

Go menyediakan package yang dapat digunakan untuk proses HTTP basic authentication.&#x20;

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](https://tools.ietf.org/html/rfc7617).

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 `Authorization`dan 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:

```go
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`&#x20;
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:

```go
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:

```go
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`

```go
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

```go
const USERNAME = "davidwinalda"
const PASSWORD = "12345"
```

Selanjutnya kita akan membuat fungsi `Auth` untuk proses pengecekan Authentication. Kita menggunakan fungsi bawaan dari [package](https://golang.org/pkg/net/http/#Request.BasicAuth) `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.

```go
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` :

```go
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` :

```go
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:

```go
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
```

![Running program menggunakan go run \*.go](/files/-MTpdjexr-Rk4AK5eRhA)

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

![Mengakses endpoint dengan http auth basic](/files/-MTpsmshwznGphDRF-8A)

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

![Credential authentication tidak sesuai](/files/-MTpt6inCvaQVBaTa7zZ)
