Skip to content

Commit e91cc6f

Browse files
feat: increase unit test coverage.
1 parent 29ccc7a commit e91cc6f

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

pkg/nodeauth/jwt/node_jwt_authenticator_test.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,3 +425,188 @@ func TestNewNodeJWTAuthenticator(t *testing.T) {
425425
assert.NotNil(t, authenticator.parser)
426426
assert.Equal(t, logger, authenticator.logger)
427427
}
428+
429+
func TestNodeJWTAuthenticator_WithoutLeeway_StrictValidation(t *testing.T) {
430+
t.Run("token expired by 1 second without leeway should be rejected", func(t *testing.T) {
431+
// Given
432+
privateKey, csaPubKey := createValidatorTestKeys()
433+
mockProvider := &mocks.NodeAuthProvider{}
434+
// No leeway option provided - strict validation
435+
authenticator := NewNodeJWTAuthenticator(mockProvider, createTestLogger())
436+
437+
testRequest := testRequest{Field: "test-request"}
438+
digest := utils.CalculateRequestDigest(testRequest)
439+
440+
// Create a token that expired 1 second ago
441+
now := time.Now()
442+
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, types.NodeJWTClaims{
443+
PublicKey: hex.EncodeToString(csaPubKey),
444+
Digest: digest,
445+
RegisteredClaims: jwt.RegisteredClaims{
446+
Issuer: hex.EncodeToString(csaPubKey),
447+
Subject: hex.EncodeToString(csaPubKey),
448+
ExpiresAt: jwt.NewNumericDate(now.Add(-1 * time.Second)), // Expired 1s ago
449+
IssuedAt: jwt.NewNumericDate(now.Add(-1 * time.Hour)),
450+
},
451+
})
452+
453+
jwtToken, err := token.SignedString(privateKey)
454+
require.NoError(t, err)
455+
456+
// When: Authenticate JWT without leeway
457+
valid, claims, err := authenticator.AuthenticateJWT(context.Background(), jwtToken, testRequest)
458+
459+
// Expect: Should fail as no leeway is configured
460+
require.Error(t, err)
461+
assert.False(t, valid)
462+
assert.NotNil(t, claims)
463+
assert.Contains(t, err.Error(), "token is expired")
464+
})
465+
466+
t.Run("token issued 1 second in future without leeway should be rejected", func(t *testing.T) {
467+
// Given
468+
privateKey, csaPubKey := createValidatorTestKeys()
469+
mockProvider := &mocks.NodeAuthProvider{}
470+
// No leeway option provided - strict validation
471+
authenticator := NewNodeJWTAuthenticator(mockProvider, createTestLogger())
472+
473+
testRequest := testRequest{Field: "test-request"}
474+
digest := utils.CalculateRequestDigest(testRequest)
475+
476+
// Create a token issued 1 second in the future
477+
now := time.Now()
478+
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, types.NodeJWTClaims{
479+
PublicKey: hex.EncodeToString(csaPubKey),
480+
Digest: digest,
481+
RegisteredClaims: jwt.RegisteredClaims{
482+
Issuer: hex.EncodeToString(csaPubKey),
483+
Subject: hex.EncodeToString(csaPubKey),
484+
ExpiresAt: jwt.NewNumericDate(now.Add(workflowJWTExpiration)),
485+
IssuedAt: jwt.NewNumericDate(now.Add(1 * time.Second)), // Issued 1s in future
486+
},
487+
})
488+
489+
jwtToken, err := token.SignedString(privateKey)
490+
require.NoError(t, err)
491+
492+
// When: Authenticate JWT without leeway
493+
valid, claims, err := authenticator.AuthenticateJWT(context.Background(), jwtToken, testRequest)
494+
495+
// Expect: Should fail as no leeway is configured
496+
require.Error(t, err)
497+
assert.False(t, valid)
498+
assert.NotNil(t, claims)
499+
assert.Contains(t, err.Error(), "used before issued")
500+
})
501+
}
502+
503+
func TestNodeJWTAuthenticator_WithLeeway_CustomDurations(t *testing.T) {
504+
t.Run("custom leeway of 10 seconds allows token expired 8 seconds ago", func(t *testing.T) {
505+
// Given
506+
privateKey, csaPubKey := createValidatorTestKeys()
507+
mockProvider := &mocks.NodeAuthProvider{}
508+
mockProvider.On("IsNodePubKeyTrusted", mock.Anything, csaPubKey).Return(true, nil)
509+
// Custom 10 second leeway
510+
authenticator := NewNodeJWTAuthenticator(mockProvider, createTestLogger(), WithLeeway(10*time.Second))
511+
512+
testRequest := testRequest{Field: "test-request"}
513+
digest := utils.CalculateRequestDigest(testRequest)
514+
515+
// Create a token that expired 8 seconds ago (within 10s leeway)
516+
now := time.Now()
517+
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, types.NodeJWTClaims{
518+
PublicKey: hex.EncodeToString(csaPubKey),
519+
Digest: digest,
520+
RegisteredClaims: jwt.RegisteredClaims{
521+
Issuer: hex.EncodeToString(csaPubKey),
522+
Subject: hex.EncodeToString(csaPubKey),
523+
ExpiresAt: jwt.NewNumericDate(now.Add(-8 * time.Second)), // Expired 8s ago
524+
IssuedAt: jwt.NewNumericDate(now.Add(-1 * time.Hour)),
525+
},
526+
})
527+
528+
jwtToken, err := token.SignedString(privateKey)
529+
require.NoError(t, err)
530+
531+
// When: Authenticate JWT
532+
valid, claims, err := authenticator.AuthenticateJWT(context.Background(), jwtToken, testRequest)
533+
534+
// Expect: Should succeed with 10s leeway
535+
require.NoError(t, err)
536+
assert.True(t, valid)
537+
assert.NotNil(t, claims)
538+
mockProvider.AssertExpectations(t)
539+
})
540+
541+
t.Run("custom leeway of 2 seconds rejects token expired 3 seconds ago", func(t *testing.T) {
542+
// Given
543+
privateKey, csaPubKey := createValidatorTestKeys()
544+
mockProvider := &mocks.NodeAuthProvider{}
545+
// Small 2 second leeway
546+
authenticator := NewNodeJWTAuthenticator(mockProvider, createTestLogger(), WithLeeway(2*time.Second))
547+
548+
testRequest := testRequest{Field: "test-request"}
549+
digest := utils.CalculateRequestDigest(testRequest)
550+
551+
// Create a token that expired 3 seconds ago (beyond 2s leeway)
552+
now := time.Now()
553+
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, types.NodeJWTClaims{
554+
PublicKey: hex.EncodeToString(csaPubKey),
555+
Digest: digest,
556+
RegisteredClaims: jwt.RegisteredClaims{
557+
Issuer: hex.EncodeToString(csaPubKey),
558+
Subject: hex.EncodeToString(csaPubKey),
559+
ExpiresAt: jwt.NewNumericDate(now.Add(-3 * time.Second)), // Expired 3s ago
560+
IssuedAt: jwt.NewNumericDate(now.Add(-1 * time.Hour)),
561+
},
562+
})
563+
564+
jwtToken, err := token.SignedString(privateKey)
565+
require.NoError(t, err)
566+
567+
// When: Authenticate JWT
568+
valid, claims, err := authenticator.AuthenticateJWT(context.Background(), jwtToken, testRequest)
569+
570+
// Expect: Should fail as it's beyond 2s leeway
571+
require.Error(t, err)
572+
assert.False(t, valid)
573+
assert.NotNil(t, claims)
574+
assert.Contains(t, err.Error(), "token is expired")
575+
})
576+
577+
t.Run("zero leeway behaves like no leeway option", func(t *testing.T) {
578+
// Given
579+
privateKey, csaPubKey := createValidatorTestKeys()
580+
mockProvider := &mocks.NodeAuthProvider{}
581+
// Explicitly set zero leeway
582+
authenticator := NewNodeJWTAuthenticator(mockProvider, createTestLogger(), WithLeeway(0*time.Second))
583+
584+
testRequest := testRequest{Field: "test-request"}
585+
digest := utils.CalculateRequestDigest(testRequest)
586+
587+
// Create a token that expired 1 second ago
588+
now := time.Now()
589+
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, types.NodeJWTClaims{
590+
PublicKey: hex.EncodeToString(csaPubKey),
591+
Digest: digest,
592+
RegisteredClaims: jwt.RegisteredClaims{
593+
Issuer: hex.EncodeToString(csaPubKey),
594+
Subject: hex.EncodeToString(csaPubKey),
595+
ExpiresAt: jwt.NewNumericDate(now.Add(-1 * time.Second)), // Expired 1s ago
596+
IssuedAt: jwt.NewNumericDate(now.Add(-1 * time.Hour)),
597+
},
598+
})
599+
600+
jwtToken, err := token.SignedString(privateKey)
601+
require.NoError(t, err)
602+
603+
// When: Authenticate JWT
604+
valid, claims, err := authenticator.AuthenticateJWT(context.Background(), jwtToken, testRequest)
605+
606+
// Expect: Should fail with zero leeway
607+
require.Error(t, err)
608+
assert.False(t, valid)
609+
assert.NotNil(t, claims)
610+
assert.Contains(t, err.Error(), "token is expired")
611+
})
612+
}

0 commit comments

Comments
 (0)