From 0e484edf9b8b12ec4710f86ba9b0831ccd1f6283 Mon Sep 17 00:00:00 2001 From: Steve Kamerman Date: Tue, 26 Feb 2019 11:58:01 -0500 Subject: [PATCH] Added ClosureTokenPersistence for wrapping other cache providers. --- README.md | 40 ++++++++++++++- src/Persistence/ClosureTokenPersistence.php | 49 +++++++++++++++++++ .../ClosureTokenPersistenceTest.php | 46 +++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/Persistence/ClosureTokenPersistence.php create mode 100644 tests/Persistence/ClosureTokenPersistenceTest.php diff --git a/README.md b/README.md index 9dcb447..4fa1fc3 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,9 @@ $response = $client->get('http://somehost/some_secure_url'); echo "Status: ".$response->getStatusCode()."\n"; ``` +### Authorization Code Example +There is a full example of using the `AuthorizationCode` grant type with a `RefreshToken` in the `examples/` directory. + ### Grant Types The following OAuth grant types are supported directly, and you can always create your own by implementing `kamermans\OAuth2\GrantType\GrantTypeInterface`: - `AuthorizationCode` @@ -205,8 +208,9 @@ By default, access tokens are not persisted anywhere. There are some built-in m - `DoctrineCacheTokenPersistence` Takes a `Doctrine\Common\Cache\Cache` object and optionally a key name (default: `guzzle-oauth2-token`) where the access token will be saved. - `SimpleCacheTokenPersistence` Takes a PSR-16 SimpleCache and optionally a key name (default: `guzzle-oauth2-token`) where the access token will be saved. This allows any PSR-16 compatible cache to be used. - `Laravel5CacheTokenPersistence` Takes an `Illuminate\Contracts\Cache\Repository` object and optionally a key name (default: `guzzle-oauth2-token`) where the access token will be saved. + - `ClosureTokenPersistence` Allows you to define a token persistence provider by providing closures to handle the persistence functions. -If you want to use your own persistence layer, you should write your own class that implements `TokenPersistenceInterface`. +If you want to use your own persistence layer, you should write your own class that implements `TokenPersistenceInterface` or use the `ClosureTokenPersistence` provider, which is described at the end of this section. To enable token persistence, you must use the `OAuth2Middleware::setTokenPersistence()` or `OAuth2Subscriber::setTokenPersistence()` method, like this: @@ -220,6 +224,40 @@ $grant_type = new ClientCredentials($reauth_client, $reauth_config); $oauth = new OAuth2Middleware($grant_type); $oauth->setTokenPersistence($token_persistence); ``` +### Closure-Based Token Persistence +There are plenty of cases where you would like to use your own caching layer to store the OAuth2 data, but there is no adapter included that works with your cache provider. The `ClosureTokenPersistence` provider makes this case easier by allowing you to define closures that handle the OAuth2 persistence data, as shown in the example below. + +```php +// We'll store everything in an array, but you can use any provider you want +$cache = []; +$cache_key = "foo"; + +// Returns true if the item exists in cache +$exists = function() use (&$cache, $cache_key) { + return array_key_exists($cache_key, $cache); +}; + +// Sets the given $value array in cache +$set = function(array $value) use (&$cache, $cache_key) { + $cache[$cache_key] = $value; +}; + +// Gets the previously-stored value from cache (or null) +$get = function() use (&$cache, $cache_key, $exists) { + return $exists()? $cache[$cache_key]: null; +}; + +// Deletes the previously-stored value from cache (if exists) +$delete = function() use (&$cache, $cache_key, $exists) { + if ($exists()) { + unset($cache[$cache_key]); + } +}; + +$persistence = new ClosureTokenPersistence($set, $get, $delete, $exists); +``` + +> Note: The format of the token data is a PHP associative array. You can flatten the array with `serialize()` or `json_encode()` or whatever else you want before storing it, but remember to decode it back to an array in `get()` before returning it! Also, the above example is not very thread-safe, so if you have a high level of concurrency, you will need to find more atomic ways to handle this logic, or at least wrap things with `try/catch` and handle things gracefully. Please see the `src/Persistence/` directory for more information on persistence. diff --git a/src/Persistence/ClosureTokenPersistence.php b/src/Persistence/ClosureTokenPersistence.php new file mode 100644 index 0000000..c314cf4 --- /dev/null +++ b/src/Persistence/ClosureTokenPersistence.php @@ -0,0 +1,49 @@ +doSaveToken = $saveToken; + $this->doRestoreToken = $restoreToken; + $this->doDeleteToken = $deleteToken; + $this->doHasToken = $hasToken; + } + + public function saveToken(TokenInterface $token) + { + call_user_func($this->doSaveToken, $token->serialize()); + } + + public function restoreToken(TokenInterface $token) + { + $data = call_user_func($this->doRestoreToken); + + if (!is_array($data)) { + return null; + } + + return $token->unserialize($data); + } + + public function deleteToken() + { + call_user_func($this->doDeleteToken); + } + + public function hasToken() + { + return call_user_func($this->doHasToken); + } +} diff --git a/tests/Persistence/ClosureTokenPersistenceTest.php b/tests/Persistence/ClosureTokenPersistenceTest.php new file mode 100644 index 0000000..c413b33 --- /dev/null +++ b/tests/Persistence/ClosureTokenPersistenceTest.php @@ -0,0 +1,46 @@ +cache = []; + } + + public function getInstance() + { + $cache = &$this->cache; + $cache_key = "foo"; + + $exists = function() use (&$cache, $cache_key) { + return array_key_exists($cache_key, $cache); + }; + + $set = function(array $value) use (&$cache, $cache_key) { + $cache[$cache_key] = $value; + }; + + $get = function() use (&$cache, $cache_key, $exists) { + return $exists()? $cache[$cache_key]: null; + }; + + $delete = function() use (&$cache, $cache_key, $exists) { + if ($exists()) { + unset($cache[$cache_key]); + } + }; + + return new ClosureTokenPersistence($set, $get, $delete, $exists); + } +}