Skip to content

Commit

Permalink
added abstract class for caching
Browse files Browse the repository at this point in the history
  • Loading branch information
gaokevin1 committed Jan 2, 2025
1 parent deb6452 commit 1dedcba
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 24 deletions.
56 changes: 55 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,61 @@ $descopeSDK = new DescopeSDK([
]);
```

This SDK will easily allow you to handle Descope JWT tokens with the following built-in functions:
### Caching Mechanism

The Descope PHP SDK uses a caching mechanism to store frequently accessed data, such as JSON Web Key Sets (JWKs) for session token validation. By default, the SDK uses **APCu** for caching, provided it is enabled and configured in your environment. If APCu is not available, and no other caching mechanism is provided, caching is disabled.

By using the `CacheInterface`, you can integrate the Descope PHP SDK with any caching mechanism that suits your application, ensuring optimal performance in both small and large-scale deployments.

#### Custom Caching with `CacheInterface`

The SDK allows you to provide a custom caching mechanism by implementing the `CacheInterface`. This interface defines three methods that any cache implementation should support:

- `get(string $key)`: Retrieve a value by key.
- `set(string $key, $value, int $ttl = 3600): bool`: Store a value with a specified time-to-live (TTL).
- `delete(string $key): bool`: Remove a value by key.

You can provide your custom caching implementation by creating a class that implements `CacheInterface`. Here’s an example using Laravel’s cache system:

```php
namespace App\Cache;

use Descope\SDK\Cache\CacheInterface;
use Illuminate\Support\Facades\Cache;

class LaravelCache implements CacheInterface
{
public function get(string $key)
{
return Cache::get($key);
}

public function set(string $key, $value, int $ttl = 3600): bool
{
// Laravel TTL is in minutes
return Cache::put($key, $value, $ttl / 60);
}

public function delete(string $key): bool
{
return Cache::forget($key);
}
}
```

To use the Laravel cache in the SDK:

```php
use Descope\SDK\DescopeSDK;
use App\Cache\LaravelCache;

$descopeSDK = new DescopeSDK([
'projectId' => $_ENV['DESCOPE_PROJECT_ID'],
'managementKey' => $_ENV['DESCOPE_MANAGEMENT_KEY'],
], new LaravelCache());
```

Once you've configured your caching, you're ready to use the SDK. This SDK will easily allow you integrate Descope functionality with the following built-in functions:

## Password Authentication

Expand Down
21 changes: 21 additions & 0 deletions src/SDK/Cache/APCuCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Descope\SDK\Cache;

class APCuCache implements CacheInterface
{
public function get(string $key)
{
return apcu_fetch($key);
}

public function set(string $key, $value, int $ttl = 3600): bool
{
return apcu_store($key, $value, $ttl);
}

public function delete(string $key): bool
{
return apcu_delete($key);
}
}
10 changes: 10 additions & 0 deletions src/SDK/Cache/CacheInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Descope\SDK\Cache;

interface CacheInterface
{
public function get(string $key);
public function set(string $key, $value, int $ttl = 3600): bool;
public function delete(string $key): bool;
}
23 changes: 23 additions & 0 deletions src/SDK/Cache/LaravelCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Descope\SDK\Cache;

use Illuminate\Support\Facades\Cache;

class LaravelCache implements CacheInterface
{
public function get(string $key)
{
return Cache::get($key);
}

public function set(string $key, $value, int $ttl = 3600): bool
{
return Cache::put($key, $value, $ttl / 60);
}

public function delete(string $key): bool
{
return Cache::forget($key);
}
}
21 changes: 21 additions & 0 deletions src/SDK/Cache/NullCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Descope\SDK\Cache;

class NullCache implements CacheInterface
{
public function get(string $key)
{
return null;
}

public function set(string $key, $value, int $ttl = 3600): bool
{
return true;
}

public function delete(string $key): bool
{
return true;
}
}
32 changes: 23 additions & 9 deletions src/SDK/Configuration/SDKConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,33 @@
use GuzzleHttp\Psr7\Request;
use Descope\SDK\EndpointsV2;
use Descope\SDK\API;
use Descope\SDK\Cache\CacheInterface;
use Descope\SDK\Cache\APCuCache;
use Descope\SDK\Cache\NullCache;

final class SDKConfig
{
public $client;
public $projectId;
public $managementKey;
public $jwkSets;
private $cachedJWKSets = null;
private $cache;
private const JWKS_CACHE_KEY = 'descope_jwks';

public function __construct(array $config)
public function __construct(array $config, ?CacheInterface $cache = null)
{
$this->client = new Client();
$this->projectId = $config['projectId'];
$this->managementKey = $config['managementKey'] ?? '';

if ($cache) {
$this->cache = $cache;
} elseif (extension_loaded('apcu') && ini_get('apc.enable_cli')) {
$this->cache = new APCuCache();
} else {
$this->cache = new NullCache();
error_log('APCu is not enabled. Falling back to NullCache. Caching is disabled.');
}

EndpointsV2::setBaseUrl($config['projectId']);
}

Expand All @@ -30,14 +42,16 @@ public function __construct(array $config)
*/
public function getJWKSets(bool $forceRefresh = false): array
{
// Return cached JWK if it exists and no refresh is requested
if ($this->cachedJWKSets !== null && !$forceRefresh) {
return $this->cachedJWKSets;
if (!$forceRefresh) {
$cachedJWKSets = $this->cache->get(self::JWKS_CACHE_KEY);
if ($cachedJWKSets) {
return $cachedJWKSets;
}
}

// Fetch new JWK KeySet from Descope API
$this->cachedJWKSets = $this->fetchJWKSets();
return $this->cachedJWKSets;
$jwks = $this->fetchJWKSets();
$this->cache->set(self::JWKS_CACHE_KEY, $jwks, 3600); // Cache for 1 hour
return $jwks;
}

/**
Expand Down
20 changes: 8 additions & 12 deletions src/SDK/Token/Extractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,14 @@ private function verifySignature(string $signedData, string $signature, string $
throw new TokenException('Invalid public key');
}

try {
$result = openssl_verify(
$signedData,
$signature,
$publicKey,
OPENSSL_ALGO_SHA256
);

return $result === 1;
} finally {
openssl_free_key($publicKey);
}
$result = openssl_verify(
$signedData,
$signature,
$publicKey,
OPENSSL_ALGO_SHA256
);

return $result === 1;
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/tests/DescopeSDKTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Descope\SDK\Auth\Password;
use Descope\SDK\Auth\SSO;
use Descope\SDK\Management\Management;
use Descope\SDK\Exception\ValidationException;

final class DescopeSDKTest extends TestCase
{
Expand All @@ -32,13 +33,13 @@ public function testConstructorInitializesComponents()

public function testVerifyThrowsExceptionWithoutToken()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectException(ValidationException::class);
$this->sdk->verify(null);
}

public function testRefreshSessionThrowsExceptionWithoutToken()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectException(ValidationException::class);
$this->sdk->refreshSession(null);
}
}

0 comments on commit 1dedcba

Please sign in to comment.