SDKsGo SDK
HTTP Middleware
HelloJohn authentication middleware for Go HTTP servers — net/http, Chi, Gin, and Echo integration patterns.
HTTP Middleware
Authentication middleware for Go HTTP servers using net/http, Chi, Gin, or Echo.
net/http
package middleware
import (
"context"
"net/http"
"strings"
hellojohn "github.com/hellojohn/hellojohn-go"
)
type contextKey string
const authKey contextKey = "auth"
type AuthClaims struct {
UserID string
OrgID string
OrgRole string
}
func Auth(hj *hellojohn.Client) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := bearerToken(r)
if token == "" {
http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
return
}
payload, err := hj.VerifyToken(r.Context(), token)
if err != nil {
http.Error(w, `{"error":"invalid token"}`, http.StatusUnauthorized)
return
}
claims := AuthClaims{
UserID: payload.Sub,
OrgID: payload.OrgID,
OrgRole: payload.OrgRole,
}
ctx := context.WithValue(r.Context(), authKey, claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func GetAuth(r *http.Request) (AuthClaims, bool) {
claims, ok := r.Context().Value(authKey).(AuthClaims)
return claims, ok
}
func bearerToken(r *http.Request) string {
h := r.Header.Get("Authorization")
if !strings.HasPrefix(h, "Bearer ") {
return ""
}
return strings.TrimPrefix(h, "Bearer ")
}Usage:
mux := http.NewServeMux()
auth := middleware.Auth(hj)
mux.Handle("GET /api/me", auth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, _ := middleware.GetAuth(r)
json.NewEncoder(w).Encode(map[string]string{"userId": claims.UserID})
})))Chi Router
import "github.com/go-chi/chi/v5"
r := chi.NewRouter()
r.Use(middleware.Auth(hj)) // Apply globally
r.Get("/api/me", func(w http.ResponseWriter, r *http.Request) {
claims, _ := middleware.GetAuth(r)
json.NewEncoder(w).Encode(map[string]string{"userId": claims.UserID})
})
// Group with auth
r.Group(func(r chi.Router) {
r.Use(middleware.Auth(hj))
r.Get("/api/protected", protectedHandler)
r.Post("/api/data", createDataHandler)
})Gin
import "github.com/gin-gonic/gin"
func HJAuth(hj *hellojohn.Client) gin.HandlerFunc {
return func(c *gin.Context) {
token := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing token"})
c.Abort()
return
}
payload, err := hj.VerifyToken(c.Request.Context(), token)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
c.Abort()
return
}
c.Set("userId", payload.Sub)
c.Set("orgId", payload.OrgID)
c.Next()
}
}
// Usage
r := gin.Default()
protected := r.Group("/api", HJAuth(hj))
protected.GET("/me", func(c *gin.Context) {
userID := c.GetString("userId")
c.JSON(200, gin.H{"userId": userID})
})Role Guard
func RequireRole(role string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, ok := middleware.GetAuth(r)
if !ok {
http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
return
}
if claims.OrgRole != role && claims.OrgRole != "owner" {
http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}
// Usage: require admin role
mux.Handle("DELETE /api/users/{id}",
auth(RequireRole("admin")(http.HandlerFunc(deleteUserHandler))))