programming

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:

  1. Create a project directory via the terminal or your preferred IDE.
  2. 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:

  1. A store from gorilla/sessions to manage session storage.
  2. 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:

  1. TestAuthMiddleware: Ensures that the middleware returns a 401 Unauthorized status if a user session is invalid.
  2. 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!

comments powered by Disqus