From 01749a7a74a609e309af939022f863eb16824a02 Mon Sep 17 00:00:00 2001 From: vlnst Date: Wed, 7 Oct 2026 03:50:04 +0300 Subject: [PATCH] fix: use standard JWT expiry claims for image proxy --- src/routes/image.go | 6 ----- src/types/imageProxy.go | 3 --- src/utils/images.go | 7 ++++-- src/utils/images_test.go | 51 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 src/utils/images_test.go diff --git a/src/routes/image.go b/src/routes/image.go index a070985..eaa49f9 100644 --- a/src/routes/image.go +++ b/src/routes/image.go @@ -4,7 +4,6 @@ import ( "anonymousoverflow/src/types" "fmt" "os" - "time" "github.com/gin-gonic/gin" "github.com/go-resty/resty/v2" @@ -45,11 +44,6 @@ func GetImage(c *gin.Context) { return } - if claims.Exp < time.Now().Unix() { - c.String(400, "Token expired") - return - } - // download the image client := resty.New() resp, err := client.R().Get(claims.ImageURL) diff --git a/src/types/imageProxy.go b/src/types/imageProxy.go index 2d9bdc4..ab4f765 100644 --- a/src/types/imageProxy.go +++ b/src/types/imageProxy.go @@ -7,8 +7,5 @@ type ImageProxyClaims struct { ImageURL string `json:"image_url"` - Iss int64 `json:"iss"` - Exp int64 `json:"exp"` - jwt.RegisteredClaims } diff --git a/src/utils/images.go b/src/utils/images.go index 4e57df4..9330cc3 100644 --- a/src/utils/images.go +++ b/src/utils/images.go @@ -42,11 +42,14 @@ func ReplaceImgTags(inHtml string) string { func generateImageProxyAuth(url string) (string, error) { // generate a jwt with types.ImageProxyClaims + now := time.Now() claims := types.ImageProxyClaims{ Action: "imageProxy", ImageURL: url, - Iss: time.Now().Unix(), - Exp: time.Now().Add(time.Minute).Unix(), + RegisteredClaims: jwt.RegisteredClaims{ + IssuedAt: jwt.NewNumericDate(now), + ExpiresAt: jwt.NewNumericDate(now.Add(time.Minute)), + }, } token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims) diff --git a/src/utils/images_test.go b/src/utils/images_test.go new file mode 100644 index 0000000..2ff82d1 --- /dev/null +++ b/src/utils/images_test.go @@ -0,0 +1,51 @@ +package utils + +import ( + "anonymousoverflow/src/types" + "errors" + "testing" + "time" + + "github.com/golang-jwt/jwt/v4" +) + +func TestImageProxyAuth(t *testing.T) { + const secret = "image-proxy-test-secret" + const imageURL = "https://example.com/image.png" + t.Setenv("JWT_SIGNING_SECRET", secret) + + authorization, err := generateImageProxyAuth(imageURL) + if err != nil { + t.Fatal(err) + } + + keyFunc := func(token *jwt.Token) (interface{}, error) { + return []byte(secret), nil + } + claims := &types.ImageProxyClaims{} + token, err := jwt.ParseWithClaims(authorization, claims, keyFunc) + if err != nil || !token.Valid { + t.Fatalf("fresh token rejected: %v", err) + } + if claims.Action != "imageProxy" || claims.ImageURL != imageURL { + t.Fatalf("unexpected claims: %+v", claims) + } + if claims.IssuedAt == nil || claims.ExpiresAt == nil { + t.Fatal("missing issue time or expiry") + } + if claims.ExpiresAt.Sub(claims.IssuedAt.Time) != time.Minute { + t.Fatal("token lifetime is not one minute") + } + + originalTimeFunc := jwt.TimeFunc + t.Cleanup(func() { jwt.TimeFunc = originalTimeFunc }) + jwt.TimeFunc = func() time.Time { return claims.ExpiresAt.Add(time.Second) } + + token, err = jwt.ParseWithClaims(authorization, &types.ImageProxyClaims{}, keyFunc) + if !errors.Is(err, jwt.ErrTokenExpired) { + t.Fatalf("expected expiry error from ParseWithClaims, got %v", err) + } + if token != nil && token.Valid { + t.Fatal("expired token is valid") + } +}