diff --git a/src/Auth0.php b/src/Auth0.php index 48f216e6..310bb6ed 100644 --- a/src/Auth0.php +++ b/src/Auth0.php @@ -11,10 +11,12 @@ use Auth0\SDK\Auth0 as SDK; use Auth0\SDK\Configuration\SdkConfiguration as Configuration; use Auth0\SDK\Contract\API\ManagementInterface; -use Auth0\SDK\Contract\Auth0Interface as SDKContract; +use Auth0\SDK\Contract\{Auth0Interface as SDKContract, StoreInterface}; use Auth0\SDK\Utility\HttpTelemetry; +use Psr\Cache\CacheItemPoolInterface; use function in_array; +use function is_string; /** * Service that provides access to the Auth0 SDK. @@ -31,9 +33,144 @@ final class Auth0 implements ServiceContract public function __construct( private ?SDKContract $sdk = null, private ?Configuration $configuration = null, + private ?CacheItemPoolInterface $tokenCachePool = null, + private ?CacheItemPoolInterface $managementTokenCachePool = null, ) { } + private function bootManagementTokenCache(array $config): array + { + $managementTokenCache = $config['managementTokenCache'] ?? null; + + if (false === $managementTokenCache) { + unset($config['managementTokenCache']); + + return $config; + } + + if (null === $managementTokenCache) { + $managementTokenCache = $this->getManagementTokenCachePool(); + } + + if (is_string($managementTokenCache)) { + $managementTokenCache = app(trim($managementTokenCache)); + } + + $config['managementTokenCache'] = $managementTokenCache instanceof CacheItemPoolInterface ? $managementTokenCache : null; + + return $config; + } + + private function bootSessionStorage(array $config): array + { + $sessionStorage = $config['sessionStorage'] ?? null; + $sessionStorageId = $config['sessionStorageId'] ?? 'auth0_session'; + + if (false === $sessionStorage) { + unset($config['sessionStorage']); + + return $config; + } + + if (null === $sessionStorage) { + $sessionStorage = app(LaravelSession::class, [ + 'prefix' => $sessionStorageId, + ]); + } + + if (is_string($sessionStorage)) { + $sessionStorage = app(trim($sessionStorage), [ + 'prefix' => $sessionStorageId, + ]); + } + + $config['sessionStorage'] = $sessionStorage instanceof StoreInterface ? $sessionStorage : null; + + return $config; + } + + private function bootStrategy(array $config): array + { + $strategy = $config['strategy'] ?? Configuration::STRATEGY_REGULAR; + + if (! is_string($strategy)) { + $strategy = Configuration::STRATEGY_REGULAR; + } + + $config['strategy'] = $strategy; + + return $config; + } + + private function bootTokenCache(array $config): array + { + $tokenCache = $config['tokenCache'] ?? null; + + if (false === $tokenCache) { + unset($config['tokenCache']); + + return $config; + } + + if (null === $tokenCache) { + $tokenCache = $this->getTokenCachePool(); + } + + if (is_string($tokenCache)) { + $tokenCache = app(trim($tokenCache)); + } + + $config['tokenCache'] = $tokenCache instanceof CacheItemPoolInterface ? $tokenCache : null; + + return $config; + } + + private function bootTransientStorage(array $config): array + { + $transientStorage = $config['transientStorage'] ?? null; + $transientStorageId = $config['transientStorageId'] ?? 'auth0_transient'; + + if (false === $transientStorage) { + unset($config['transientStorage']); + + return $config; + } + + if (null === $transientStorage) { + $transientStorage = app(LaravelSession::class, [ + 'prefix' => $transientStorageId, + ]); + } + + if (is_string($transientStorage)) { + $transientStorage = app(trim($transientStorage), [ + 'prefix' => $transientStorageId, + ]); + } + + $config['transientStorage'] = $transientStorage instanceof StoreInterface ? $transientStorage : null; + + return $config; + } + + private function getManagementTokenCachePool(): CacheItemPoolInterface + { + if (! $this->managementTokenCachePool instanceof CacheItemPoolInterface) { + $this->managementTokenCachePool = app(LaravelCachePool::class); + } + + return $this->managementTokenCachePool; + } + + private function getTokenCachePool(): CacheItemPoolInterface + { + if (! $this->tokenCachePool instanceof CacheItemPoolInterface) { + $this->tokenCachePool = app(LaravelCachePool::class); + } + + return $this->tokenCachePool; + } + /** * Updates the Auth0 PHP SDK's telemetry to include the correct Laravel markers. */ @@ -53,49 +190,26 @@ public function getConfiguration(): Configuration /** * @var array $config */ - if (! isset($config['tokenCache']) || ! isset($config['managementTokenCache'])) { - $cache = new LaravelCachePool(); - - if (! isset($config['tokenCache'])) { - $config['tokenCache'] = $cache; - } - - if (! isset($config['managementTokenCache'])) { - $config['managementTokenCache'] = $cache; - } - } + // Give host application an opportunity to update the configuration before assigning defaults. $event = new Building($config); event($event); + $config = $event->getConfiguration(); - $configuration = new Configuration($event->getConfiguration()); + $config = $this->bootStrategy($config); + $config = $this->bootTokenCache($config); + $config = $this->bootManagementTokenCache($config); - if (! in_array($configuration->getStrategy(), [Configuration::STRATEGY_API, Configuration::STRATEGY_MANAGEMENT_API], true)) { - // If no sessionStorage is defined, use an LaravelSession store instance. - if (! isset($config['sessionStorage'])) { - $sessionStore = app(LaravelSession::class, [ - 'prefix' => $configuration->getSessionStorageId(), - ]); - - $configuration->setSessionStorage(sessionStorage: $sessionStore); - } - - // If no transientStorage is defined, use an LaravelSession store instance. - if (! isset($config['transientStorage'])) { - $transientStore = app(LaravelSession::class, [ - 'prefix' => $configuration->getTransientStorageId(), - ]); - - $configuration->setTransientStorage(transientStorage: $transientStore); - } + if (in_array($config['strategy'], Configuration::STRATEGIES_USING_SESSIONS, true)) { + $config = $this->bootSessionStorage($config); + $config = $this->bootTransientStorage($config); } - $this->configuration = $configuration; + $config = new Configuration($config); - // Give apps an opportunity to mutate the configuration before applying it. - $event = new Built($configuration); + // Give host application an opportunity to update the configuration before applying it. + $event = new Built($config); event($event); - $this->configuration = $event->getConfiguration(); } diff --git a/tests/Unit/Auth0Test.php b/tests/Unit/Auth0Test.php index 31ac32e2..c01cf55b 100644 --- a/tests/Unit/Auth0Test.php +++ b/tests/Unit/Auth0Test.php @@ -104,3 +104,107 @@ $updated = spl_object_id($laravel->getSdk()); expect($cache)->not->toBe($updated); }); + +test('bootStrategy() rejects non-string values', function (): void { + $method = new ReflectionMethod(Auth0::class, 'bootStrategy'); + $method->setAccessible(true); + + expect($method->invoke($this->laravel, ['strategy' => 123])) + ->toMatchArray(['strategy' => SdkConfiguration::STRATEGY_REGULAR]); +}); + +test('bootSessionStorage() behaves as expected', function (): void { + $method = new ReflectionMethod(Auth0::class, 'bootSessionStorage'); + $method->setAccessible(true); + + expect($method->invoke($this->laravel, [])) + ->sessionStorage->toBeInstanceOf(LaravelSession::class); + + expect($method->invoke($this->laravel, ['sessionStorage' => null])) + ->sessionStorage->toBeInstanceOf(LaravelSession::class); + + expect($method->invoke($this->laravel, ['sessionStorage' => false])) + ->sessionStorage->toBeNull(); + + expect($method->invoke($this->laravel, ['sessionStorage' => LaravelCachePool::class])) + ->sessionStorage->toBeNull(); + + expect($method->invoke($this->laravel, ['sessionStorage' => MemoryStore::class])) + ->sessionStorage->toBeInstanceOf(MemoryStore::class); + + $this->app->singleton('testStore', static fn (): MemoryStore => app(MemoryStore::class)); + + expect($method->invoke($this->laravel, ['sessionStorage' => 'testStore'])) + ->sessionStorage->toBeInstanceOf(MemoryStore::class); +}); + +test('bootTransientStorage() behaves as expected', function (): void { + $method = new ReflectionMethod(Auth0::class, 'bootTransientStorage'); + $method->setAccessible(true); + + expect($method->invoke($this->laravel, [])) + ->transientStorage->toBeInstanceOf(LaravelSession::class); + + expect($method->invoke($this->laravel, ['transientStorage' => null])) + ->transientStorage->toBeInstanceOf(LaravelSession::class); + + expect($method->invoke($this->laravel, ['transientStorage' => false])) + ->transientStorage->toBeNull(); + + expect($method->invoke($this->laravel, ['transientStorage' => LaravelCachePool::class])) + ->transientStorage->toBeNull(); + + expect($method->invoke($this->laravel, ['transientStorage' => MemoryStore::class])) + ->transientStorage->toBeInstanceOf(MemoryStore::class); + + $this->app->singleton('testStore', static fn (): MemoryStore => app(MemoryStore::class)); + + expect($method->invoke($this->laravel, ['transientStorage' => 'testStore'])) + ->transientStorage->toBeInstanceOf(MemoryStore::class); +}); + +test('bootTokenCache() behaves as expected', function (): void { + $method = new ReflectionMethod(Auth0::class, 'bootTokenCache'); + $method->setAccessible(true); + + expect($method->invoke($this->laravel, [])) + ->tokenCache->toBeInstanceOf(LaravelCachePool::class); + + expect($method->invoke($this->laravel, ['tokenCache' => null])) + ->tokenCache->toBeInstanceOf(LaravelCachePool::class); + + expect($method->invoke($this->laravel, ['tokenCache' => LaravelCachePool::class])) + ->tokenCache->toBeInstanceOf(LaravelCachePool::class); + + expect($method->invoke($this->laravel, ['tokenCache' => false])) + ->tokenCache->toBeNull(); + + expect($method->invoke($this->laravel, ['tokenCache' => MemoryStore::class])) + ->tokenCache->toBeNull(); + + expect($method->invoke($this->laravel, ['tokenCache' => 'cache.psr6'])) + ->tokenCache->toBeInstanceOf(CacheItemPoolInterface::class); +}); + +test('bootManagementTokenCache() behaves as expected', function (): void { + $method = new ReflectionMethod(Auth0::class, 'bootManagementTokenCache'); + $method->setAccessible(true); + + expect($method->invoke($this->laravel, [])) + ->managementTokenCache->toBeInstanceOf(LaravelCachePool::class); + + expect($method->invoke($this->laravel, ['managementTokenCache' => null])) + ->managementTokenCache->toBeInstanceOf(LaravelCachePool::class); + + expect($method->invoke($this->laravel, ['managementTokenCache' => LaravelCachePool::class])) + ->managementTokenCache->toBeInstanceOf(LaravelCachePool::class); + + expect($method->invoke($this->laravel, ['managementTokenCache' => false])) + ->managementTokenCache->toBeNull(); + + expect($method->invoke($this->laravel, ['managementTokenCache' => MemoryStore::class])) + ->managementTokenCache->toBeNull(); + + expect($method->invoke($this->laravel, ['managementTokenCache' => 'cache.psr6'])) + ->managementTokenCache->toBeInstanceOf(CacheItemPoolInterface::class); +});