From 82e5c3063f8474b42c4d34e9f2197f33b090f91f Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Wed, 18 Dec 2024 15:48:14 +0530 Subject: [PATCH 1/2] [ECO-5171][RTN22] Server initiated auth/client requested auth before expiry 1. Added implementation to renew token if client requests auth 30 seconds before expiry 2. Added doc comment for getServerTime method in AblyBroadcaster.php --- src/AblyBroadcaster.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/AblyBroadcaster.php b/src/AblyBroadcaster.php index c913800..a6b4b6c 100644 --- a/src/AblyBroadcaster.php +++ b/src/AblyBroadcaster.php @@ -74,7 +74,9 @@ public function __construct(AblyRest $ably, $config) } /** - * @return int + * Get the current server time adjusted by the Ably server time difference (if clock difference exists). + * + * @return int The current server time in seconds. */ private function getServerTime() { @@ -217,6 +219,14 @@ public function getSignedToken($channelName, $token, $clientId, $guardedChannelC $iat = $payload['iat']; $exp = $payload['exp']; $channelClaims = json_decode($payload['x-ably-capability'], true); + + // Check if the token is about to expire and renew it if necessary + // The Laravel Echo client typically initiates token renewal 30 seconds before expiry + // Spec: RTN22 + if ($exp - $serverTimeFn() <= 30) { + $iat = $serverTimeFn(); + $exp = $iat + $this->tokenExpiry; + } } else { $iat = $serverTimeFn(); $exp = $iat + $this->tokenExpiry; From c8a4e765d6894a85440b16bf0d1bd5cbe49cbaf6 Mon Sep 17 00:00:00 2001 From: sacOO7 Date: Wed, 18 Dec 2024 23:03:21 +0530 Subject: [PATCH 2/2] [ECO-5171][RTN22] Server initiated auth/client requested auth before expiry 1. Added implementation to renew token if client requests auth 30 seconds before expiry 2. Added missing check for identifying the same user 3. Added doc comment for getServerTime method in AblyBroadcaster.php --- src/AblyBroadcaster.php | 2 +- src/Utils.php | 15 +++++++++++++++ tests/AblyBroadcasterTest.php | 3 ++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/AblyBroadcaster.php b/src/AblyBroadcaster.php index a6b4b6c..f8f40df 100644 --- a/src/AblyBroadcaster.php +++ b/src/AblyBroadcaster.php @@ -214,7 +214,7 @@ public function getSignedToken($channelName, $token, $clientId, $guardedChannelC $serverTimeFn = function () { return $this->getServerTime(); }; - if ($token && Utils::isJwtValid($token, $serverTimeFn, $this->getPrivateToken())) { + if ($token && Utils::isJwtValid($token, $serverTimeFn, $this->getPrivateToken()) && Utils::isSameUser($token, $clientId)) { $payload = Utils::parseJwt($token)['payload']; $iat = $payload['iat']; $exp = $payload['exp']; diff --git a/src/Utils.php b/src/Utils.php index b3c8c9f..36fd399 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -104,4 +104,19 @@ public static function decodeSocketId($socketId): ?object } return $socketIdObject; } + + public static function isSameUser($token, $clientId) + { + // Decode the JWT token to extract the payload + $decodedToken = Utils::parseJwt($token); + $payload = $decodedToken['payload']; + + // Check if the clientId in the payload matches the provided clientId + if (isset($payload['x-ably-clientId']) && $payload['x-ably-clientId'] == $clientId) { + return true; + } + + // If the clientId does not match, return false + return false; + } } diff --git a/tests/AblyBroadcasterTest.php b/tests/AblyBroadcasterTest.php index b06d403..45641f6 100644 --- a/tests/AblyBroadcasterTest.php +++ b/tests/AblyBroadcasterTest.php @@ -184,7 +184,8 @@ public function testShouldHaveUpgradedCapabilitiesForValidToken() $token = $this->broadcaster->getSignedToken('private:channel3', $token, 'user98', $this->guardedChannelCapability); $parsedToken = Utils::parseJwt($token); $payload = $parsedToken['payload']; - $expectedCapability = '{"public:*":["subscribe","history","channel-metadata"],"private:channel":["*"],"private:channel2":["*"],"private:channel3":["*"]}'; + // Since this is a different user, the capabilities from previous token will be reset and only returned for given channel + $expectedCapability = '{"public:*":["subscribe","history","channel-metadata"],"private:channel3":["*"]}'; self::assertEquals('user98', $payload['x-ably-clientId']); self::assertEquals($expectedCapability, $payload['x-ably-capability']); self::assertEquals($iat, $payload['iat']);