{
  "slug": "Securing-the-Admin-Layer-in-Go-with-JWT--A-Step-by-Step-Integration-Guide----cf5bcced97e3",
  "title": "Securing the Admin Layer in Go with JWT: A Step-by-Step Integration Guide 🔐",
  "subtitle": "In modern web applications, securing administrative endpoints is essential. In this article, we’ll walk through how to integrate JWT",
  "excerpt": "In modern web applications, securing administrative endpoints is essential. In this article, we’ll walk through how to integrate JWT",
  "date": "2025-10-13",
  "tags": [
    "Go",
    "Security"
  ],
  "readingTime": "4 min",
  "url": "https://medium.com/@mobinshaterian/securing-the-admin-layer-in-go-with-jwt-a-step-by-step-integration-guide-cf5bcced97e3",
  "hero": "https://cdn-images-1.medium.com/max/800/1*Z1n1Pu3MHvCyouteWQ0EAQ.png",
  "content": [
    {
      "type": "heading",
      "level": 2,
      "text": "Securing the Admin Layer in Go with JWT: A Step-by-Step Integration Guide 🔐"
    },
    {
      "type": "paragraph",
      "html": "In modern web applications, securing administrative endpoints is essential. In this article, we’ll walk through how to integrate JWT (JSON Web Token) authentication into the admin layer of a Go project using the Gin framework. We’ll use the go-simple project as our working example."
    },
    {
      "type": "image",
      "src": "https://cdn-images-1.medium.com/max/800/1*Z1n1Pu3MHvCyouteWQ0EAQ.png",
      "alt": "Securing the Admin Layer in Go with JWT: A Step-by-Step Integration Guide 🔐",
      "caption": "",
      "width": 1024,
      "height": 1024
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Go-simple"
    },
    {
      "type": "paragraph",
      "html": "<strong>go-simple</strong> is a clean, modular Go project designed to handle HTTP and gRPC requests with clarity and maintainability. It’s ideal for developers who want to explore idiomatic Go patterns, experiment with scalable architecture, and integrate modern tooling like Uber FX, Buf, and gRPC-Gateway."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🎯 Project Goals"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "<strong>Simplicity</strong>: Minimal boilerplate, clear separation of concerns, and intuitive structure.",
        "<strong>Modularity</strong>: Organized by domain, not just layers — making it easy to scale and refactor.",
        "<strong>Extensibility</strong>: Built to support JWT authentication, Swagger documentation, and lifecycle hooks.",
        "<strong>Real-world readiness</strong>: Includes patterns for configuration management, dependency injection, and testing."
      ]
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧱 Project Structure Overview"
    },
    {
      "type": "paragraph",
      "html": "The <code>go-simple</code> project follows a modular design with clear separation of concerns. The admin product controller is defined in:"
    },
    {
      "type": "code",
      "lang": "text",
      "code": "internal/product/controller/admin.go"
    },
    {
      "type": "paragraph",
      "html": "Configuration is loaded via Viper in:"
    },
    {
      "type": "code",
      "lang": "text",
      "code": "internal/config/config.go"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🔐 Step 1: Extend Configuration for JWT"
    },
    {
      "type": "paragraph",
      "html": "First, we add JWT-related fields to the <code>Config</code> struct:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "type Config struct {\n    // existing fields...\n    JWTSecret string\n    JWTExpiryHours int\n}"
    },
    {
      "type": "paragraph",
      "html": "Then, in <code>NewConfig()</code>:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "v.SetDefault(\"JWT_SECRET\", \"your-secret-key\")v.SetDefault(\"JWT_EXPIRY_HOURS\", 72)cfg := &Config{\n// existing fields...    JWTSecret:      v.GetString(\"JWT_SECRET\"),    JWTExpiryHours:\nv.GetInt(\"JWT_EXPIRY_HOURS\"),}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧪 Step 2: Token Generation"
    },
    {
      "type": "paragraph",
      "html": "In <code>internal/auth/token.go</code>, we update the <code>GenerateToken</code> function to use the config:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "func GenerateToken(cfg *config.Config, adminID string) (string, error) {\n    claims := jwt.MapClaims{\n        \"admin_id\": adminID, \"exp\": time.Now().Add(time.Hour *\n        time.Duration(cfg.JWTExpiryHours)).Unix(),\n    }\n    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n    return token.SignedString([]byte(cfg.JWTSecret))\n}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧱 Step 3: JWT Middleware for Gin"
    },
    {
      "type": "paragraph",
      "html": "In <code>internal/middleware/jwt.go</code>, we define a Gin-compatible middleware:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "func JWTAuth(cfg *config.Config) gin.HandlerFunc {\n    return func(c *gin.Context) {\n        authHeader := c.GetHeader(\"Authorization\") if authHeader == \"\" ||\n        !strings.HasPrefix(authHeader, \"Bearer \") {\n            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{\n                \"error\": \"Missing or invalid token\"\n            }) return\n        }\n        tokenStr := strings.TrimPrefix(authHeader, \"Bearer \") token, err := jwt.Parse(tokenStr,\n        func(token *jwt.Token) (interface{\n        }, error) {\n            return []byte(cfg.JWTSecret), nil\n        }) if err != nil || !token.Valid {\n            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{\n                \"error\": \"Invalid token\"\n            }) return\n        }\n        c.Next()\n    }\n}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🔗 Step 4: Protect Admin Routes"
    },
    {
      "type": "paragraph",
      "html": "In <code>admin.go</code>, we inject the middleware into the route group:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "func (c *AdminProduct) RegisterRoutes(rg *gin.RouterGroup, cfg *config.Config) {\n    auth := middleware.JWTAuth(cfg)\n    rg.POST(\"/\", auth, c.CreateProduct)\n    rg.PUT(\"/:id\", auth, c.UpdateProduct)\n    rg.DELETE(\"/:id\", auth, c.DeleteProduct)\n    rg.GET(\"/:id\", auth, c.GetProductByID)\n    rg.GET(\"/\", auth, c.ListProducts)\n}"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧪 Step 5: Authenticated Integration Tests"
    },
    {
      "type": "paragraph",
      "html": "We updated all test helpers to include the JWT token in the <code>Authorization</code> header:"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "✅ Create Product"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "req.Header.Set(\"Authorization\", \"Bearer \"+token)"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "✅ List Products"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "req, _ := http.NewRequest(http.MethodGet, addr, nil)req.Header.Set(\"Authorization\", \"Bearer \"+token)"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "✅ Get Product by ID"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"%s/%d\", addr, product.ID),\nnil)req.Header.Set(\"Authorization\", \"Bearer \"+token)"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "✅ Update Product"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "req, _ := http.NewRequest(http.MethodPut, fmt.Sprintf(\"%s/%d\", addr, product.ID),\nbody)req.Header.Set(\"Authorization\", \"Bearer \"+token)"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "✅ Delete Product"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "req, _ := http.NewRequest(http.MethodDelete, fmt.Sprintf(\"%s/%d\", addr, product.ID),\nnil)req.Header.Set(\"Authorization\", \"Bearer \"+token)"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "✅ Verify Deletion"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf(\"%s/%d\", addr, product.ID),\nnil)req.Header.Set(\"Authorization\", \"Bearer \"+token)"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧩 Final Thoughts"
    },
    {
      "type": "paragraph",
      "html": "This integration ensures that all admin endpoints are protected by JWT, with configuration-driven secrets and expiration. The test suite now mirrors real-world usage by authenticating each request, improving both security and test fidelity."
    },
    {
      "type": "paragraph",
      "html": "If you’re building a scalable Go service, this pattern — modular, idiomatic, and secure — lays a strong foundation for future enhancements like role-based access, refresh tokens, and OAuth integration."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "Git commit"
    },
    {
      "type": "heading",
      "level": 3,
      "text": "Add Swagger"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🔐 How to Add JWT Authentication to Swagger in Go"
    },
    {
      "type": "paragraph",
      "html": "If you’re building a secure API in Go and using Swagger for documentation, adding JWT (JSON Web Token) support to Swagger UI is a must. It allows developers to test protected endpoints directly from the browser by authorizing with a token."
    },
    {
      "type": "paragraph",
      "html": "This guide walks you through integrating JWT Bearer authentication into Swagger using <code>swaggo/swag</code> and <code>gin-swagger</code>."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "✍️ Step 1: Define JWT Security Scheme"
    },
    {
      "type": "paragraph",
      "html": "In your main Swagger annotations file (usually <code>main.go</code>), add the following block:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "// @securityDefinitions.apikey BearerAuth// @in header// @name Authorization// @description Enter\nyour JWT token in the format: Bearer <token>"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🔍 What This Does"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "Declares a security scheme named <code>BearerAuth</code>",
        "Tells Swagger to send the token in the <code>Authorization</code> header",
        "Adds a helpful description to guide users on formatting"
      ]
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🔐 Step 2: Annotate Protected Endpoints"
    },
    {
      "type": "paragraph",
      "html": "For each route that requires JWT authentication, add:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "// @Security BearerAuth"
    },
    {
      "type": "paragraph",
      "html": "Example:"
    },
    {
      "type": "code",
      "lang": "go",
      "code": "// @Summary Create a new product// @Description Admin-only endpoint to create a product// @Tags\nAdmin// @Accept json// @Produce json// @Param product body dto.AdminCreateProductRequest true\n\"Product info\"// @Success 201 {object} dto.ProductResponse// @Security BearerAuth// @Router\n/admin/products [post]"
    },
    {
      "type": "paragraph",
      "html": "Repeat this for all admin routes: <code>POST</code>, <code>PUT</code>, <code>DELETE</code>, <code>GET /:id</code>, and <code>GET /</code>."
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🚀 Step 3: Serve Swagger UI"
    },
    {
      "type": "paragraph",
      "html": "Make sure Swagger UI is wired into your Gin router:"
    },
    {
      "type": "code",
      "lang": "python",
      "code": "import ginSwagger \"github.com/swaggo/gin-swagger\"\nimport swaggerFiles \"github.com/swaggo/files\"r.GET(\"/swagger/*any\",\nginSwagger.WrapHandler(swaggerFiles.Handler))"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "🧪 Step 4: Test It"
    },
    {
      "type": "list",
      "ordered": false,
      "items": [
        "Run your app and visit <code>/swagger/index.html</code>",
        "Click the 🔒 “Authorize” button",
        "Paste your token like this:"
      ]
    },
    {
      "type": "code",
      "lang": "text",
      "code": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    },
    {
      "type": "paragraph",
      "html": "Swagger will now include this token in all requests to protected endpoints"
    },
    {
      "type": "heading",
      "level": 2,
      "text": "✅ Result"
    },
    {
      "type": "paragraph",
      "html": "You now have a fully interactive Swagger UI that supports JWT authentication. This improves developer experience, makes testing easier, and ensures your API documentation reflects real-world usage."
    }
  ]
}
