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.
go 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:
go get github.com/julienschmidt/httprouter
go get github.com/gorilla/sessions
Step 3: Creating the Authentication Middleware
Now, we will create middleware to validate sessions for every incoming HTTP request. We need:
- A
store
fromgorilla/sessions
to manage session storage. - A middleware function to check whether a valid session exists.
authMiddleware.go
package main
import (
"github.com/gorilla/sessions"
"github.com/julienschmidt/httprouter"
"net/http"
"log"
)
var store = sessions.NewCookieStore([]byte("secret-key"))
// AuthMiddleware checks if the user session is valid
func AuthMiddleware(next httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// Retrieve session from request
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, "Could not get session", http.StatusInternalServerError)
return
}
// Check if the "user" session exists
user, ok := session.Values["user"]
if !ok || user == nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Proceed if authentication is valid
next(w, r, ps)
}
}
func main() {
router := httprouter.New()
router.GET("/private", AuthMiddleware(privatePage))
log.Fatal(http.ListenAndServe(":8080", router))
}
// Handler for a protected private page
func privatePage(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.Write([]byte("Welcome to the private page"))
}
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
package main
import (
"github.com/julienschmidt/httprouter"
"net/http"
"log"
)
func loginHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
session, _ := store.Get(r, "session-name")
// Set user session (e.g., after successful login)
session.Values["user"] = "authenticated-user"
session.Save(r, w)
w.Write([]byte("Login successful"))
}
func main() {
router := httprouter.New()
router.GET("/login", loginHandler)
router.GET("/private", AuthMiddleware(privatePage))
log.Fatal(http.ListenAndServe(":8080", router))
}
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
package main
import (
"github.com/julienschmidt/httprouter"
"net/http"
"net/http/httptest"
"testing"
)
func TestAuthMiddleware(t *testing.T) {
router := httprouter.New()
router.GET("/private", AuthMiddleware(privatePage))
req, err := http.NewRequest("GET", "/private", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusUnauthorized {
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusUnauthorized)
}
}
func TestLogin(t *testing.T) {
router := httprouter.New()
router.GET("/login", loginHandler)
req, err := http.NewRequest("GET", "/login", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("Expected status OK, got %v", rr.Code)
}
}
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!