Authentication and Authorization in Go - Tutorial

Authentication and authorization are essential components of secure web applications. In Go, you can implement authentication and authorization functionality using various libraries and techniques. In this tutorial, we will explore how to handle authentication and authorization in Go, covering the basic steps involved in user authentication, securing routes, and managing user roles. By the end of this tutorial, you will have a solid understanding of how to implement secure authentication and authorization in your Go applications.

User Authentication with Sessions

User authentication involves verifying the identity of users before granting them access to protected resources. One common approach is to use session-based authentication, where a unique session identifier is assigned to each user upon successful login. This session identifier is stored on the client-side, typically in a browser cookie, and is used to validate subsequent requests.

Example:

package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/sessions"
)

var (
	store  = sessions.NewCookieStore([]byte("secret-key"))
	secret = "secret"
)

func main() {
	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/logout", logoutHandler)
	http.HandleFunc("/dashboard", dashboardHandler)

	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		fmt.Println("Server error:", err)
	}
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "session-name")
	userID := session.Values["user-id"]
	if userID != nil {
		fmt.Fprintf(w, "Welcome, User ID: %v", userID)
	} else {
		fmt.Fprint(w, "Welcome, Guest")
	}
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "session-name")
	session.Values["user-id"] = 1
	session.Save(r, w)
	http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "session-name")
	delete(session.Values, "user-id")
	session.Save(r, w)
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func dashboardHandler(w http.ResponseWriter, r *http.Request) {
	session, _ := store.Get(r, "session-name")
	userID := session.Values["user-id"]
	if userID == nil {
		http.Redirect(w, r, "/", http.StatusSeeOther)
		return
	}
	fmt.Fprintf(w, "Dashboard, User ID: %v", userID)
}

In the example above, we use the gorilla/sessions package to manage user sessions. We create a CookieStore with a secret key and define four route handlers: homeHandler, loginHandler, logoutHandler, and dashboardHandler. In the loginHandler, we store the user ID in the session and redirect the user to the dashboard. The logoutHandler clears the user ID from the session, and the dashboardHandler checks if the user is authenticated before displaying the dashboard.

Securing Routes with Middleware

Once you have implemented user authentication, you need to secure specific routes to ensure that only authenticated users can access them. This can be achieved by using middleware functions that validate the user's session before allowing access to the protected routes.

Example:

package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/sessions"
)

var (
	store  = sessions.NewCookieStore([]byte("secret-key"))
	secret = "secret"
)

func main() {
	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/logout", logoutHandler)
	http.HandleFunc("/dashboard", authenticateMiddleware(dashboardHandler))

	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		fmt.Println("Server error:", err)
	}
}

func authenticateMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		session, _ := store.Get(r, "session-name")
		userID := session.Values["user-id"]
		if userID == nil {
			http.Redirect(w, r, "/", http.StatusSeeOther)
			return
		}
		next(w, r)
	}
}

// Remaining handler functions...