10 Creating Authentication Middleware Using JWT with Httprouter in Golang
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/v4for managing JWT.github.com/julienschmidt/httprouterfor routing.
To install these libraries, use the following command:
1go get github.com/golang-jwt/jwt/v4 github.com/julienschmidt/httprouterStep 1: Creating the Project Structure
Create the following folder structure:
1project-root/
2├── main.go
3├── middleware/
4│ └── auth.go
5├── handlers/
6│ └── user.go
7├── utils/
8│ └── jwt.goThe 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:
1package main
2
3import (
4 "fmt"
5 "log"
6 "net/http"
7 "github.com/julienschmidt/httprouter"
8 "project-root/middleware"
9 "project-root/handlers"
10)
11
12func main() {
13 router := httprouter.New()
14
15 // Public endpoint without authentication
16 router.GET("/public", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
17 fmt.Fprint(w, "Public endpoint does not require authentication!\n")
18 })
19
20 // Private endpoint using authentication middleware
21 router.GET("/private", middleware.JWTAuth(handlers.PrivateHandler))
22
23 log.Println("Server running at http://localhost:8080")
24 log.Fatal(http.ListenAndServe(":8080", router))
25}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:
1package middleware
2
3import (
4 "net/http"
5 "strings"
6 "github.com/golang-jwt/jwt/v4"
7 "project-root/utils"
8 "github.com/julienschmidt/httprouter"
9)
10
11func JWTAuth(next httprouter.Handle) httprouter.Handle {
12 return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
13 tokenString := extractToken(r)
14 if tokenString == "" {
15 http.Error(w, "Authorization header missing", http.StatusUnauthorized)
16 return
17 }
18
19 // Validate token
20 claims, err := utils.ValidateJWT(tokenString)
21 if err != nil {
22 http.Error(w, "Invalid token", http.StatusUnauthorized)
23 return
24 }
25
26 // Proceed to the next handler
27 next(w, r, ps)
28 }
29}
30
31func extractToken(r *http.Request) string {
32 bearer := r.Header.Get("Authorization")
33 if bearer == "" || !strings.HasPrefix(bearer, "Bearer ") {
34 return ""
35 }
36 return strings.TrimPrefix(bearer, "Bearer ")
37}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:
1package handlers
2
3import (
4 "fmt"
5 "net/http"
6 "github.com/julienschmidt/httprouter"
7)
8
9func PrivateHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
10 fmt.Fprint(w, "Welcome to the private endpoint! Your token is valid.\n")
11}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:
1package utils
2
3import (
4 "errors"
5 "time"
6 "github.com/golang-jwt/jwt/v4"
7)
8
9var secretKey = []byte("super_secret")
10
11type CustomClaims struct {
12 Username string `json:"username"`
13 jwt.RegisteredClaims
14}
15
16func GenerateJWT(username string) (string, error) {
17 claims := CustomClaims{
18 Username: username,
19 RegisteredClaims: jwt.RegisteredClaims{
20 ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
21 },
22 }
23 token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
24 return token.SignedString(secretKey)
25}
26
27func ValidateJWT(tokenString string) (*CustomClaims, error) {
28 token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
29 return secretKey, nil
30 })
31 if err != nil {
32 return nil, err
33 }
34
35 claims, ok := token.Claims.(*CustomClaims)
36 if !ok || !token.Valid {
37 return nil, errors.New("invalid token")
38 }
39
40 return claims, nil
41}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:
1go run main.goTest the endpoints using curl or Postman.
1. Public Endpoint (No Token Required)
1curl http://localhost:8080/publicResponse:
1Public 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:
1curl -H "Authorization: Bearer <YOUR_TOKEN>" http://localhost:8080/privateIf the token is valid, the response will be:
1Welcome 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!