In modern application development, authentication is one of the most crucial components. JSON Web Tokens (JWT) is a popular method for handling token-based authentication. In this article, we will create an authentication middleware using JWT in Golang with the httprouter
library from julienschmidt.
This article is designed for beginner programmers with detailed steps and explanations to make it easy to follow.
Prerequisites
- Basic understanding of Golang: You should have a basic understanding of Golang, including functions, structures, and modules.
- Golang installed: Ensure you have Go installed on your system.
- Required libraries: We will use the following additional libraries:
github.com/golang-jwt/jwt/v4
for managing JWT.github.com/julienschmidt/httprouter
for routing.
To install these libraries, use the following command:
go get github.com/golang-jwt/jwt/v4 github.com/julienschmidt/httprouter
Step 1: Creating the Project Structure
Create the following folder structure:
project-root/
├── main.go
├── middleware/
│ └── auth.go
├── handlers/
│ └── user.go
├── utils/
│ └── jwt.go
The main.go
file serves as the entry point of the application, while the other folders organize various functions and middleware.
Step 2: Creating the Main File
Open main.go
and add the following code:
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
"project-root/middleware"
"project-root/handlers"
)
func main() {
router := httprouter.New()
// Public endpoint without authentication
router.GET("/public", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Public endpoint does not require authentication!\n")
})
// Private endpoint using authentication middleware
router.GET("/private", middleware.JWTAuth(handlers.PrivateHandler))
log.Println("Server running at http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", router))
}
The above code provides a minimal application setup using middleware for private endpoints.
Step 3: Creating JWT Middleware
Open middleware/auth.go
and add the following code:
package middleware
import (
"net/http"
"strings"
"github.com/golang-jwt/jwt/v4"
"project-root/utils"
"github.com/julienschmidt/httprouter"
)
func JWTAuth(next httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
tokenString := extractToken(r)
if tokenString == "" {
http.Error(w, "Authorization header missing", http.StatusUnauthorized)
return
}
// Validate token
claims, err := utils.ValidateJWT(tokenString)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Proceed to the next handler
next(w, r, ps)
}
}
func extractToken(r *http.Request) string {
bearer := r.Header.Get("Authorization")
if bearer == "" || !strings.HasPrefix(bearer, "Bearer ") {
return ""
}
return strings.TrimPrefix(bearer, "Bearer ")
}
This middleware extracts the token from the Authorization
header, validates it, and proceeds to the next handler if the token is valid.
Step 4: Creating the Private Endpoint Handler
Open handlers/user.go
and add the following code:
package handlers
import (
"fmt"
"net/http"
"github.com/julienschmidt/httprouter"
)
func PrivateHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome to the private endpoint! Your token is valid.\n")
}
This handler can only be accessed if the JWT middleware successfully validates the token.
Step 5: Creating Utility Functions for JWT
Open utils/jwt.go
and add the following code:
package utils
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v4"
)
var secretKey = []byte("super_secret")
type CustomClaims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
func GenerateJWT(username string) (string, error) {
claims := CustomClaims{
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(secretKey)
}
func ValidateJWT(tokenString string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return secretKey, nil
})
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*CustomClaims)
if !ok || !token.Valid {
return nil, errors.New("invalid token")
}
return claims, nil
}
This file contains functions for creating and validating JWT tokens. GenerateJWT
is used to create a token, while ValidateJWT
checks its validity.
Step 6: Testing the Application
Run the application with the following command:
go run main.go
Test the endpoints using curl
or Postman.
1. Public Endpoint (No Token Required)
curl http://localhost:8080/public
Response:
Public endpoint does not require authentication!
2. Private Endpoint (With Token)
First, generate a token using GenerateJWT
(implement it in another app or manually for testing). Then, use the token to access the private endpoint:
curl -H "Authorization: Bearer <YOUR_TOKEN>" http://localhost:8080/private
If the token is valid, the response will be:
Welcome to the private endpoint! Your token is valid.
Conclusion
You have successfully created an authentication middleware using JWT in Golang with httprouter
. With this approach, you can ensure that sensitive endpoints are only accessible to authenticated users. This middleware is also flexible for production applications.
Hopefully, this article helps you understand the basics of JWT in Golang. If you have any questions, feel free to ask or explore further!