From 60730d81649c77e90672cf0c305b984c98e1c7fe Mon Sep 17 00:00:00 2001 From: Deluan Date: Fri, 21 Nov 2025 11:04:46 -0500 Subject: [PATCH] fix(deezer): reduce JWT token expiration buffer from 10 minutes to 1 minute Signed-off-by: Deluan --- core/agents/deezer/client_auth.go | 4 +-- core/agents/deezer/client_auth_test.go | 36 +++++++++++++------------- core/agents/deezer/client_test.go | 10 +++---- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/core/agents/deezer/client_auth.go b/core/agents/deezer/client_auth.go index 7de15ee55..c88c2bcb6 100644 --- a/core/agents/deezer/client_auth.go +++ b/core/agents/deezer/client_auth.go @@ -83,13 +83,13 @@ func (c *client) getJWT(ctx context.Context) (string, error) { return "", fmt.Errorf("deezer: failed to parse JWT token: %w", err) } - // Calculate TTL with a 10-minute buffer for clock skew and network delays + // Calculate TTL with a 1-minute buffer for clock skew and network delays expiresAt := token.Expiration() if expiresAt.IsZero() { return "", errors.New("deezer: JWT token has no expiration time") } - ttl := time.Until(expiresAt) - 10*time.Minute + ttl := time.Until(expiresAt) - 1*time.Minute if ttl <= 0 { return "", errors.New("deezer: JWT token already expired or expires too soon") } diff --git a/core/agents/deezer/client_auth_test.go b/core/agents/deezer/client_auth_test.go index e18916ae1..b0c2d195d 100644 --- a/core/agents/deezer/client_auth_test.go +++ b/core/agents/deezer/client_auth_test.go @@ -28,7 +28,7 @@ var _ = Describe("JWT Authentication", func() { Describe("getJWT", func() { Context("with a valid JWT response", func() { It("successfully fetches and caches a JWT token", func() { - testJWT := createTestJWT(1 * time.Hour) + testJWT := createTestJWT(5 * time.Minute) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s"}`, testJWT))), @@ -40,7 +40,7 @@ var _ = Describe("JWT Authentication", func() { }) It("returns the cached token on subsequent calls", func() { - testJWT := createTestJWT(1 * time.Hour) + testJWT := createTestJWT(5 * time.Minute) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s"}`, testJWT))), @@ -61,7 +61,7 @@ var _ = Describe("JWT Authentication", func() { }) It("parses the JWT expiration time correctly", func() { - expectedExpiration := time.Now().Add(2 * time.Hour) + expectedExpiration := time.Now().Add(5 * time.Minute) testToken, err := jwt.NewBuilder(). Expiration(expectedExpiration). Build() @@ -79,16 +79,16 @@ var _ = Describe("JWT Authentication", func() { Expect(token).ToNot(BeEmpty()) // Verify the token is cached until close to expiration - // The cache should expire 10 minutes before the JWT expires - expectedCacheExpiry := expectedExpiration.Add(-10 * time.Minute) + // The cache should expire 1 minute before the JWT expires + expectedCacheExpiry := expectedExpiration.Add(-1 * time.Minute) Expect(client.jwt.expiresAt).To(BeTemporally("~", expectedCacheExpiry, 2*time.Second)) }) }) Context("with JWT tokens that expire soon", func() { - It("rejects tokens that expire in less than 10 minutes", func() { - // Create a token that expires in 5 minutes - testJWT := createTestJWT(5 * time.Minute) + It("rejects tokens that expire in less than 1 minute", func() { + // Create a token that expires in 30 seconds (less than 1-minute buffer) + testJWT := createTestJWT(30 * time.Second) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s"}`, testJWT))), @@ -100,8 +100,8 @@ var _ = Describe("JWT Authentication", func() { }) It("rejects already expired tokens", func() { - // Create a token that expired 1 hour ago - testJWT := createTestJWT(-1 * time.Hour) + // Create a token that expired 1 minute ago + testJWT := createTestJWT(-1 * time.Minute) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s"}`, testJWT))), @@ -112,9 +112,9 @@ var _ = Describe("JWT Authentication", func() { Expect(err.Error()).To(ContainSubstring("JWT token already expired or expires too soon")) }) - It("accepts tokens that expire in exactly 11 minutes", func() { - // Create a token that expires in 11 minutes (just over the 10-minute buffer) - testJWT := createTestJWT(11 * time.Minute) + It("accepts tokens that expire in more than 1 minute", func() { + // Create a token that expires in 2 minutes (just over the 1-minute buffer) + testJWT := createTestJWT(2 * time.Minute) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s"}`, testJWT))), @@ -197,8 +197,8 @@ var _ = Describe("JWT Authentication", func() { Context("token caching behavior", func() { It("fetches a new token when the cached token expires", func() { - // First token expires in 15 minutes - firstJWT := createTestJWT(15 * time.Minute) + // First token expires in 5 minutes + firstJWT := createTestJWT(5 * time.Minute) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s"}`, firstJWT))), @@ -211,8 +211,8 @@ var _ = Describe("JWT Authentication", func() { // Manually expire the cached token client.jwt.expiresAt = time.Now().Add(-1 * time.Second) - // Second token with different expiration - secondJWT := createTestJWT(30 * time.Minute) + // Second token with different expiration (10 minutes) + secondJWT := createTestJWT(10 * time.Minute) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s"}`, secondJWT))), @@ -241,7 +241,7 @@ var _ = Describe("JWT Authentication", func() { }) It("returns true for valid tokens", func() { - cache.set("test-token", 1*time.Hour) + cache.set("test-token", 4*time.Minute) token, valid := cache.get() Expect(valid).To(BeTrue()) Expect(token).To(Equal("test-token")) diff --git a/core/agents/deezer/client_test.go b/core/agents/deezer/client_test.go index cbf3707f4..7e4f7a49f 100644 --- a/core/agents/deezer/client_test.go +++ b/core/agents/deezer/client_test.go @@ -47,8 +47,8 @@ var _ = Describe("client", func() { Describe("ArtistBio", func() { BeforeEach(func() { - // Mock the JWT token endpoint with a valid JWT that expires in 1 hour - testJWT := createTestJWT(1 * time.Hour) + // Mock the JWT token endpoint with a valid JWT that expires in 5 minutes + testJWT := createTestJWT(5 * time.Minute) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s","refresh_token":""}`, testJWT))), @@ -70,7 +70,7 @@ var _ = Describe("client", func() { It("uses the configured language", func() { client = newClient(httpClient, "fr") // Mock JWT token for the new client instance with a valid JWT - testJWT := createTestJWT(1 * time.Hour) + testJWT := createTestJWT(5 * time.Minute) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s","refresh_token":""}`, testJWT))), @@ -158,8 +158,8 @@ var _ = Describe("client", func() { }) It("handles JWT token that expires too soon", func() { - // Create a JWT that expires in 5 minutes (less than the 10-minute buffer) - expiredJWT := createTestJWT(5 * time.Minute) + // Create a JWT that expires in 30 seconds (less than the 1-minute buffer) + expiredJWT := createTestJWT(30 * time.Second) httpClient.mock("https://auth.deezer.com/login/anonymous", http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"jwt":"%s","refresh_token":""}`, expiredJWT))),