Handling Authentication and Authorization

In this chapter, we'll delve into the essential aspects of handling authentication and authorization in Go applications. From basic concepts to advanced techniques, we'll cover everything you need to know to secure your Go applications effectively.

Understanding Authentication and Authorization

.Introduction to Authorization

Authorization, on the other hand, involves determining what actions or resources a user is allowed to access after authentication. It defines permissions and access levels based on roles, privileges, or other criteria.

Basic Authentication in Go

HTTP Basic Authentication

HTTP Basic Authentication is a simple authentication mechanism where the client sends credentials (usually username and password) in the HTTP headers. The server validates these credentials against a user database or other authentication sources.

				
					package main

import (
    "fmt"
    "net/http"
)

func basicAuthHandler(w http.ResponseWriter, r *http.Request) {
    username, password, ok := r.BasicAuth()
    if !ok || username != "user" || password != "password" {
        w.WriteHeader(http.StatusUnauthorized)
        fmt.Fprint(w, "Unauthorized access")
        return
    }

    fmt.Fprint(w, "Welcome, authenticated user!")
}

func main() {
    http.HandleFunc("/protected", basicAuthHandler)
    http.ListenAndServe(":8080", nil)
}

				
			

Token-Based Authentication with JWT

Introduction to JWT

JSON Web Tokens (JWT) are a popular method for implementing token-based authentication. They consist of a compact and self-contained JSON object that securely represents claims between two parties.

				
					package main

import (
    "fmt"
    "net/http"
)

func basicAuthHandler(w http.ResponseWriter, r *http.Request) {
    username, password, ok := r.BasicAuth()
    if !ok || username != package main

import (
    "fmt"
    "github.com/dgrijalva/jwt-go"
    "net/http"
    "time"
)

var secretKey = []byte("secret")

func generateToken() (string, error) {
    token := jwt.New(jwt.SigningMethodHS256)
    claims := token.Claims.(jwt.MapClaims)
    claims["username"] = "user"
    claims["exp"] = time.Now().Add(time.Hour * 24).Unix() // Token expiry time

    return token.SignedString(secretKey)
}

func validateToken(tokenString string) (jwt.MapClaims, error) {
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        return secretKey, nil
    })
    if err != nil {
        return nil, err
    }

    if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
        return claims, nil
    } else {
        return nil, fmt.Errorf("Invalid token")
    }
}

func tokenHandler(w http.ResponseWriter, r *http.Request) {
    tokenString, err := generateToken()
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Fprintf(w, "Error generating token: %v", err)
        return
    }

    fmt.Fprintf(w, "Token: %s", tokenString)
}

func protectedHandler(w http.ResponseWriter, r *http.Request) {
    tokenString := r.Header.Get("Authorization")
    if tokenString == "" {
        w.WriteHeader(http.StatusUnauthorized)
        fmt.Fprint(w, "Unauthorized access")
        return
    }

    claims, err := validateToken(tokenString)
    if err != nil {
        w.WriteHeader(http.StatusUnauthorized)
        fmt.Fprintf(w, "Invalid token: %v", err)
        return
    }

    username := claims["username"].(string)
    fmt.Fprintf(w, "Welcome, %s!", username)
}

func main() {
    http.HandleFunc("/token", tokenHandler)
    http.HandleFunc("/protected", protectedHandler)
    http.ListenAndServe(":8080", nil)
}
"user" || password != "password" {
        w.WriteHeader(http.StatusUnauthorized)
        fmt.Fprint(w, "Unauthorized access")
        return
    }

    fmt.Fprint(w, "Welcome, authenticated user!")
}

func main() {
    http.HandleFunc("/protected", basicAuthHandler)
    http.ListenAndServe(":8080", nil)
}

				
			

Role-Based Access Control (RBAC)

Introduction to RBAC

Role-Based Access Control (RBAC) is a method of restricting network access based on a user’s roles or permissions. It assigns permissions to roles and then associates roles with users.

				
					package main

import (
    "fmt"
    "net/http"
)

var userRoles = map[string][]string{
    "user1": {"read"},
    "user2": {"read", "write"},
}

func authorizedHandler(roles []string) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // Check if the user has any of the required roles
        for _, role := range roles {
            if hasRole(r, role) {
                fmt.Fprintf(w, "Authorized access for role: %s\n", role)
                return
            }
        }

        w.WriteHeader(http.StatusForbidden)
        fmt.Fprint(w, "Access forbidden")
    }
}

func hasRole(r *http.Request, role string) bool {
    // Retrieve user role from request context or session
    user := r.Context().Value("user").(string)

    // Check if user has the specified role
    for _, r := range userRoles[user] {
        if r == role {
            return true
        }
    }

    return false
}

func main() {
    http.HandleFunc("/read", authorizedHandler([]string{"read"}))
    http.HandleFunc("/write", authorizedHandler([]string{"write"}))
    http.ListenAndServe(":8080", nil)
}

				
			

In conclusion, handling authentication and authorization is crucial for securing your Go applications. By understanding basic authentication mechanisms like HTTP Basic Authentication and implementing token-based authentication with JWT, you can ensure secure user authentication. Role-Based Access Control (RBAC) further enhances security by defining granular permissions based on user roles. By following these best practices and implementing robust security measures, you can protect your Go applications from unauthorized access and mitigate security risks effectively. Happy coding !❤️

Table of Contents