Skip to content

Commit 4a433ae

Browse files
Merge pull request #354 from opentok/chore/vonage-video-shim
Chore/vonage video shim
2 parents c1fc22d + 3497cc3 commit 4a433ae

15 files changed

+221
-115
lines changed

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,28 @@ $options = [
8585
]
8686
$opentok = new OpenTok($apiKey, $apiSecret, $options);
8787
```
88+
#### Migrating to Vonage Video API
89+
90+
There is some useful behaviour on initialization in this SDK that will help as a stopgap before switching out from the
91+
legacy TokBok API to the new, Vonage Video API. To do this, you can pass in an Application ID for the API Key, and the path to
92+
a Vonage private key as the API Secret.
93+
94+
```php
95+
use OpenTok\OpenTok;
96+
use MyCompany\CustomOpenTokClient;
97+
98+
$applicationID = '61bb2dae-9b69-400c-9abb-642d082af5fc';
99+
$privateKey = './private.key';
100+
101+
$opentok = new OpenTok($applicationID, $privateKey);
102+
```
103+
104+
The SDK will notice that the authentiction has changed, and will automatically start to forward requests to the Vonage API Routes
105+
instead of the OpenTok API Routes. All requests and responses in your code should be exactly the same as they are on the
106+
OpenTok API.
107+
108+
**NOTE:** The SDK will read the private key path from the root directory of your project (generally one level above the docroot
109+
of your application), so you will need to make sure that the path provided is either absolute, or relative to your project root.
88110

89111
### Creating Sessions
90112

sample/Archiving/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Next, input your own API Key and API Secret into the `run-demo` script file:
1919

2020
```
2121
export API_KEY=0000000
22-
export API_SECRET=abcdef1234567890abcdef01234567890abcdef
22+
export API_SECRET=b60d0b2568f3ea9731bd9d3f71be263ce19f802f
2323
```
2424

2525
Finally, start the PHP CLI development server (requires PHP >= 5.4) using the `run-demo` script

src/OpenTok/Archive.php

-2
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,6 @@ public function __construct($archiveData, $options = array())
144144

145145
$this->client = isset($client) ? $client : new Client();
146146
if (!$this->client->isConfigured()) {
147-
Validators::validateApiKey($apiKey);
148-
Validators::validateApiSecret($apiSecret);
149147
Validators::validateApiUrl($apiUrl);
150148

151149
$this->client->configure($apiKey, $apiSecret, $apiUrl);

src/OpenTok/ArchiveList.php

-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ public function __construct($archiveListData, $options = array())
4949

5050
$this->client = isset($client) ? $client : new Client();
5151
if (!$this->client->isConfigured()) {
52-
Validators::validateApiKey($apiKey);
53-
Validators::validateApiSecret($apiSecret);
5452
Validators::validateApiUrl($apiUrl);
5553

5654
$this->client->configure($apiKey, $apiSecret, $apiUrl);

src/OpenTok/Broadcast.php

-2
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,6 @@ public function __construct($broadcastData, $options = array())
147147
$this->client = $options['client'] ?? new Client();
148148

149149
if (!$this->client->isConfigured()) {
150-
Validators::validateApiKey($options['apiKey']);
151-
Validators::validateApiSecret($options['apiSecret']);
152150
Validators::validateApiUrl($options['apiUrl']);
153151

154152
$this->client->configure($options['apiKey'], $options['apiSecret'], $options['apiUrl']);

src/OpenTok/OpenTok.php

+23-6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
* and the API secret for your <a href="https://tokbox.com/account">OpenTok Video API account</a>. Do not
2626
* publicly share your API secret. You will use it with the OpenTok() constructor (only on your web
2727
* server) to create OpenTok sessions.
28+
*
29+
* If you set api_key to a VONAGE_APPLICATION_ID and api_secret to a VONAGE_PRIVATE_KEY_PATH, the SDK
30+
* will hit Vonage Video with Vonage Auth instead.
2831
* <p>
2932
* Be sure to include the entire OpenTok server SDK on your web server.
3033
*/
@@ -36,26 +39,40 @@ class OpenTok
3639
private $apiSecret;
3740
/** @internal */
3841
private $client;
39-
/** @internal */
42+
43+
/**
44+
* @var bool
45+
* Override to determine whether to hit Vonage servers with Vonage Auth in requests
46+
*/
47+
private $vonage = false;
48+
49+
/**
50+
* @var array
51+
* @internal
52+
*/
4053
public $options;
4154

4255
/** @internal */
4356
public function __construct($apiKey, $apiSecret, $options = array())
4457
{
58+
$apiUrl = 'https://api.opentok.com';
59+
60+
if (Validators::isVonageKeypair($apiKey, $apiSecret)) {
61+
$this->vonage = true;
62+
$apiUrl = 'https://video.api.vonage.com';
63+
}
64+
4565
// unpack optional arguments (merging with default values) into named variables
4666
$defaults = array(
47-
'apiUrl' => 'https://api.opentok.com',
67+
'apiUrl' => $apiUrl,
4868
'client' => null,
49-
'timeout' => null // In the future we should set this to 2
69+
'timeout' => null, // In the future we should set this to 2
5070
);
5171

5272
$this->options = array_merge($defaults, array_intersect_key($options, $defaults));
5373

5474
list($apiUrl, $client, $timeout) = array_values($this->options);
5575

56-
// validate arguments
57-
Validators::validateApiKey($apiKey);
58-
Validators::validateApiSecret($apiSecret);
5976
Validators::validateApiUrl($apiUrl);
6077
Validators::validateClient($client);
6178
Validators::validateDefaultTimeout($timeout);

src/OpenTok/Util/Client.php

+7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
use OpenTok\Exception\ForceDisconnectAuthenticationException;
3838
use OpenTok\Exception\ForceDisconnectUnexpectedValueException;
39+
use Vonage\JWT\TokenGenerator;
3940

4041
/**
4142
* @internal
@@ -124,13 +125,19 @@ public function isConfigured()
124125

125126
private function createAuthHeader()
126127
{
128+
if (Validators::isVonageKeypair($this->apiKey, $this->apiSecret)) {
129+
$tokenGenerator = new TokenGenerator($this->apiKey, file_get_contents($this->apiSecret));
130+
return $tokenGenerator->generate();
131+
}
132+
127133
$token = array(
128134
'ist' => 'project',
129135
'iss' => $this->apiKey,
130136
'iat' => time(), // this is in seconds
131137
'exp' => time() + (5 * 60),
132138
'jti' => uniqid('', true),
133139
);
140+
134141
return JWT::encode($token, $this->apiSecret, 'HS256');
135142
}
136143

src/OpenTok/Util/Validators.php

+37-12
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OpenTok\Exception\InvalidArgumentException;
1414
use JohnStevenson\JsonWorks\Document;
1515
use JohnStevenson\JsonWorks\Utils as JsonUtils;
16+
use RuntimeException;
1617

1718
/**
1819
* @internal
@@ -25,13 +26,44 @@ class Validators
2526

2627
public const STREAM_MODES = ['auto', 'manual'];
2728

28-
public static function validateApiKey($apiKey)
29+
public static function isVonageKeypair($apiKey, $apiSecret): bool
2930
{
30-
if (!(is_string($apiKey) || is_int($apiKey))) {
31-
throw new InvalidArgumentException(
32-
'The apiKey was not a string nor an integer: ' . print_r($apiKey, true)
33-
);
31+
if (!is_string($apiKey) || !is_string($apiSecret)) {
32+
throw new InvalidArgumentException("API Key and API Secret must be strings.");
33+
}
34+
35+
$isOpenTokKey = preg_match('/^\d+$/', $apiKey);
36+
$isOpenTokSecret = preg_match('/^[a-f0-9]{40}$/i', $apiSecret);
37+
38+
if ($isOpenTokKey && $isOpenTokSecret) {
39+
return false;
3440
}
41+
42+
$isVonageApplicationId = preg_match('/^[a-f0-9\-]{36}$/i', $apiKey);
43+
$isVonagePrivateKey = self::isValidPrivateKey($apiSecret);
44+
45+
if ($isVonageApplicationId && $isVonagePrivateKey) {
46+
return true;
47+
}
48+
49+
// Mixed formats or invalid formats - throw an exception
50+
throw new InvalidArgumentException("Invalid Vonage Keypair credentials provided.");
51+
}
52+
53+
private static function isValidPrivateKey(string $filePath): bool
54+
{
55+
if (!file_exists($filePath) || !is_readable($filePath)) {
56+
throw new InvalidArgumentException("Private key file does not exist or is not readable.");
57+
}
58+
59+
$keyContents = file_get_contents($filePath);
60+
61+
if ($keyContents === false) {
62+
throw new RuntimeException("Failed to read private key file.");
63+
}
64+
65+
// Check if it contains a valid private RSA key header
66+
return (bool) preg_match('/^-----BEGIN PRIVATE KEY-----[\s\S]+-----END PRIVATE KEY-----$/m', trim($keyContents));
3567
}
3668

3769
public static function validateForceMuteAllOptions(array $options)
@@ -56,13 +88,6 @@ public static function validateForceMuteAllOptions(array $options)
5688
}
5789
}
5890

59-
public static function validateApiSecret($apiSecret)
60-
{
61-
if (!(is_string($apiSecret))) {
62-
throw new InvalidArgumentException('The apiSecret was not a string: ' . print_r($apiSecret, true));
63-
}
64-
}
65-
6691
public static function validateApiUrl($apiUrl)
6792
{
6893
if (!(is_string($apiUrl) && filter_var($apiUrl, FILTER_VALIDATE_URL))) {

tests/OpenTokTest/ArchiveTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public function setupArchives($streamMode)
6262
private function setupOTWithMocks($mocks)
6363
{
6464
$this->API_KEY = defined('API_KEY') ? API_KEY : '12345678';
65-
$this->API_SECRET = defined('API_SECRET') ? API_SECRET : '0123456789abcdef0123456789abcdef0123456789';
65+
$this->API_SECRET = defined('API_SECRET') ? API_SECRET : 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f';
6666

6767
if (is_array($mocks)) {
6868
$responses = TestHelpers::mocksToResponses($mocks, self::$mockBasePath);

tests/OpenTokTest/BroadcastTest.php

+1-34
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function setupBroadcasts($streamMode)
6969
private function setupOTWithMocks($mocks)
7070
{
7171
$this->API_KEY = defined('API_KEY') ? API_KEY : '12345678';
72-
$this->API_SECRET = defined('API_SECRET') ? API_SECRET : '0123456789abcdef0123456789abcdef0123456789';
72+
$this->API_SECRET = defined('API_SECRET') ? API_SECRET : 'b60d0b2568f3ea9731bd9d3f71be263ce19f802f';
7373

7474
if (is_array($mocks)) {
7575
$responses = TestHelpers::mocksToResponses($mocks, self::$mockBasePath);
@@ -98,39 +98,6 @@ private function setupOTWithMocks($mocks)
9898
$handlerStack->push($history);
9999
}
100100

101-
public function testCannotCreateBroadcastWithAddInvalidApiKey(): void
102-
{
103-
$this->expectException(InvalidArgumentException::class);
104-
$this->expectExceptionMessage('The apiKey was not a string nor an integer: ');
105-
106-
$broadcastObject = new Broadcast($this->broadcastData, [
107-
'apiKey' => new Client()
108-
]);
109-
}
110-
111-
public function testCannotCreateBroadcastWithInvalidApiSecret(): void
112-
{
113-
$this->expectException(InvalidArgumentException::class);
114-
$this->expectExceptionMessage('The apiSecret was not a string: OpenTok\Util\Client Object');
115-
116-
$broadcastObject = new Broadcast($this->broadcastData, [
117-
'apiKey' => 'test',
118-
'apiSecret' => new Client()
119-
]);
120-
}
121-
122-
public function testCannotCreateBroadcastWithInvalidApiUrl(): void
123-
{
124-
$this->expectException(InvalidArgumentException::class);
125-
$this->expectExceptionMessage('The optional apiUrl was not a string: ');
126-
127-
$broadcastObject = new Broadcast($this->broadcastData, [
128-
'apiKey' => 'validKey',
129-
'apiSecret' => 'validSecret',
130-
'apiUrl' => 'test'
131-
]);
132-
}
133-
134101
private function setupOT()
135102
{
136103
return $this->setupOTWithMocks([]);

0 commit comments

Comments
 (0)