Skip to content

Commit f078b71

Browse files
committed
Auto-commit
1 parent cfb2cfa commit f078b71

2 files changed

Lines changed: 129 additions & 1 deletion

File tree

auth-service/src/auth_service.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,20 @@ func (s *AuthServiceServer) ValidateToken(ctx context.Context, req *auth.Validat
186186
}
187187

188188
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
189+
// Validate JTI is UUID v4
190+
if jti, exists := claims["jti"].(string); exists {
191+
parsedUUID, err := uuid.Parse(jti)
192+
if err != nil || uuid.Version(parsedUUID) != 4 {
193+
return nil, errors.New("invalid JTI format")
194+
}
195+
}
196+
// Validate JTI is UUID v4
197+
if jti, exists := claims["jti"].(string); exists {
198+
parsedUUID, err := uuid.Parse(jti)
199+
if err != nil || uuid.Version(parsedUUID) != 4 {
200+
return nil, errors.New("invalid JTI format")
201+
}
202+
}
189203
userID := claims["sub"].(string)
190204
username := claims["username"].(string)
191205
expiresAt := int64(claims["exp"].(float64))
@@ -234,7 +248,13 @@ func (s *AuthServiceServer) RefreshToken(ctx context.Context, req *auth.RefreshT
234248
if !ok {
235249
return nil, status.Error(codes.Unauthenticated, "invalid token claims")
236250
}
237-
251+
// Validate JTI is UUID v4
252+
if jti, exists := claims["jti"].(string); exists {
253+
parsedUUID, err := uuid.Parse(jti)
254+
if err != nil || uuid.Version(parsedUUID) != 4 {
255+
return nil, status.Error(codes.Unauthenticated, "invalid JTI format")
256+
}
257+
}
238258
userID := claims["sub"].(string)
239259

240260
// Get user by ID to retrieve username
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
"github.com/golang-jwt/jwt/v5"
6+
"github.com/google/uuid"
7+
"google.golang.org/grpc/codes"
8+
"google.golang.org/grpc/status"
9+
)
10+
11+
var testKey = []byte("test-secret")
12+
13+
func generateTestToken(claims jwt.MapClaims) (string, error) {
14+
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
15+
return token.SignedString(testKey)
16+
}
17+
18+
func TestValidateTokenWithValidUUIDv4(t *testing.T) {
19+
validUUID := uuid.New().String()
20+
claims := jwt.MapClaims{"jti": validUUID, "sub": "user123"}
21+
tokenString, _ := generateTestToken(claims)
22+
23+
_, err := ValidateToken(tokenString)
24+
if err != nil {
25+
t.Fatalf("Expected no error, got %v", err)
26+
}
27+
}
28+
29+
func TestValidateTokenWithInvalidUUIDFormat(t *testing.T) {
30+
invalidUUID := "not-a-uuid"
31+
claims := jwt.MapClaims{"jti": invalidUUID, "sub": "user123"}
32+
tokenString, _ := generateTestToken(claims)
33+
34+
_, err := ValidateToken(tokenString)
35+
if err == nil {
36+
t.Fatal("Expected error, got none")
37+
}
38+
expectedErr := status.Error(codes.Unauthenticated, "invalid JTI format")
39+
if err.Error() != expectedErr.Error() {
40+
t.Fatalf("Expected error %v, got %v", expectedErr, err)
41+
}
42+
}
43+
44+
func TestValidateTokenWithMissingJTI(t *testing.T) {
45+
claims := jwt.MapClaims{"sub": "user123"}
46+
tokenString, _ := generateTestToken(claims)
47+
48+
_, err := ValidateToken(tokenString)
49+
if err != nil {
50+
t.Fatalf("Expected no error, got %v", err)
51+
}
52+
}
53+
54+
func TestValidateTokenWithNonV4UUID(t *testing.T) {
55+
nonV4UUID := "123e4567-e89b-11d3-a75a-426614174000" // UUID v1
56+
claims := jwt.MapClaims{"jti": nonV4UUID, "sub": "user123"}
57+
tokenString, _ := generateTestToken(claims)
58+
59+
_, err := ValidateToken(tokenString)
60+
if err == nil {
61+
t.Fatal("Expected error, got none")
62+
}
63+
expectedErr := status.Error(codes.Unauthenticated, "invalid JTI format")
64+
if err.Error() != expectedErr.Error() {
65+
t.Fatalf("Expected error %v, got %v", expectedErr, err)
66+
}
67+
}
68+
69+
func TestRefreshTokenWithValidUUIDv4(t *testing.T) {
70+
validUUID := uuid.New().String()
71+
claims := jwt.MapClaims{"jti": validUUID, "sub": "user123"}
72+
tokenString, _ := generateTestToken(claims)
73+
74+
_, err := RefreshToken(tokenString)
75+
if err != nil {
76+
t.Fatalf("Expected no error, got %v", err)
77+
}
78+
}
79+
80+
func TestRefreshTokenWithInvalidUUIDFormat(t *testing.T) {
81+
invalidUUID := "not-a-uuid"
82+
claims := jwt.MapClaims{"jti": invalidUUID, "sub": "user123"}
83+
tokenString, _ := generateTestToken(claims)
84+
85+
_, err := RefreshToken(tokenString)
86+
if err == nil {
87+
t.Fatal("Expected error, got none")
88+
}
89+
expectedErr := status.Error(codes.Unauthenticated, "invalid JTI format")
90+
if err.Error() != expectedErr.Error() {
91+
t.Fatalf("Expected error %v, got %v", expectedErr, err)
92+
}
93+
}
94+
95+
func TestRefreshTokenWithNonV4UUID(t *testing.T) {
96+
nonV4UUID := "123e4567-e89b-11d3-a75a-426614174000" // UUID v1
97+
claims := jwt.MapClaims{"jti": nonV4UUID, "sub": "user123"}
98+
tokenString, _ := generateTestToken(claims)
99+
100+
_, err := RefreshToken(tokenString)
101+
if err == nil {
102+
t.Fatal("Expected error, got none")
103+
}
104+
expectedErr := status.Error(codes.Unauthenticated, "invalid JTI format")
105+
if err.Error() != expectedErr.Error() {
106+
t.Fatalf("Expected error %v, got %v", expectedErr, err)
107+
}
108+
}

0 commit comments

Comments
 (0)