Describe the bug
When using the built-in JWT authentication middleware (jwt: Auth) with file upload endpoints, the middleware attempts to read the entire multipart/form-data request body (including large file contents) to search for the JWT token. This causes severe performance issues: the entire file content is printed to console, validation takes 5-30 seconds, and memory usage spikes to the file size.
To Reproduce
Steps to reproduce the behavior:
-
The code is
// core.api
syntax = "v1"
@server (
prefix: /api/file
jwt: Auth // Using built-in JWT middleware
)
service core-api {
@handler UploadFileHandler
post /upload (UploadFileRequest) returns (UploadFileResponse)
}
type UploadFileRequest {
Hash string `json:"hash,optional"`
Name string `json:"name,optional"`
Ext string `json:"ext,optional"`
Size int64 `json:"size,optional"`
Path string `json:"path,optional"`
}
type UploadFileResponse {
Identity string `json:"identity"`
}
// Handler code
func UploadFileHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
file, fileHeader, err := r.FormFile("file")
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
defer file.Close()
// Process file upload...
}
}
# Upload a large file (e.g., 100MB video) with JWT token in header
curl -X POST http://localhost:8888/api/file/upload \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-F "file=@large_video.mp4"
-
The error/issue is
# Console output (example):
[Thousands of lines of binary file content printed to console]
����JFIF��������....[binary data continues]....
# Response time: 15-30 seconds for a 100MB file
# Memory usage: Spikes to 100MB+ (file size)
# Expected: < 1 second response time, minimal memory usage
Expected behavior
- JWT middleware should only check for token in HTTP headers (
Authorization, X-Token) and query parameters (?token=xxx)
- JWT middleware should NOT parse form data when Content-Type is
multipart/form-data
- File upload should complete quickly (within seconds, only limited by network speed)
- Console should remain clean without printing file contents
- Memory usage should be minimal (streaming file upload, not loading entire file to memory)
Screenshots
Before (with built-in JWT middleware):
Console output:
����JFIF��������C��....[thousands of lines]....
[Binary file content printed to console during JWT validation]
Response time: 15-30 seconds ❌
Memory usage: 100MB+ (file size) ❌
After (with custom middleware that skips form parsing):
Console output:
[Clean, no binary data]
Response time: < 1 second ✅
Memory usage: < 10MB ✅
Environments
- OS: macOS Sonoma 14.x / Linux (Ubuntu 22.04)
- go-zero version: v1.6.6 (verified issue exists)
- goctl version: 1.9.2
- Go version: go1.20+
More description
Root Cause Analysis
The JWT middleware in go-zero searches for tokens in multiple locations, including form parameters:
// Pseudocode of current implementation (rest/handler/authhandler.go)
func (h *AuthorizeHandler) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 1. Check Authorization header ✅
token := r.Header.Get("Authorization")
// 2. Check query parameter ✅
if token == "" {
token = r.URL.Query().Get("token")
}
// 3. Check form parameter ❌ THIS IS THE PROBLEM
if token == "" {
token = r.FormValue("token") // <-- Reads entire multipart/form-data!
}
// Validate token...
}
}
The Problem: r.FormValue("token") internally calls r.ParseMultipartForm(), which parses the entire multipart/form-data request body, including all file uploads (potentially hundreds of MBs), just to check if there's a token field in the form.
Impact
This bug makes it impractical to use go-zero's JWT middleware with file upload endpoints in production:
- ❌ Large file uploads (videos, archives, datasets) become extremely slow
- ❌ High memory usage causes OOM in containerized environments
- ❌ Console pollution makes debugging impossible
- ❌ Poor user experience (30+ second upload times)
- ❌ Cannot handle concurrent file uploads
Suggested Solutions
Option 1: Skip Form Parsing for multipart/form-data (Recommended)
func (h *AuthorizeHandler) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
token = r.URL.Query().Get("token")
}
// Only check form if NOT multipart/form-data
contentType := r.Header.Get("Content-Type")
if token == "" && !strings.Contains(contentType, "multipart/form-data") {
token = r.FormValue("token")
}
// Continue validation...
}
}
Option 2: Add Configuration Option
# config.yaml
Auth:
AccessSecret: xxx
AccessExpire: 36000
SkipFormParsing: true # New option to skip form parsing
Option 3: Provide Lightweight JWT Middleware for File Uploads
Provide a separate middleware (e.g., jwt-lite: Auth) that only checks headers and query parameters.
Current Workaround
Users must implement a custom JWT middleware:
// Custom middleware that skips form parsing
type FileAuthMiddleware struct {
accessSecret string
accessExpire int64
}
func (m *FileAuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
token = strings.TrimPrefix(token, "Bearer ")
if token == "" {
token = r.Header.Get("X-Token")
}
if token == "" {
token = r.URL.Query().Get("token")
}
// Skip r.FormValue() entirely!
claims, err := ParseToken(token, m.accessSecret, m.accessExpire)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
// Store user info in context
ctx := context.WithValue(r.Context(), "user_id", claims.Id)
r = r.WithContext(ctx)
next(w, r)
}
}
Then use custom middleware instead of built-in JWT:
@server (
prefix: /api/file
middleware: FileAuthMiddleware // Custom middleware
)
Performance Comparison
| Metric |
Built-in JWT |
Custom Middleware |
Improvement |
| Auth time |
5-30 seconds |
< 100ms |
99%+ |
| Memory usage |
File size |
< 1MB |
Significant |
| Console output |
Binary data |
Clean |
✅ |
| Concurrent uploads |
❌ Breaks |
✅ Works |
✅ |
Related Issues
Similar problems exist in other frameworks:
- Express.js: Middleware order matters for multipart parsing
- Django: Custom authentication recommended for file uploads
- Spring Boot: MultipartResolver configuration needed
Test Case
func TestJWTMiddlewareWithFileUpload(t *testing.T) {
// Setup test server with JWT middleware
// Upload a 100MB file with valid JWT token in header
// Assert:
// 1. Request completes in < 5 seconds
// 2. Memory usage < 10MB (not loading file to memory)
// 3. No binary data printed to console
}
I'm willing to submit a PR to fix this issue if the maintainers agree on the approach. This significantly impacts go-zero's usability for file-heavy applications.
References
Describe the bug
When using the built-in JWT authentication middleware (
jwt: Auth) with file upload endpoints, the middleware attempts to read the entiremultipart/form-datarequest body (including large file contents) to search for the JWT token. This causes severe performance issues: the entire file content is printed to console, validation takes 5-30 seconds, and memory usage spikes to the file size.To Reproduce
Steps to reproduce the behavior:
The code is
The error/issue is
Expected behavior
Authorization,X-Token) and query parameters (?token=xxx)multipart/form-dataScreenshots
Before (with built-in JWT middleware):
After (with custom middleware that skips form parsing):
Environments
More description
Root Cause Analysis
The JWT middleware in go-zero searches for tokens in multiple locations, including form parameters:
The Problem:
r.FormValue("token")internally callsr.ParseMultipartForm(), which parses the entiremultipart/form-datarequest body, including all file uploads (potentially hundreds of MBs), just to check if there's atokenfield in the form.Impact
This bug makes it impractical to use go-zero's JWT middleware with file upload endpoints in production:
Suggested Solutions
Option 1: Skip Form Parsing for multipart/form-data (Recommended)
Option 2: Add Configuration Option
Option 3: Provide Lightweight JWT Middleware for File Uploads
Provide a separate middleware (e.g.,
jwt-lite: Auth) that only checks headers and query parameters.Current Workaround
Users must implement a custom JWT middleware:
Then use custom middleware instead of built-in JWT:
Performance Comparison
Related Issues
Similar problems exist in other frameworks:
Test Case
I'm willing to submit a PR to fix this issue if the maintainers agree on the approach. This significantly impacts go-zero's usability for file-heavy applications.
References
rest/handler/authhandler.go