From fa07bcdd1de5ff1cdcbf3f4a10ac371bb0e25afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Thu, 15 Oct 2026 10:59:24 +0200 Subject: [PATCH] test(service): refresh keeps clientWrite from an encoded join token Start from an encoded join JWT, so the test also catches a grant field the decoder drops, and cover clientWrite alone and with write. Needs protocol with ObservabilityGrant.ClientWrite: land it together with that go.mod bump, as the pinned protocol decodes clientWrite away and the refresh would silently lose it. --- pkg/service/roommanager_token_test.go | 41 ++++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/pkg/service/roommanager_token_test.go b/pkg/service/roommanager_token_test.go index e2e389929..65853f9dc 100644 --- a/pkg/service/roommanager_token_test.go +++ b/pkg/service/roommanager_token_test.go @@ -2,6 +2,7 @@ package service import ( "testing" + "time" "github.com/stretchr/testify/require" @@ -12,32 +13,44 @@ import ( "github.com/livekit/livekit-server/pkg/rtc/types/typesfakes" ) -// refreshToken backs every refresh: the first one at join, the periodic tick and claims changes. +// Exercises refreshToken, the shared function behind every refresh, starting from an encoded join +// token so a grant field the pinned protocol cannot decode is caught too. func TestRefreshTokenKeepsObservabilityGrant(t *testing.T) { for name, obs := range map[string]*auth.ObservabilityGrant{ - "none": nil, - "write": {Write: true}, + "none": nil, + "write": {Write: true}, + "clientWrite": {ClientWrite: true}, + "write+clientWrite": {Write: true, ClientWrite: true}, } { t.Run(name, func(t *testing.T) { secret := utils.RandomSecret() r := &RoomManager{config: &config.Config{Keys: map[string]string{"APIkey": secret}}} + join, err := auth.NewAccessToken("APIkey", secret). + SetIdentity("alice"). + SetVideoGrant(&auth.VideoGrant{RoomJoin: true, Room: "room"}). + SetObservabilityGrant(obs). + SetValidFor(time.Minute). + ToJWT() + require.NoError(t, err) + p := &typesfakes.FakeLocalParticipant{} p.IdentityReturns("alice") - p.ClaimGrantsReturns(&auth.ClaimGrants{ - Identity: "alice", - Video: &auth.VideoGrant{RoomJoin: true, Room: "room"}, - Observability: obs, - }) + p.ClaimGrantsReturns(verifyToken(t, join, secret)) require.NoError(t, r.refreshToken(p)) require.Equal(t, 1, p.SendRefreshTokenCallCount()) - - v, err := auth.ParseAPIToken(p.SendRefreshTokenArgsForCall(0)) - require.NoError(t, err) - _, grants, err := v.Verify(secret) - require.NoError(t, err) - require.Equal(t, obs, grants.Observability, "kept as joined, never added") + require.Equal(t, obs, verifyToken(t, p.SendRefreshTokenArgsForCall(0), secret).Observability, + "kept as joined, never added") }) } } + +func verifyToken(t *testing.T, raw, secret string) *auth.ClaimGrants { + t.Helper() + v, err := auth.ParseAPIToken(raw) + require.NoError(t, err) + _, grants, err := v.Verify(secret) + require.NoError(t, err) + return grants +}