diff --git a/composer.json b/composer.json index 7cfe1f5..3db0491 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "php": "^7.2|^8.0", "ext-xml": "*", "johnstevenson/json-works": "~1.1", - "firebase/php-jwt": "^6.0", + "firebase/php-jwt": "^6.11", "guzzlehttp/guzzle": "~6.0|~7.0", "ext-json": "*", "vonage/jwt": "^0.5.1" diff --git a/src/OpenTok/OpenTok.php b/src/OpenTok/OpenTok.php index 7250673..a4ef38b 100644 --- a/src/OpenTok/OpenTok.php +++ b/src/OpenTok/OpenTok.php @@ -3,6 +3,7 @@ namespace OpenTok; use DateTimeImmutable; +use Firebase\JWT\JWT; use Firebase\JWT\Key; use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Encoding\ChainedFormatter; @@ -84,7 +85,7 @@ public function __construct($apiKey, $apiSecret, $options = array()) * @param string $sessionId The session ID corresponding to the session to which the user * will connect. * - * @param array $options This array defines options for the token. This array includes the + * @param array $payload This array defines options for the token. This array includes the * following keys, all of which are optional: * * * * @param bool $legacy By default, OpenTok uses SHA256 JWTs for authentication. Switching - * legacy to true will create a deprecated T1 token for backwards compatibility. + * legacy to true will create a T1 token for backwards compatibility. * * @return string The token string. */ - public function generateToken(string $sessionId, array $options = array(), bool $legacy = false): string + public function generateToken(string $sessionId, array $payload = array(), bool $legacy = false): string { - // Note, JWT generation disabled due to a backend bug regarding `exp` claims being mandatory - CRT - // if ($legacy) { - return $this->returnLegacyToken($sessionId, $options); - // } - - // $issuedAt = new \DateTimeImmutable('@' . time()); - - // $defaults = [ - // 'session_id' => $sessionId, - // 'role' => Role::PUBLISHER, - // 'expireTime' => null, - // 'initial_layout_list' => [''], - // 'ist' => 'project', - // 'nonce' => mt_rand(), - // 'scope' => 'session.connect' - // ]; - - // $options = array_merge($defaults, array_intersect_key($options, $defaults)); - - // $builder = new Builder(new JoseEncoder(), ChainedFormatter::default()); - // $builder = $builder->issuedBy($this->apiKey); - - // if ($options['expireTime']) { - // $expiry = new \DateTimeImmutable('@' . $options['expireTime']); - // $builder = $builder->expiresAt($expiry); - // } - - // unset($options['expireTime']); + if ($legacy) { + return $this->returnLegacyToken($sessionId, $payload); + } - // $builder = $builder->issuedAt($issuedAt); - // $builder = $builder->canOnlyBeUsedAfter($issuedAt); - // $builder = $builder->identifiedBy(bin2hex(random_bytes(16))); + $issuedAt = new \DateTimeImmutable('@' . time()); - // foreach ($options as $key => $value) { - // $builder = $builder->withClaim($key, $value); - // } + $defaults = [ + 'iss' => $this->apiKey, + 'iat' => $issuedAt->getTimestamp(), + 'session_id' => $sessionId, + 'role' => Role::PUBLISHER, + 'ist' => 'project', + 'nonce' => mt_rand(), + 'scope' => 'session.connect' + ]; - // $token = $builder->getToken(new \Lcobucci\JWT\Signer\Hmac\Sha256(), InMemory::plainText($this->apiSecret)); + $payload = array_merge($defaults, array_intersect_key($payload, $defaults)); - // return $token->toString(); + return JWT::encode($payload, $this->apiSecret, 'HS256'); } private function returnLegacyToken(string $sessionId, array $options = []): string diff --git a/tests/OpenTokTest/OpenTokTest.php b/tests/OpenTokTest/OpenTokTest.php index 4920acb..996f8c9 100644 --- a/tests/OpenTokTest/OpenTokTest.php +++ b/tests/OpenTokTest/OpenTokTest.php @@ -2,6 +2,8 @@ namespace OpenTokTest; +use Firebase\JWT\JWT; +use Firebase\JWT\Key; use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Token\Plain; use OpenTok\Render; @@ -753,41 +755,28 @@ public function testWillCreateLegacyT1WhenRequested(): void public function testWillCreateLegacyT1DirectlyToBypassExpBug(): void { $openTok = new OpenTok('12345678', '0123456789abcdef0123456789abcdef0123456789'); - $token = $openTok->generateToken('1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI', []); + $token = $openTok->generateToken('1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI', [], true); $this->assertEquals('T1', substr($token, 0, 2)); } - /** - * Makes sure that a JWT is generated for the client-side token - * - * Currently disabled due to the backend requiring an `exp` claim, which was - * not required on T1s. Uncomment when the backend is fixed. - CRT - */ - // public function testWillCreateJwt(): void - // { - // $openTok = new OpenTok('my-api-key', 'my-super-long-and-cool-api-secret'); - // $token = $openTok->generateToken('some-token-value'); - - // $config = Configuration::forSymmetricSigner( - // new \Lcobucci\JWT\Signer\Hmac\Sha256(), - // \Lcobucci\JWT\Signer\Key\InMemory::plainText('my-super-long-and-cool-api-secret') - // ); - - // $token = $config->parser()->parse($token); - // $this->assertInstanceOf(Plain::class, $token); - - // $this->assertTrue($config->validator()->validate($token, new \Lcobucci\JWT\Validation\Constraint\SignedWith( - // $config->signer(), - // $config->signingKey() - // ))); - - // $this->assertEquals('my-api-key', $token->claims()->get('iss')); - // $this->assertEquals('some-token-value', $token->claims()->get('session_id')); - // $this->assertEquals('publisher', $token->claims()->get('role')); - // $this->assertEquals('project', $token->claims()->get('ist')); - // $this->assertEquals('session.connect', $token->claims()->get('scope')); - // } + public function testWillGenerateSha256Token(): void + { + $openTok = new OpenTok('12345678', '0123456789abcdef0123456789abcdef0123456789'); + $token = $openTok->generateToken('1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI'); + + $this->assertNotEquals('T1', substr($token, 0, 2)); + + $decoded = JWT::decode($token, new Key('0123456789abcdef0123456789abcdef0123456789', 'HS256')); + $decodedArray = (array) $decoded; + + $this->assertEquals('12345678', $decodedArray['iss']); + $this->assertEquals('1_MX4xMjM0NTY3OH4-VGh1IEZlYiAyNyAwNDozODozMSBQU1QgMjAxNH4wLjI0NDgyMjI', $decodedArray['session_id']); + $this->assertEquals('project', $decodedArray['ist']); + $this->assertEquals('session.connect', $decodedArray['scope']); + $this->assertEquals('publisher', $decodedArray['role']); + + } public function testStartsArchive(): void {