Back To Wall

Securing Your Go Web App with JWT Authentication

In this tutorial, we'll explore the implementation of JSON Web Tokens (JWTs) in your Go web application to enhance user authentication and secure your API routes

To access the entire codebase, simply visit the GitHub Repository. Happy coding! 🚀


Generating JWT Tokens

Let's begin by generating a JWT token using the generateToken.go script. This script creates a unique user ID, generates a token, and prints it out for you. This token will serve as your digital authentication pass.

package main

import (
    ...
)

func main() {
    if err := godotenv.Load(); err != nil {
        fmt.Println("Error loading .env file")
        return
    }

    uniqueID := generateUniqueID()
    token, _ := createToken(uniqueID)

    fmt.Println("Your Token:")
    fmt.Println(token)
}

Once you've run this script, you'll obtain your token, ready for use in securing your API routes.

Token


As you see Token is generated. Copy it to use later.

Also do not forget to add your SECRET_KEY to the .env file. Or you may specify in the editor. I prefer to use .env for security concerns.

Secret Key

 

Securing API Routes

To secure your API routes, we'll use the JWT token in conjunction with the TokenVerifyMiddleware.

This middleware ensures that only users with a valid token can access the specified routes. Let's take a look at the code:

package middleware

import (
    ...
	"net/http"
	"os"
	"strings"
	"github.com/dgrijalva/jwt-go"
)

func TokenVerifyMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        secretKey := os.Getenv("SECRET_KEY")
        authHeader := r.Header.Get("Authorization")
        if authHeader == "" {
            http.Error(w, "Authorization header is missing", http.StatusForbidden)
            return
        }

        // Extract the token from the Authorization header
        parts := strings.Split(authHeader, " ")
        if len(parts) != 2 {
            http.Error(w, "Malformed token", http.StatusForbidden)
            return
        }

        tokenPart := parts[1]
        token, err := jwt.Parse(tokenPart, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
            }
            return []byte(secretKey), nil
        })

        if err != nil || !token.Valid {
            http.Error(w, "Invalid or expired token", http.StatusForbidden)
            return
        }

        next.ServeHTTP(w, r)
    }
}

Now, let's secure our API routes using the TokenVerifyMiddleware:

import (
    ...
	"github.com/aozen/cocktail-recipe/internal/middleware"
)

func SecureAPI(handler http.HandlerFunc) http.HandlerFunc {
    return middleware.TokenVerifyMiddleware(handler)
}

func SetupRoutes(r *mux.Router, db *mongo.Database) {
    collection = db.Collection("cocktails")

    r.HandleFunc("/api/cocktails", SecureAPI(getCocktails)).Methods("GET")
    // ... Other routes protected by SecureAPI ...
}

With this setup, only users with a valid token can access the secured routes.

 

Demo: Testing API Routes

For demonstration purposes, let's use Postman to test our API routes. Open Postman and follow these steps:


  1. Accessing Without Token:

    • Try accessing a secured route (e.g., /api/cocktails) without providing a token in the Authorization header. Observe the "Authorization header is missing" error response.
    • Authorization header is missing
  2. Accessing With Token:

    • Now, add your generated token to the Authorization header with the "Bearer " prefix.
    • Access the same secured route (e.g., /api/cocktails). You should now receive a successful response.
      Valid Token

Feel free to explore more features and adapt the code based on your project's requirements. Secure coding made simple! 🍹

 

Handy Resources

 

Feel free to reach out if you have any questions or need further assistance. Whether through email or your preferred communication channel, I'm here to help! Happy coding and enjoy the enhanced security of your Go web application! 🔐