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.
.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.
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)
}
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) 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 !❤️