10 Adding Simple Authentication
At this stage we will try to add simple Authentication by using middleware in Golang. You need to know that middleware is the process of intercepting an API which before the service when accessed goes to the *handler * layer, it will pass through this * middleware * layer (which we will create) to capture and process something for certain needs. For example, in this case we will intercept the service process to see if the API has a header with the key condition X-API-Key.
So, the middleware that we will create has the following rules.
- Checks whether the API has an
X-API-Keyheader - If it does not exist, it will provide
Unauthorizederror information - And if it exists and matches the predetermined value of
s3cr3tand is the same, then the process will continue.
Here is more or less the flow middleware that we will create.
---
Title: Authentication Middleware
---
stateDiagram-v2
[*] --> hasHeader
hasHeader --> unAuthorized
unAuthorized --> [*]
hasHeader --> HeaderXAPIKey
HeaderXAPIKey --> unAuthorized
HeaderXAPIKey --> SuccessContinue
SuccessContinue --> [*]
Middleware Chain Creation
First we will create the pkg/middleware-chain folder with the name middleware_chain.go. Here are the contents of the file.
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 // initialize http router
2 router := httprouter.New()
3
4 // initialize middleware chain
5 m := middleware_chain.New(
6 // middleware function that we will add
7 )Create a Simple Authentication Middleware
In the function that we will create, namely * middleware * which needs to do Authentication service so that not just anyone accesses our services and data so that it is more secure.
In accordance with what has been explained above, we will create this authentication middleware process for our service where we will save the file in the middleware/auth.go folder and fill the file with the code below.
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}Very simple because we want to try to create simple and easy middleware first so that friends can understand the process of this middleware.
Turning Router Initialization into its Own Function
At this stage we will separate the initialization of the Router API into a separate function so that it is easy to recognize and not too long when it has many endpoints causing the main.go file to become long and large. So we will separate the Router API initialization code with the code as below.
1func NewRouter(articleHandler *httpHandler.Delivery) *httprouter.Router {
2 // initialize http router
3 router := httprouter.New()
4
5 // initialize middleware chain
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}And don’t forget we change the Router API initialization in the main.go file like this.
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 // initialize the database
13 db := database.New()
14
15 // initialize repository
16 repository := mysqlRepository.New(db)
17 // initialize usecase
18 articleUsecase := articleUsecase.New(repository)
19 // handler initialization
20 articleHandler := httpHandler.New(articleUsecase)
21 // initialize 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}Testing
After we have installed the Authentication Middleware we need to test it on each endpoint that we have created. Testing it means there are two scenarios, namely
- Unauthorized when we access the service without sending the
X-API-KeyHeader with the values3cr3t - Successfully accessing the service by sending the appropriate Header
- Unauthorized when we send the
X-API-KeyHeader with the wrong value for examplesecret.
Here are the results of our test

X-API-Key header resulted in an error
X-API-Key header with appropriate value