package auth import ( "errors" "strconv" "testing" "time" jwt "github.com/golang-jwt/jwt/v5" ) func TestVerifierParsesLargeUserIDStringWithoutPrecisionLoss(t *testing.T) { t.Parallel() const userID int64 = 399999999999990001 token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "user_id": strconv.FormatInt(userID, 10), "app_code": "lalu", }) signed, err := token.SignedString([]byte("secret")) if err != nil { t.Fatalf("sign token failed: %v", err) } claims, err := NewVerifier("secret").Verify("Bearer " + signed) if err != nil { t.Fatalf("Verify failed: %v", err) } if claims.UserID != userID { t.Fatalf("user_id lost precision: got %d want %d", claims.UserID, userID) } } func TestVerifierDistinguishesExpiredAccessTokenFromInvalidCredentials(t *testing.T) { t.Parallel() expired := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "user_id": "42", "sid": "sess-expired", "exp": time.Now().Add(-time.Minute).Unix(), }) signed, err := expired.SignedString([]byte("secret")) if err != nil { t.Fatalf("sign expired token failed: %v", err) } if _, err := NewVerifier("secret").Verify("Bearer " + signed); !errors.Is(err, ErrAccessTokenExpired) { t.Fatalf("expired token must return ErrAccessTokenExpired, got %v", err) } if _, err := NewVerifier("different-secret").Verify("Bearer " + signed); errors.Is(err, ErrAccessTokenExpired) || err == nil { t.Fatalf("invalid signature must remain generic unauthorized, got %v", err) } contradictory := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "user_id": "42", "sid": "sess-invalid-window", "exp": time.Now().Add(-time.Minute).Unix(), "nbf": time.Now().Add(time.Hour).Unix(), }) contradictorySigned, err := contradictory.SignedString([]byte("secret")) if err != nil { t.Fatalf("sign contradictory token failed: %v", err) } if _, err := NewVerifier("secret").Verify("Bearer " + contradictorySigned); errors.Is(err, ErrAccessTokenExpired) || err == nil { t.Fatalf("expired token with another invalid time claim must remain generic unauthorized, got %v", err) } }