diff --git a/README.md b/README.md index 7a9f4ce..ab945a5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Codeigniter OAuth 2.0 +## !!! DEPRECATED !!! +**This package is no longer actively maintained. If somebody sends in a pull request with some major security bug +then I'll merge it, but otherwise nothing will be done. Use [thephpleague/oauth2-client](https://github.com/thephpleague/oauth2-client) instead.** + Authorize users with your application in a driver-base fashion meaning one implementation works for multiple OAuth 2 providers. This is only to authenticate onto OAuth2 providers and not to build an OAuth2 service. Note that this Spark ONLY provides the authorization mechanism. There's an example controller below, however in a later version there will be a full controller. @@ -40,7 +44,7 @@ class Auth extends CI_Controller { $this->load->helper('url_helper'); - $this->load->spark('oauth2/0.3.0'); + $this->load->spark('oauth2/0.3.1'); $provider = $this->oauth2->provider($provider, array( 'id' => 'your-client-id', @@ -91,4 +95,4 @@ Contribute 3. Write a test which shows that the bug was fixed or that the feature works as expected 4. Send a pull request and bug me until I merge it -[the repository]: https://github.com/philsturgeon/codeigniter-oauth2 \ No newline at end of file +[the repository]: https://github.com/philsturgeon/codeigniter-oauth2 diff --git a/config/autoload.php b/config/autoload.php index caa6440..2565b85 100644 --- a/config/autoload.php +++ b/config/autoload.php @@ -1,3 +1,3 @@ result['error'])) + { + $message = $this->result['error']; + if (is_string($message)) + { + // OAuth 2.0 Draft 10 style + return $message; + } + } + return 'Exception'; + } /** * To make debugging easier. * - * @returns - * The string representation of the error. + * @return string The string representation of the error. */ public function __toString() { diff --git a/libraries/OAuth2.php b/libraries/OAuth2.php index 47d9ccc..cb594cb 100644 --- a/libraries/OAuth2.php +++ b/libraries/OAuth2.php @@ -1,5 +1,5 @@ */ class OAuth2 { - + /** * Create a new provider. * * // Load the Twitter provider * $provider = $this->oauth2->provider('twitter'); * - * @param string provider name - * @param array provider options - * @return OAuth_Provider + * @param string $name provider name + * @param array $options provider options + * @return OAuth2_Provider */ public static function provider($name, array $options = NULL) { - include_once 'Provider/'.strtolower($name).'.php'; - - $class = 'OAuth2_Provider_'.ucfirst($name); + $name = ucfirst(strtolower($name)); + + include_once 'Provider/'.$name.'.php'; + + $class = 'OAuth2_Provider_'.$name; return new $class($options); } - + } \ No newline at end of file diff --git a/libraries/Provider.php b/libraries/Provider.php index 190c921..8ccf80e 100644 --- a/libraries/Provider.php +++ b/libraries/Provider.php @@ -51,8 +51,8 @@ abstract class OAuth2_Provider * * Any of the provider options can be set here, such as app_id or secret. * - * @param array provider options - * @return void + * @param array $options provider options + * @throws Exception if a required option is not provided */ public function __construct(array $options = array()) { @@ -82,7 +82,7 @@ public function __construct(array $options = array()) * // Get the provider signature * $signature = $provider->signature; * - * @param string variable name + * @param string $key variable name * @return mixed */ public function __get($key) @@ -108,12 +108,18 @@ abstract public function url_authorize(); */ abstract public function url_access_token(); + /** + * @param OAuth2_Token_Access $token + * @return array basic user info + */ + abstract public function get_user_info(OAuth2_Token_Access $token); + /* * Get an authorization code from Facebook. Redirects to Facebook, which this redirects back to the app using the redirect address you've set. */ public function authorize($options = array()) { - $state = md5(uniqid(rand(), TRUE)); + $state = md5(uniqid(rand(), true)); get_instance()->session->set_userdata('state', $state); $params = array( @@ -125,6 +131,8 @@ public function authorize($options = array()) 'approval_prompt' => 'force' // - google force-recheck ); + $params = array_merge($params, $this->params); + redirect($this->url_authorize().'?'.http_build_query($params)); } @@ -141,6 +149,8 @@ public function access($code, $options = array()) 'client_secret' => $this->client_secret, 'grant_type' => isset($options['grant_type']) ? $options['grant_type'] : 'authorization_code', ); + + $params = array_merge($params, $this->params); switch ($params['grant_type']) { @@ -154,7 +164,7 @@ public function access($code, $options = array()) break; } - $response = null; + $response = null; $url = $this->url_access_token(); switch ($this->method) diff --git a/libraries/Provider/Appnet.php b/libraries/Provider/Appnet.php new file mode 100644 index 0000000..14203a1 --- /dev/null +++ b/libraries/Provider/Appnet.php @@ -0,0 +1,55 @@ + $token->access_token, + )); + + $user = json_decode(file_get_contents($url)); + + // Create a response from the request + return array( + 'uid' => $user->id, + 'nickname' => $user->username, + 'name' => $user->name + ); + + } + +} \ No newline at end of file diff --git a/libraries/Provider/Blooie.php b/libraries/Provider/Blooie.php new file mode 100644 index 0000000..212d788 --- /dev/null +++ b/libraries/Provider/Blooie.php @@ -0,0 +1,69 @@ + $token->access_token, + )); + + $user = json_decode(file_get_contents($url)); + + // Create a response from the request + return array( + 'uid' => $user->id, + 'nickname' => $user->username, + 'name' => $user->name, + 'first_name' => $user->first_name, + 'last_name' => $user->last_name, + 'email' => isset($user->email) ? $user->email : null, + 'location' => isset($user->hometown->name) ? $user->hometown->name : null, + 'description' => isset($user->bio) ? $user->bio : null, + 'image' => 'https://graph.facebook.com/me/picture?type=normal&access_token='.$token->access_token, + 'urls' => array( + 'Facebook' => $user->link, + ), + ); + } +} diff --git a/libraries/Provider/Facebook.php b/libraries/Provider/Facebook.php index 7d0433e..91f27a0 100644 --- a/libraries/Provider/Facebook.php +++ b/libraries/Provider/Facebook.php @@ -1,4 +1,4 @@ - $user->id, - 'nickname' => $user->username, + 'nickname' => isset($user->username) ? $user->username : null, 'name' => $user->name, 'first_name' => $user->first_name, 'last_name' => $user->last_name, diff --git a/libraries/Provider/Google.php b/libraries/Provider/Google.php index 0bf0df3..b76d343 100755 --- a/libraries/Provider/Google.php +++ b/libraries/Provider/Google.php @@ -76,7 +76,7 @@ public function get_user_info(OAuth2_Token_Access $token) 'last_name' => $user['family_name'], 'email' => $user['email'], 'location' => null, - 'image' => $user['picture'], + 'image' => (isset($user['picture'])) ? $user['picture'] : null, 'description' => null, 'urls' => array(), ); diff --git a/libraries/Provider/Instagram.php b/libraries/Provider/Instagram.php index 4df8f6c..39c8bef 100644 --- a/libraries/Provider/Instagram.php +++ b/libraries/Provider/Instagram.php @@ -9,7 +9,7 @@ * @license http://philsturgeon.co.uk/code/dbad-license */ -class OAuth_Provider_Instagram extends OAuth2_Provider +class OAuth2_Provider_Instagram extends OAuth2_Provider { /** * @var string scope separator, most use "," but some like Google are spaces diff --git a/libraries/Provider/Linkedin.php b/libraries/Provider/Linkedin.php new file mode 100644 index 0000000..3878b47 --- /dev/null +++ b/libraries/Provider/Linkedin.php @@ -0,0 +1,70 @@ + $token->access_token, + )); + $user = json_decode(file_get_contents($url_profile), true); + + $url_email = 'https://api.linkedin.com/v1/people/~/email-address?format=json&' . http_build_query(array( + 'oauth2_access_token' => $token->access_token, + )); + $user_email = json_decode(file_get_contents($url_email), true); + + $args = array(); + parse_str(parse_url(http://github.com/$user['siteStandardProfileRequest']['url'],%20PHP_URL_QUERY), $args); + $user_id = $args['id']; + return array( + 'id' => $user_id, + 'first_name' => $user['firstName'], + 'last_name' => $user['lastName'], + 'name' => $user['firstName'] . ' ' . $user['lastName'], + 'description' => $user['headline'], + 'email' => $user_email, + 'urls' => array( + 'LinkedIn' => $user['siteStandardProfileRequest']['url'] + ), + ); + } +} diff --git a/libraries/Provider/Mailru.php b/libraries/Provider/Mailru.php new file mode 100644 index 0000000..25234a2 --- /dev/null +++ b/libraries/Provider/Mailru.php @@ -0,0 +1,73 @@ + $value) { + $params .= "$key=$value"; + } + return md5($params . $secret_key); + } + + public function get_user_info(OAuth2_Token_Access $token) + { + $request_params = array( + 'app_id' => $this->client_id, + 'method' => 'users.getInfo', + 'uids' => $token->uid, + 'access_token' => $token->access_token, + 'secure' => 1 + ); + + $sig = $this->sign_server_server($request_params,$this->client_secret); + $url = 'http://www.appsmail.ru/platform/api?'.http_build_query($request_params).'&sig='.$sig; + + $user = json_decode(file_get_contents($url)); + + return array( + 'uid' => $user[0]->uid, + 'nickname' => $user[0]->nick, + 'name' => $user[0]->first_name.' '.$user[0]->last_name, + 'first_name' => $user[0]->first_name, + 'last_name' => $user[0]->last_name, + 'email' => isset($user[0]->email) ? $user[0]->email : null, + 'image' => isset($user[0]->pic_big) ? $user[0]->pic_big : null, + ); + } + + public function authorize($options = array()) + { + $state = md5(uniqid(rand(), TRUE)); + get_instance()->session->set_userdata('state', $state); + + $params = array( + 'client_id' => $this->client_id, + 'redirect_uri' => isset($options['redirect_uri']) ? $options['redirect_uri'] : $this->redirect_uri, + 'response_type' => 'code', + ); + + redirect($this->url_authorize().'?'.http_build_query($params)); + } +} diff --git a/libraries/Provider/Paypal.php b/libraries/Provider/Paypal.php index 72d065f..499b7c9 100644 --- a/libraries/Provider/Paypal.php +++ b/libraries/Provider/Paypal.php @@ -14,7 +14,7 @@ class OAuth2_Provider_Paypal extends OAuth2_Provider /** * @var string default scope (useful if a scope is required for user info) */ - protected $scope = array('https://identity.x.com/xidentity/resources/profile/me'); + protected $scope = array('https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/userinfo'); /** * @var string the method to use when requesting tokens @@ -23,22 +23,22 @@ class OAuth2_Provider_Paypal extends OAuth2_Provider public function url_authorize() { - return 'https://identity.x.com/xidentity/resources/authorize'; + return 'https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize'; } public function url_access_token() { - return 'https://identity.x.com/xidentity/oauthtokenservice'; + return 'https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/tokenservice'; } public function get_user_info(OAuth2_Token_Access $token) { - $url = 'https://identity.x.com/xidentity/resources/profile/me?' . http_build_query(array( + $url = 'https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/userinfo?' . http_build_query(array( 'oauth_token' => $token->access_token )); - $user = json_decode(file_get_contents($url)); - $user = $user->identity; + $user = json_decode(file_get_contents($url),true); + $user = $user['identity']; return array( 'uid' => $user['userId'], diff --git a/libraries/Provider/Vkontakte.php b/libraries/Provider/Vkontakte.php new file mode 100644 index 0000000..bdad78e --- /dev/null +++ b/libraries/Provider/Vkontakte.php @@ -0,0 +1,54 @@ + $token->uid, + 'fields' => implode(",",$scope), + 'access_token' => $token->access_token, + )); + + $user = json_decode(file_get_contents($url))->response; + + if(sizeof($user)==0) + return null; + else + $user = $user[0]; + + return array( + 'uid' => $user->uid, + 'nickname' => isset($user->nickname) ? $user->nickname : null, + 'name' => isset($user->name) ? $user->name : null, + 'first_name' => isset($user->first_name) ? $user->first_name : null, + 'last_name' => isset($user->last_name) ? $user->last_name : null, + 'email' => null, + 'location' => null, + 'description' => null, + 'image' => isset($user->photo_big) ? $user->photo_big : null, + 'urls' => array(), + ); + } +} diff --git a/libraries/Provider/Yandex.php b/libraries/Provider/Yandex.php new file mode 100644 index 0000000..7a8f4f8 --- /dev/null +++ b/libraries/Provider/Yandex.php @@ -0,0 +1,115 @@ + array( + 'method' => 'GET', + 'header' => 'Authorization: OAuth '.$token->access_token + ) + ); + $_default_opts = stream_context_get_params(stream_context_get_default()); + + $opts = array_merge_recursive($_default_opts['options'], $opts); + $context = stream_context_create($opts); + $url = 'http://api-yaru.yandex.ru/me/?format=json'; + + $user = json_decode(file_get_contents($url,false,$context)); + + preg_match("/\d+$/",$user->id,$uid); + + return array( + 'uid' => $uid[0], + 'nickname' => isset($user->name) ? $user->name : null, + 'name' => isset($user->name) ? $user->name : null, + 'first_name' => isset($user->first_name) ? $user->first_name : null, + 'last_name' => isset($user->last_name) ? $user->last_name : null, + 'email' => isset($user->email) ? $user->email : null, + 'location' => isset($user->hometown->name) ? $user->hometown->name : null, + 'description' => isset($user->bio) ? $user->bio : null, + 'image' => $user->links->userpic, + ); + } + + public function access($code, $options = array()) + { + $params = array( + 'client_id' => $this->client_id, + 'client_secret' => $this->client_secret, + 'grant_type' => isset($options['grant_type']) ? $options['grant_type'] : 'authorization_code', + ); + + switch ($params['grant_type']) + { + case 'authorization_code': + $params['code'] = $code; + $params['redirect_uri'] = isset($options['redirect_uri']) ? $options['redirect_uri'] : $this->redirect_uri; + break; + + case 'refresh_token': + $params['refresh_token'] = $code; + break; + } + + $response = null; + $url = $this->url_access_token(); + + $curl = curl_init($url); + + $headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8;'; + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + +// curl_setopt($curl, CURLOPT_USERAGENT, 'yamolib-php'); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30); + curl_setopt($curl, CURLOPT_TIMEOUT, 80); + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params)); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + // curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); + // curl_setopt($curl, CURLOPT_CAINFO, dirname(__FILE__) . '/../data/ca-certificate.crt'); + + $response = curl_exec($curl); + curl_close($curl); + + $return = json_decode($response, true); + + if ( ! empty($return['error'])) + { + throw new OAuth2_Exception($return); + } + + switch ($params['grant_type']) + { + case 'authorization_code': + return OAuth2_Token::factory('access', $return); + break; + + case 'refresh_token': + return OAuth2_Token::factory('refresh', $return); + break; + } + } + +} diff --git a/libraries/Token.php b/libraries/Token.php index 5828448..512b006 100644 --- a/libraries/Token.php +++ b/libraries/Token.php @@ -15,16 +15,18 @@ abstract class OAuth2_Token { * * $token = OAuth2_Token::factory($name); * - * @param string token type - * @param array token options - * @return Token + * @param string $name token type + * @param array $options token options + * @return OAuth2_Token */ public static function factory($name = 'access', array $options = null) { - include_once 'Token/'.strtolower($name).'.php'; - - $class = 'OAuth2_Token_'.ucfirst($name); - + $name = ucfirst(strtolower($name)); + + include_once 'Token/'.$name.'.php'; + + $class = 'OAuth2_Token_'.$name; + return new $class($options); } @@ -34,21 +36,21 @@ public static function factory($name = 'access', array $options = null) * // Get the token secret * $secret = $token->secret; * - * @param string variable name + * @param string $key variable name * @return mixed */ public function __get($key) { return $this->$key; } - + /** * Return a boolean if the property is set * * // Get the token secret * if ($token->secret) exit('YAY SECRET'); * - * @param string variable name + * @param string $key variable name * @return bool */ public function __isset($key) diff --git a/libraries/Token/Access.php b/libraries/Token/Access.php index 78548d6..e1714b2 100644 --- a/libraries/Token/Access.php +++ b/libraries/Token/Access.php @@ -33,8 +33,9 @@ class OAuth2_Token_Access extends OAuth2_Token /** * Sets the token, expiry, etc values. * - * @param array token options - * @return void + * @param array $options token options + * + * @throws Exception if required options are missing */ public function __construct(array $options = null) { @@ -53,6 +54,12 @@ public function __construct(array $options = null) // Some providers (not many) give the uid here, so lets take it isset($options['uid']) and $this->uid = $options['uid']; + //Vkontakte uses user_id instead of uid + isset($options['user_id']) and $this->uid = $options['user_id']; + + //Mailru uses x_mailru_vid instead of uid + isset($options['x_mailru_vid']) and $this->uid = $options['x_mailru_vid']; + // We need to know when the token expires, add num. seconds to current time isset($options['expires_in']) and $this->expires = time() + ((int) $options['expires_in']); diff --git a/spark.info b/spark.info index 85c013c..1506939 100644 --- a/spark.info +++ b/spark.info @@ -1,3 +1,3 @@ name: codeigniter-oauth2 -version: 0.3.0 -compatibility: 2.0.0 \ No newline at end of file +version: 0.3.1 +compatibility: 2.1.0 \ No newline at end of file