Implementing JWT (JSON Web Token) authentication in a Gin web application enhances security by ensuring that only authenticated users can access protected resources. This tutorial provides a step-by-step guide to integrating JWT authentication into your Gin project.

Prerequisites

  • Go programming language installed (version 1.16+)
  • Basic knowledge of Gin framework
  • Understanding of JWT concepts
  • Text editor or IDE of your choice

Step 1: Initialize Your Gin Project

Create a new directory for your project and initialize a Go module:

mkdir gin-jwt-auth
cd gin-jwt-auth
go mod init github.com/yourusername/gin-jwt-auth

Install Gin and JWT libraries:

go get -u github.com/gin-gonic/gin
go get -u github.com/dgrijalva/jwt-go

Step 2: Create Main Application File

Create a file named main.go and set up a basic Gin server:

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    r.POST("/login", login)
    r.GET("/protected", authMiddleware(), protectedEndpoint)

    r.Run(":8080")
}

Step 3: Implement Login Handler

The login handler authenticates users and issues JWT tokens:

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

var jwtKey = []byte("your_secret_key")

type Credentials struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

func login(c *gin.Context) {
    var creds Credentials
    if err := c.BindJSON(&creds); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
        return
    }

    // Replace with real authentication
    if creds.Username != "admin" || creds.Password != "password" {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
        return
    }

    expirationTime := time.Now().Add(15 * time.Minute)
    claims := &jwt.StandardClaims{
        ExpiresAt: expirationTime.Unix(),
        Subject:   creds.Username,
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    tokenString, err := token.SignedString(jwtKey)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
        return
    }

    c.JSON(http.StatusOK, gin.H{"token": tokenString})
}

Step 4: Create Authentication Middleware

The middleware verifies JWT tokens for protected routes:

func authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        tokenString := c.GetHeader("Authorization")
        if tokenString == "" {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing token"})
            return
        }

        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                return nil, jwt.ErrSignatureInvalid
            }
            return jwtKey, nil
        })

        if err != nil || !token.Valid {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
            return
        }

        c.Next()
    }
}

Step 5: Create Protected Route

Define a route that requires authentication:

func protectedEndpoint(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"message": "Welcome to the protected route!"})
}

Step 6: Run Your Application

Build and run your Gin server:

go run main.go

Test the login endpoint with a POST request containing JSON credentials, then use the returned token to access the protected route by including it in the Authorization header.

Conclusion

By following these steps, you have integrated JWT authentication into your Gin web application. This setup provides a robust security layer, ensuring that only authenticated users can access sensitive resources. Remember to keep your secret keys secure and consider implementing token refresh mechanisms for production environments.