From b92cc9d6a29b033749bcd8ca880c804199a4048a Mon Sep 17 00:00:00 2001 From: "Christopher C. Wells" Date: Tue, 22 Sep 2020 13:02:50 -0700 Subject: [PATCH] Add secure access token support --- composer.json | 1 + src/LiveStream/LiveStream.php | 81 ++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 0024d7e..3347fa6 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ ], "require": { "php": ">=7.1.3", + "ext-curl": "*", "ext-json": "*" }, "require-dev": { diff --git a/src/LiveStream/LiveStream.php b/src/LiveStream/LiveStream.php index a290e2a..cf31b8b 100644 --- a/src/LiveStream/LiveStream.php +++ b/src/LiveStream/LiveStream.php @@ -35,20 +35,66 @@ class LiveStream ]; /** - * API_KEY. + * API key. * * @var string */ private $apiKey; /** - * Class Constructor + * Client ID. + * + * @var int + */ + private $clientId; + + /** + * Scope. + * + * @var string + */ + private $scope; + + /** + * Secure access token. + * + * @var array + */ + private $token; + + /** + * Secure access token timestamp. + * + * @var int + */ + private $tokenTimestamp; + + /** + * Class Constructor. + * + * When only an API key is provided key auth method is used. When an API + * key, client ID, and scope are provided secure token auth is used. * * @param string $apiKey + * @param int|null $clientId + * @param string|null $scope + * Valid scopes are: all, readonly, playback + * + * @see https://livestream.com/developers/docs/api/#authentication */ - public function __construct(string $apiKey) + public function __construct( + string $apiKey, + ?int $clientId = null, + ?string $scope = null + ) { $this->apiKey = $apiKey; + $this->clientId = $clientId; + $this->scope = $scope; + + if ($this->clientId && $this->scope) { + $this->refreshToken(); + } } /** @@ -381,6 +427,20 @@ public function getLiveVideo( return $video; } + /** + * Refreshes a secure access token if invalid (5 minute life time). + * + * @see https://github.com/Livestream/livestream-api-samples/tree/master/php/secure-token-auth-sample + */ + private function refreshToken(): void + { + $now = round(microtime(true) * 1000); + if (!$this->tokenTimestamp || round(($now - $this->tokenTimestamp)/1000) > 300) { + $this->tokenTimestamp = (int) $now; + $this->token = hash_hmac('md5', "{$this->apiKey}:{$this->scope}:{$this->tokenTimestamp}", $this->apiKey); + } + } + /** * CURL Request * @@ -398,15 +458,24 @@ private function request( if (!$ch) throw new Exception("Could not initialize CURL."); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + + if ($this->token && $this->clientId && $this->tokenTimestamp) { + $query['timestamp'] = $this->tokenTimestamp; + $query['clientId'] = $this->clientId; + $query['token'] = $this->token; + } + else { + curl_setopt($ch, CURLOPT_USERPWD, $this->apiKey . ':'); + } + curl_setopt( $ch, CURLOPT_URL, $this->get_base_url() . $endpoint . ($query ? '?' . http_build_query($query) : '') ); - curl_setopt($ch, CURLOPT_USERPWD, $this->apiKey . ':'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - if ($verb != 'get') { if ($verb == 'post') curl_setopt($ch, CURLOPT_POST, true); if ($verb == 'put') curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');