Understanding JWT Authentication in Go (Gin Framework)
2025-09-13T12:12:53 - Vicky Chhetri
Introduction
Authentication is at the heart of any secure web application. Traditionally, developers have relied on server-side sessions to manage logged-in users. But as applications scale into microservices and stateless APIs, a modern approach is needed.
That’s where JWT (JSON Web Tokens) comes in. JWT allows us to authenticate users in a stateless, scalable, and secure way.
In this blog, we’ll:
- Understand what JWT is and why it matters.
- Learn how JWT works step-by-step.
- Implement JWT authentication in Go using the Gin framework.
- Explore the example repo: go-jwt-auth-use.
What is JWT?
JWT (JSON Web Token) is a compact way of securely transmitting information between two parties (usually client ↔ server).
It looks like this:
xxxxx.yyyyy.zzzzz
It has 3 parts:
- Header – contains algorithm & type.
- Payload – contains claims (data like
username,exp). - Signature – verifies the token’s integrity using a secret key.
Example payload:
{
"username": "alice",
"exp": 1694445200
}
When a token is signed, the server can later verify if it’s valid and untampered.
Why JWT for APIs?
- ✅ Stateless – no need to store sessions on the server.
- ✅ Fast – each request carries its identity (the token).
- ✅ Cross-platform – can be used across mobile apps, web apps, microservices.
How JWT Authentication Works
- User registers → password is hashed and stored.
- User logs in → server generates a JWT with user details.
- Server sends back the JWT.
- Client stores it (localStorage, cookie, or memory).
- For every request → client sends token in
Authorizationheader. - Middleware validates the JWT before allowing access.
Project Structure
From the repo go-jwt-auth-use:
.
├── main.go
├── controllers/
│ └── auth.go
├── middlewares/
│ └── auth.go
├── models/
│ └── user.go
└── routes/
└── auth.go
Implementation in Go Gin
1. Register User
func Register(c *gin.Context) {
var input models.User
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Hash password before saving
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(input.Password), 10)
input.Password = string(hashedPassword)
models.DB.Create(&input)
c.JSON(http.StatusOK, gin.H{"message": "user registered successfully"})
}
2. Login and Generate JWT
func Login(c *gin.Context) {
var input models.User
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var user models.User
models.DB.Where("username = ?", input.Username).First(&user)
// Verify password
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(input.Password)); err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"})
return
}
// Create JWT
claims := jwt.MapClaims{
"username": user.Username,
"exp": time.Now().Add(time.Hour * 1).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, _ := token.SignedString([]byte("secret_key"))
c.JSON(http.StatusOK, gin.H{"token": tokenString})
}
3. Middleware to 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 token"})
c.Abort()
return
}
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte("secret_key"), nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
c.Abort()
return
}
claims := token.Claims.(jwt.MapClaims)
c.Set("username", claims["username"])
c.Next()
}
}
4. Protected Route Example
func ProtectedEndpoint(c *gin.Context) {
username := c.GetString("username")
c.JSON(http.StatusOK, gin.H{
"message": "Welcome " + username,
})
}
5. Routes Setup
r := gin.Default()
r.POST("/register", controllers.Register)
r.POST("/login", controllers.Login)
protected := r.Group("/api")
protected.Use(middlewares.AuthMiddleware())
{
protected.GET("/posts", controllers.ProtectedEndpoint)
}
Running the Project
- Clone repo:
git clone https://github.com/vickychhetri/go-jwt-auth-use.git cd go-jwt-auth-use go run main.go - Register a user:
POST /registerwith{ "username": "vicky", "password": "vicky@12345" } - Login:
POST /login→ copy the JWT. - Access protected route:
GET /api/postswith header:Authorization: <jwt-token>
Key Takeaways
- JWT allows stateless authentication.
- Always hash passwords before storing.
- Always set expiration (
exp) for tokens. - Keep your secret key safe (use environment variables in production).
- Middleware ensures only valid tokens can access protected routes.
Repo
👉 Full source code is here: go-jwt-auth-use