15 Creating Session-based Authentication Middleware Using `httprouter` in Golang
In this article, we will walk through the steps of building Session-based Authentication Middleware using Golang and the httprouter library. This middleware will help verify user sessions for incoming HTTP requests.
What is Session-based Authentication?
Session-based authentication is an authentication method where a user logs in through an application, and the server stores session information in the form of a session ID, either on the server or as a cookie in the user’s browser. Each time the user accesses a protected resource, the server verifies the session.
For this example, we will use httprouter, a fast and minimalistic HTTP router for Golang, to create the session-based authentication middleware.
Step 1: Setting Up the Golang Project
If you haven’t already, set up your Golang project with the following steps:
- Create a project directory via the terminal or your preferred IDE.
- Initialize the Go project.
1go mod init session-auth-example
Step 2: Installing Dependencies
First, we need to add dependencies for httprouter and gorilla/sessions, which we will use to manage user sessions.
Install them using the following commands:
1 go get github.com/julienschmidt/httprouter
2 go get github.com/gorilla/sessionsStep 3: Creating the Authentication Middleware
Now, we will create middleware to validate sessions for every incoming HTTP request. We need:
- A
storefromgorilla/sessionsto manage session storage. - A middleware function to check whether a valid session exists.
authMiddleware.go
1package main
2
3import (
4 "github.com/gorilla/sessions"
5 "github.com/julienschmidt/httprouter"
6 "net/http"
7 "log"
8)
9
10var store = sessions.NewCookieStore([]byte("secret-key"))
11
12// AuthMiddleware checks if the user session is valid
13func AuthMiddleware(next httprouter.Handle) httprouter.Handle {
14 return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
15 // Retrieve session from request
16 session, err := store.Get(r, "session-name")
17 if err != nil {
18 http.Error(w, "Could not get session", http.StatusInternalServerError)
19 return
20 }
21
22 // Check if the "user" session exists
23 user, ok := session.Values["user"]
24 if !ok || user == nil {
25 http.Error(w, "Unauthorized", http.StatusUnauthorized)
26 return
27 }
28
29 // Proceed if authentication is valid
30 next(w, r, ps)
31 }
32}
33
34func main() {
35 router := httprouter.New()
36 router.GET("/private", AuthMiddleware(privatePage))
37
38 log.Fatal(http.ListenAndServe(":8080", router))
39}
40
41// Handler for a protected private page
42func privatePage(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
43 w.Write([]byte("Welcome to the private page"))
44}Step 4: Creating a Login Endpoint to Set a Session
To test authentication, we need an endpoint to log in. This endpoint initializes a session when a user successfully logs in.
loginHandler.go
1package main
2
3import (
4 "github.com/julienschmidt/httprouter"
5 "net/http"
6 "log"
7)
8
9func loginHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
10 session, _ := store.Get(r, "session-name")
11
12 // Set user session (e.g., after successful login)
13 session.Values["user"] = "authenticated-user"
14 session.Save(r, w)
15
16 w.Write([]byte("Login successful"))
17}
18
19func main() {
20 router := httprouter.New()
21 router.GET("/login", loginHandler)
22 router.GET("/private", AuthMiddleware(privatePage))
23
24 log.Fatal(http.ListenAndServe(":8080", router))
25}Step 5: Writing Unit Tests for the Middleware
Writing unit tests ensures that our middleware functions as expected. We will use Golang’s standard testing package.
authMiddleware_test.go
1package main
2
3import (
4 "github.com/julienschmidt/httprouter"
5 "net/http"
6 "net/http/httptest"
7 "testing"
8)
9
10func TestAuthMiddleware(t *testing.T) {
11 router := httprouter.New()
12 router.GET("/private", AuthMiddleware(privatePage))
13
14 req, err := http.NewRequest("GET", "/private", nil)
15 if err != nil {
16 t.Fatal(err)
17 }
18
19 rr := httptest.NewRecorder()
20 router.ServeHTTP(rr, req)
21
22 if status := rr.Code; status != http.StatusUnauthorized {
23 t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusUnauthorized)
24 }
25}
26
27func TestLogin(t *testing.T) {
28 router := httprouter.New()
29 router.GET("/login", loginHandler)
30
31 req, err := http.NewRequest("GET", "/login", nil)
32 if err != nil {
33 t.Fatal(err)
34 }
35
36 rr := httptest.NewRecorder()
37 router.ServeHTTP(rr, req)
38
39 if rr.Code != http.StatusOK {
40 t.Errorf("Expected status OK, got %v", rr.Code)
41 }
42}Explanation of Unit Tests:
- TestAuthMiddleware: Ensures that the middleware returns a 401 Unauthorized status if a user session is invalid.
- TestLogin: Confirms that the login request succeeds with a 200 OK status.
Step 6: Conclusion
Using Golang, we can efficiently create Session-based Authentication middleware with httprouter. In this example, we built a middleware that verifies user sessions stored in cookies to secure protected endpoints.
For more information on Golang and HTTP routing, check out:
This guide outlines the fundamentals of implementing session-based authentication using middleware in Golang. Feel free to explore further enhancements and refinements!