* $storage = new OAuth2\Storage\Redis($redis); * $storage->setClientDetails($client_id, $client_secret, $redirect_uri); * */ class Redis implements AuthorizationCodeInterface, AccessTokenInterface, ClientCredentialsInterface, UserCredentialsInterface, RefreshTokenInterface, JwtBearerInterface, ScopeInterface, OpenIDAuthorizationCodeInterface { private $cache; /* The redis client */ protected $redis; /* Configuration array */ protected $config; /** * Redis Storage! * * @param \Predis\Client $redis * @param array $config */ public function __construct($redis, $config=array()) { $this->redis = $redis; $this->config = array_merge(array( 'client_key' => 'oauth_clients:', 'access_token_key' => 'oauth_access_tokens:', 'refresh_token_key' => 'oauth_refresh_tokens:', 'code_key' => 'oauth_authorization_codes:', 'user_key' => 'oauth_users:', 'jwt_key' => 'oauth_jwt:', 'scope_key' => 'oauth_scopes:', ), $config); } protected function getValue($key) { if ( isset($this->cache[$key]) ) { return $this->cache[$key]; } $value = $this->redis->get($key); if ( isset($value) ) { return json_decode($value, true); } else { return false; } } protected function setValue($key, $value, $expire=0) { $this->cache[$key] = $value; $str = json_encode($value); if ($expire > 0) { $seconds = $expire - time(); $ret = $this->redis->setex($key, $seconds, $str); } else { $ret = $this->redis->set($key, $str); } // check that the key was set properly // if this fails, an exception will usually thrown, so this step isn't strictly necessary return is_bool($ret) ? $ret : $ret->getPayload() == 'OK'; } protected function expireValue($key) { unset($this->cache[$key]); return $this->redis->del($key); } /* AuthorizationCodeInterface */ public function getAuthorizationCode($code) { return $this->getValue($this->config['code_key'] . $code); } public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) { return $this->setValue( $this->config['code_key'] . $authorization_code, compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), $expires ); } public function expireAuthorizationCode($code) { $key = $this->config['code_key'] . $code; unset($this->cache[$key]); return $this->expireValue($key); } /* UserCredentialsInterface */ public function checkUserCredentials($username, $password) { $user = $this->getUserDetails($username); return $user && $user['password'] === $password; } public function getUserDetails($username) { return $this->getUser($username); } public function getUser($username) { if (!$userInfo = $this->getValue($this->config['user_key'] . $username)) { return false; } // the default behavior is to use "username" as the user_id return array_merge(array( 'user_id' => $username, ), $userInfo); } public function setUser($username, $password, $first_name = null, $last_name = null) { return $this->setValue( $this->config['user_key'] . $username, compact('username', 'password', 'first_name', 'last_name') ); } /* ClientCredentialsInterface */ public function checkClientCredentials($client_id, $client_secret = null) { if (!$client = $this->getClientDetails($client_id)) { return false; } return isset($client['client_secret']) && $client['client_secret'] == $client_secret; } public function isPublicClient($client_id) { if (!$client = $this->getClientDetails($client_id)) { return false; } return empty($result['client_secret']); } /* ClientInterface */ public function getClientDetails($client_id) { return $this->getValue($this->config['client_key'] . $client_id); } public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) { return $this->setValue( $this->config['client_key'] . $client_id, compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id') ); } public function checkRestrictedGrantType($client_id, $grant_type) { $details = $this->getClientDetails($client_id); if (isset($details['grant_types'])) { $grant_types = explode(' ', $details['grant_types']); return in_array($grant_type, (array) $grant_types); } // if grant_types are not defined, then none are restricted return true; } /* RefreshTokenInterface */ public function getRefreshToken($refresh_token) { return $this->getValue($this->config['refresh_token_key'] . $refresh_token); } public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) { return $this->setValue( $this->config['refresh_token_key'] . $refresh_token, compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'), $expires ); } public function unsetRefreshToken($refresh_token) { return $this->expireValue($this->config['refresh_token_key'] . $refresh_token); } /* AccessTokenInterface */ public function getAccessToken($access_token) { return $this->getValue($this->config['access_token_key'].$access_token); } public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) { return $this->setValue( $this->config['access_token_key'].$access_token, compact('access_token', 'client_id', 'user_id', 'expires', 'scope'), $expires ); } public function unsetAccessToken($access_token) { return $this->expireValue($this->config['access_token_key'] . $access_token); } /* ScopeInterface */ public function scopeExists($scope) { $scope = explode(' ', $scope); $result = $this->getValue($this->config['scope_key'].'supported:global'); $supportedScope = explode(' ', (string) $result); return (count(array_diff($scope, $supportedScope)) == 0); } public function getDefaultScope($client_id = null) { if (is_null($client_id) || !$result = $this->getValue($this->config['scope_key'].'default:'.$client_id)) { $result = $this->getValue($this->config['scope_key'].'default:global'); } return $result; } public function setScope($scope, $client_id = null, $type = 'supported') { if (!in_array($type, array('default', 'supported'))) { throw new \InvalidArgumentException('"$type" must be one of "default", "supported"'); } if (is_null($client_id)) { $key = $this->config['scope_key'].$type.':global'; } else { $key = $this->config['scope_key'].$type.':'.$client_id; } return $this->setValue($key, $scope); } /*JWTBearerInterface */ public function getClientKey($client_id, $subject) { if (!$jwt = $this->getValue($this->config['jwt_key'] . $client_id)) { return false; } if (isset($jwt['subject']) && $jwt['subject'] == $subject) { return $jwt['key']; } return null; } public function setClientKey($client_id, $key, $subject = null) { return $this->setValue($this->config['jwt_key'] . $client_id, array( 'key' => $key, 'subject' => $subject )); } public function getClientScope($client_id) { if (!$clientDetails = $this->getClientDetails($client_id)) { return false; } if (isset($clientDetails['scope'])) { return $clientDetails['scope']; } return null; } public function getJti($client_id, $subject, $audience, $expiration, $jti) { //TODO: Needs redis implementation. throw new \Exception('getJti() for the Redis driver is currently unimplemented.'); } public function setJti($client_id, $subject, $audience, $expiration, $jti) { //TODO: Needs redis implementation. throw new \Exception('setJti() for the Redis driver is currently unimplemented.'); } }