Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions src/Auth/JWTGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@

namespace GetStream\Auth;

use GetStream\Exceptions\StreamException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use GetStream\Exceptions\StreamException;

/**
* JWT token generator for GetStream authentication
* JWT token generator for GetStream authentication.
*/
class JWTGenerator
{
private string $secret;
private string $algorithm;

/**
* Create a new JWTGenerator
* Create a new JWTGenerator.
*
* @param string $secret The API secret
* @param string $secret The API secret
* @param string $algorithm JWT algorithm (default: HS256)
*/
public function __construct(string $secret, string $algorithm = 'HS256')
Expand All @@ -33,16 +33,17 @@ public function __construct(string $secret, string $algorithm = 'HS256')
}

/**
* Generate a server-side token for API authentication
* Generate a server-side token for API authentication.
*
* @param array $claims Additional claims to include
* @param array $claims Additional claims to include
* @param int|null $expiration Token expiration in seconds (null for no expiration)
*
* @return string JWT token
*/
public function generateServerToken(array $claims = [], ?int $expiration = null): string
{
$now = time();

$payload = array_merge([
'iat' => $now,
'server' => true,
Expand All @@ -56,11 +57,12 @@ public function generateServerToken(array $claims = [], ?int $expiration = null)
}

/**
* Generate a user token for client-side authentication
* Generate a user token for client-side authentication.
*
* @param string $userId The user ID
* @param array $claims Additional claims to include
* @param string $userId The user ID
* @param array $claims Additional claims to include
* @param int|null $expiration Token expiration in seconds (null for no expiration)
*
* @return string JWT token
*/
public function generateUserToken(string $userId, array $claims = [], ?int $expiration = null): string
Expand All @@ -70,7 +72,7 @@ public function generateUserToken(string $userId, array $claims = [], ?int $expi
}

$now = time();

$payload = array_merge([
'user_id' => $userId,
'iat' => $now,
Expand All @@ -84,33 +86,37 @@ public function generateUserToken(string $userId, array $claims = [], ?int $expi
}

/**
* Verify and decode a JWT token
* Verify and decode a JWT token.
*
* @param string $token The JWT token to verify
*
* @return array Decoded token payload
*
* @throws StreamException
*/
public function verifyToken(string $token): array
{
try {
$decoded = JWT::decode($token, new Key($this->secret, $this->algorithm));

return (array) $decoded;
} catch (\Exception $e) {
throw new StreamException('Invalid JWT token: ' . $e->getMessage(), 0, $e);
}
}

/**
* Verify a webhook signature
* Verify a webhook signature.
*
* @param string $body The request body
* @param string $body The request body
* @param string $signature The signature to verify
*
* @return bool True if signature is valid
*/
public function verifyWebhookSignature(string $body, string $signature): bool
{
$expectedSignature = hash_hmac('sha256', $body, $this->secret);

return hash_equals($expectedSignature, $signature);
}
}

74 changes: 39 additions & 35 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

namespace GetStream;

use GetStream\Exceptions\StreamException;
use GetStream\Http\HttpClientInterface;
use GetStream\Http\GuzzleHttpClient;
use GetStream\Auth\JWTGenerator;
use GetStream\Exceptions\StreamException;
use GetStream\Generated\CommonTrait;
use GetStream\Http\GuzzleHttpClient;
use GetStream\Http\HttpClientInterface;

/**
* Main GetStream client for interacting with the API
* Main GetStream client for interacting with the API.
*/
class Client
{
Expand All @@ -24,11 +24,11 @@ class Client
private array $defaultHeaders;

/**
* Create a new GetStream client
* Create a new GetStream client.
*
* @param string $apiKey The API key
* @param string $apiSecret The API secret
* @param string $baseUrl The base URL for the API
* @param string $apiKey The API key
* @param string $apiSecret The API secret
* @param string $baseUrl The base URL for the API
* @param HttpClientInterface|null $httpClient Optional HTTP client
*/
public function __construct(
Expand All @@ -40,7 +40,7 @@ public function __construct(
if (empty($apiKey)) {
throw new StreamException('API key cannot be empty');
}

if (empty($apiSecret)) {
throw new StreamException('API secret cannot be empty');
}
Expand All @@ -50,7 +50,7 @@ public function __construct(
$this->baseUrl = rtrim($baseUrl, '/');
$this->httpClient = $httpClient ?? new GuzzleHttpClient();
$this->jwtGenerator = new JWTGenerator($apiSecret);

$this->defaultHeaders = [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
Expand All @@ -59,69 +59,72 @@ public function __construct(
}

/**
* Get the API key
* Get the API key.
*/
public function getApiKey(): string
{
return $this->apiKey;
}

/**
* Get the API secret
* Get the API secret.
*/
public function getApiSecret(): string
{
return $this->apiSecret;
}

/**
* Get the base URL
* Get the base URL.
*/
public function getBaseUrl(): string
{
return $this->baseUrl;
}

/**
* Get the HTTP client
* Get the HTTP client.
*/
public function getHttpClient(): HttpClientInterface
{
return $this->httpClient;
}

/**
* Get the JWT generator
* Get the JWT generator.
*/
public function getJWTGenerator(): JWTGenerator
{
return $this->jwtGenerator;
}

/**
* Create a feed instance
* Create a feed instance.
*
* @param string $feedGroup The feed group (e.g., 'user', 'timeline')
* @param string $feedId The feed ID (e.g., user ID)
* @return Feed
* @param string $feedId The feed ID (e.g., user ID)
*
* @throws StreamException
*/
public function feed(string $feedGroup, string $feedId): Feed
{
// Create a FeedsV3Client instance using the same configuration
$feedsV3Client = new FeedsV3Client($this->apiKey, $this->apiSecret, $this->baseUrl, $this->httpClient);

return new Feed($feedsV3Client, $feedGroup, $feedId);
}

/**
* Make an authenticated HTTP request to the GetStream API
* Make an authenticated HTTP request to the GetStream API.
*
* @param string $method HTTP method
* @param string $path API path
* @param array $queryParams Query parameters
* @param mixed $body Request body
* @param array $pathParams Path parameters for URL substitution
*
* @param string $method HTTP method
* @param string $path API path
* @param array $queryParams Query parameters
* @param mixed $body Request body
* @param array $pathParams Path parameters for URL substitution
* @return StreamResponse<mixed>
*
* @throws StreamException
*/
public function makeRequest(
Expand All @@ -133,21 +136,21 @@ public function makeRequest(
): StreamResponse {
// Replace path parameters
foreach ($pathParams as $key => $value) {
$path = str_replace('{' . $key . '}', (string)$value, $path);
$path = str_replace('{' . $key . '}', (string) $value, $path);
}

// Build URL
$url = $this->baseUrl . $path;

// Add API key to query parameters
$queryParams['api_key'] = $this->apiKey;

// Add query parameters (there will always be at least api_key)
$url .= '?' . http_build_query($queryParams);

// Generate authentication token
$token = $this->jwtGenerator->generateServerToken();

// Prepare headers
$headers = array_merge($this->defaultHeaders, [
'Authorization' => $token,
Expand All @@ -158,14 +161,13 @@ public function makeRequest(
return $this->httpClient->request($method, $url, $headers, $body);
}



/**
* Generate a user token for client-side authentication
* Generate a user token for client-side authentication.
*
* @param string $userId The user ID
* @param array $claims Additional claims
* @param string $userId The user ID
* @param array $claims Additional claims
* @param int|null $expiration Token expiration in seconds (null for no expiration)
*
* @return string JWT token
*/
public function createUserToken(string $userId, array $claims = [], ?int $expiration = null): string
Expand All @@ -174,17 +176,19 @@ public function createUserToken(string $userId, array $claims = [], ?int $expira
}

/**
* Create or update users
* Create or update users.
*
* @param array $users Array of user data keyed by user ID
*
* @return StreamResponse<mixed>
*
* @throws StreamException
*/
public function upsertUsers(array $users): StreamResponse
{
$path = '/api/v2/users';
$requestData = ['users' => $users];

return $this->makeRequest('POST', $path, [], $requestData);
}
}
Loading