Skip to content

Commit 4201bd5

Browse files
authored
fix(api): require exp claim when verifying access tokens (#779)
1 parent 47dcb55 commit 4201bd5

2 files changed

Lines changed: 35 additions & 8 deletions

File tree

livekit-api/livekit/api/access_token.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -233,14 +233,26 @@ def verify(self, token: str, *, verify_signature: bool = True) -> Claims:
233233
if verify_signature and (not self.api_key or not self.api_secret):
234234
raise ValueError("api_key and api_secret must be set")
235235

236-
claims = jwt.decode(
237-
token,
238-
key=self.api_secret or "",
239-
issuer=self.api_key or "",
240-
algorithms=["HS256"],
241-
leeway=self._leeway.total_seconds(),
242-
options={"verify_signature": verify_signature},
243-
)
236+
# First-party minters always set exp. Without this, a hand-rolled token
237+
# with a valid signature and no exp verifies forever (livekit/protocol#1706).
238+
if verify_signature:
239+
claims = jwt.decode(
240+
token,
241+
key=self.api_secret or "",
242+
issuer=self.api_key or "",
243+
algorithms=["HS256"],
244+
leeway=self._leeway.total_seconds(),
245+
options={"verify_signature": True, "require": ["exp"]},
246+
)
247+
else:
248+
claims = jwt.decode(
249+
token,
250+
key=self.api_secret or "",
251+
issuer=self.api_key or "",
252+
algorithms=["HS256"],
253+
leeway=self._leeway.total_seconds(),
254+
options={"verify_signature": False},
255+
)
244256
video_dict = claims.get("video", dict())
245257
video_dict = {camel_to_snake(k): v for k, v in video_dict.items()}
246258
video_dict = {k: v for k, v in video_dict.items() if k in VideoGrants.__dataclass_fields__}

tests/api/test_access_token.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import calendar
12
import datetime
23

4+
import jwt
35
import pytest
46
from livekit.api import AccessToken, TokenVerifier, VideoGrants, SIPGrants
57
from livekit.protocol.room import RoomConfiguration
@@ -101,3 +103,16 @@ def test_verify_token_expired() -> None:
101103
)
102104
with pytest.raises(Exception):
103105
token_verifier.verify(token)
106+
107+
108+
def test_verify_token_missing_exp() -> None:
109+
now = calendar.timegm(datetime.datetime.now(datetime.timezone.utc).utctimetuple())
110+
token = jwt.encode(
111+
{"sub": "test_identity", "iss": TEST_API_KEY, "nbf": now},
112+
TEST_API_SECRET,
113+
algorithm="HS256",
114+
)
115+
116+
token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET)
117+
with pytest.raises(Exception):
118+
token_verifier.verify(token)

0 commit comments

Comments
 (0)