From 050b0e6b5783ebf97e73729bb27462798c05060e Mon Sep 17 00:00:00 2001 From: Vladislav Gruchik <4280527+vagruchi@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:17:07 +0100 Subject: [PATCH 1/3] Generate JWT valid 5 seconds before generation --- getstream/stream.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/getstream/stream.py b/getstream/stream.py index 5b9c26cb..756967ed 100644 --- a/getstream/stream.py +++ b/getstream/stream.py @@ -125,7 +125,8 @@ def _create_token( now = int(time.time()) claims = { - "iat": now, + # generate token valid from 5 seconds ago to avoid unauthorized error due to clock skew + "iat": now - 5, } if channel_cids is not None: From 8f5d9165ad4daeaec4adcb16be70216eda7e0f72 Mon Sep 17 00:00:00 2001 From: Vladislav Gruchik <4280527+vagruchi@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:23:25 +0100 Subject: [PATCH 2/3] fix test --- tests/test_video_integration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_video_integration.py b/tests/test_video_integration.py index f21aee53..fbfc37d1 100644 --- a/tests/test_video_integration.py +++ b/tests/test_video_integration.py @@ -62,7 +62,8 @@ def test_create_token_with_expiration(client: Stream): assert token is not None decoded = jwt.decode(token, client.api_secret, algorithms=["HS256"]) assert decoded["iat"] is not None - assert decoded["exp"] == decoded["iat"] + 10 + # 5 seconds to avoid clock skew and 10 seconds - expiration + assert decoded["exp"] == decoded["iat"] + 5 + 10 assert decoded["user_id"] == "tommaso" From c22a53b2146c03f695a66a8e002f181a98403294 Mon Sep 17 00:00:00 2001 From: Vladislav Gruchik <4280527+vagruchi@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:02:54 +0100 Subject: [PATCH 3/3] remove iat from server side token --- getstream/stream.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/getstream/stream.py b/getstream/stream.py index 756967ed..a9701e13 100644 --- a/getstream/stream.py +++ b/getstream/stream.py @@ -101,7 +101,9 @@ def create_token( if user_id is None or user_id == "": raise ValueError("user_id is required") - return self._create_token(user_id=user_id, expiration=expiration) + return self._create_token( + user_id=user_id, expiration=expiration, iat=int(time.time()) - 5 + ) def create_call_token( self, @@ -111,7 +113,11 @@ def create_call_token( expiration: int = None, ): return self._create_token( - user_id=user_id, call_cids=call_cids, role=role, expiration=expiration + user_id=user_id, + call_cids=call_cids, + role=role, + expiration=expiration, + iat=int(time.time() - 5), ) def _create_token( @@ -121,13 +127,14 @@ def _create_token( call_cids: List[str] = None, role: str = None, expiration=None, + iat: int = None, ): now = int(time.time()) - claims = { - # generate token valid from 5 seconds ago to avoid unauthorized error due to clock skew - "iat": now - 5, - } + claims = {} + + if iat is not None: + claims["iat"] = iat if channel_cids is not None: claims["channel_cids"] = channel_cids