🔐 Adding JWT Authentication to Your Golang API with Gin

If you're building APIs with Go, sooner or later you'll need to authenticate users. One of the most popular and scalable ways to do this is by using JWT (JSON Web Tokens).
In this guide, we'll walk through:
What JWT is and why it's useful
How to implement JWT-based authentication in Go using Gin
How to protect your API routes
Testing everything using Postman
🧠 What is JWT?
JWT (JSON Web Token) is a compact and stateless way of securely transmitting information between two parties. It consists of:
Header – contains algorithm & token type
Payload – contains claims like user ID, roles, etc.
Signature – verifies the token hasn’t been tampered with
Example JWT:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
It's perfect for REST APIs because it doesn’t require session state — just validate the token on each request.
⚙️ Project Setup
📦 Step 1: Install Required Packages
go mod init jwt-auth-example
go get github.com/gin-gonic/gin
go get github.com/golang-jwt/jwt/v5
🧱 Step 2: Define the Main Structure
Create a file called main.go
and paste the following:
package main
import (
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
var jwtSecret = []byte("your_secret_key") // Change this to a secure value
// JWT Claims structure
type Claims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
func main() {
r := gin.Default()
r.POST("/login", loginHandler)
r.GET("/protected", authMiddleware(), protectedHandler)
r.Run(":8080")
}
🔑 Step 3: JWT Token Generation (Login Route)
func loginHandler(c *gin.Context) {
var body struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
// Simple check (in real apps, validate from DB)
if body.Username != "admin" || body.Password != "password" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
expirationTime := time.Now().Add(30 * time.Minute)
claims := &Claims{
Username: body.Username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtSecret)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Token generation failed"})
return
}
c.JSON(http.StatusOK, gin.H{"token": tokenString})
}
🛡️ Step 4: Auth Middleware (Protect Routes)
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tokenString := c.GetHeader("Authorization")
if tokenString == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Missing Authorization header"})
c.Abort()
return
}
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
c.Abort()
return
}
// Store the username in context for later use
c.Set("username", claims.Username)
c.Next()
}
}
✅ Step 5: Protected Route Example
func protectedHandler(c *gin.Context) {
username := c.MustGet("username").(string)
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("Welcome, %s! You have access to protected data.", username),
})
}
🧪 Testing with Postman
POST /login
Body:
{ "username": "admin", "password": "password" }
✅ Response will include
token
GET /protected
Add header:
Authorization: <paste-your-token-here>
✅ Should return welcome message if token is valid
🛠️ You can use Postman for testing, or install Thunder Client in VSCode.
🔗 Tools I Used
Tool | Purpose |
JetBrains GoLand | Advanced IDE for Go |
Postman | Test APIs locally |
Gin Gonic | Web framework for Go |
Udemy Go Security Course | Learn Go + JWT in depth |
🧠 Final Thoughts
You’ve just added secure, token-based authentication to your Go API using JWT and Gin! This is the foundation of most modern backend systems — and you’ve built it from scratch.
Let me know in the comments if you’d like a follow-up on:
Refresh tokens
Storing tokens in cookies
Role-based access control
👉 Found this helpful? Subscribe for more posts on building secure, scalable backends with Golang & Python!
📢 Coming Next:
“Connecting Your Golang API to Redis: Fast Caching for Real-World APIs”
Subscribe to my newsletter
Read articles from NEELAM SAI KUMAR directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
