PluginProbe
Gianism / 1.1
Gianism v1.1
4.3.0 4.3.1 4.3.2 4.3.3 4.3.4 4.4.0 5.0.0 5.0.1 5.0.2 5.1.0 5.2.1 5.2.2 5.3.0 6.0.0 6.0.1 trunk 1.0 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 All 65 releases
gianism / sdks / google / auth / apiOAuth.php

apiOAuth.php in Gianism 1.1, at sdks/google/auth/apiOAuth.php

251 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * Copyright 2008 Google Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 require_once "external/OAuth.php";
19
20 /**
21 * Authentication class that deals with 3-Legged OAuth 1.0a authentication
22 *
23 * This class uses the OAuth 1.0a spec which has a slightly different work flow in
24 * how callback urls, request & access tokens are dealt with to prevent a possible
25 * man in the middle attack.
26 *
27 * @author Chris Chabot <chabotc@google.com>
28 *
29 */
30 class apiOAuth extends apiAuth {
31 public $cacheKey;
32 protected $consumerToken;
33 protected $accessToken;
34 protected $privateKeyFile;
35 protected $developerKey;
36 public $service;
37
38 /**
39 * Instantiates the class, but does not initiate the login flow, leaving it
40 * to the discretion of the caller.
41 */
42 public function __construct() {
43 global $apiConfig;
44 if (!empty($apiConfig['developer_key'])) {
45 $this->setDeveloperKey($apiConfig['developer_key']);
46 }
47 $this->consumerToken = new apiClientOAuthConsumer($apiConfig['oauth_consumer_key'], $apiConfig['oauth_consumer_secret'], NULL);
48 $this->signatureMethod = new apiClientOAuthSignatureMethod_HMAC_SHA1();
49 $this->cacheKey = 'OAuth:' . $apiConfig['oauth_consumer_key']; // Scope data to the local user as well, or else multiple local users will share the same OAuth credentials.
50 }
51
52 /**
53 * The 3 legged oauth class needs a way to store the access key and token
54 * it uses the apiCache class to do so.
55 *
56 * Constructing this class will initiate the 3 legged oauth work flow, including redirecting
57 * to the OAuth provider's site if required(!)
58 *
59 * @param string $consumerKey
60 * @param string $consumerSecret
61 * @return apiOAuth3Legged the logged-in provider instance
62 */
63 public function authenticate($service) {
64 global $apiConfig;
65 $this->service = $service;
66 $this->service['authorization_token_url'] .= '?scope=' . apiClientOAuthUtil::urlencodeRFC3986($service['scope']) . '&domain=' . apiClientOAuthUtil::urlencodeRFC3986($apiConfig['site_name']) . '&oauth_token=';
67 if (isset($_GET['oauth_verifier']) && isset($_GET['oauth_token']) && isset($_GET['uid'])) {
68 $uid = $_GET['uid'];
69 $secret = apiClient::$cache->get($this->cacheKey.":nonce:" . $uid);
70 apiClient::$cache->delete($this->cacheKey.":nonce:" . $uid);
71 $token = $this->upgradeRequestToken($_GET['oauth_token'], $secret, $_GET['oauth_verifier']);
72 return json_encode($token);
73 } else {
74 // Initialize the OAuth dance, first request a request token, then kick the client to the authorize URL
75 // First we store the current URL in our cache, so that when the oauth dance is completed we can return there
76 $callbackUrl = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
77 $uid = uniqid();
78 $token = $this->obtainRequestToken($callbackUrl, $uid);
79 apiClient::$cache->set($this->cacheKey.":nonce:" . $uid, $token->secret);
80 $this->redirectToAuthorization($token);
81 }
82 }
83
84 /**
85 * Sets the internal oauth access token (which is returned by the authenticate function), a user should only
86 * go through the authenticate() flow once (which involces a bunch of browser redirections and authentication screens, not fun)
87 * and every time the user comes back the access token from the authentication() flow should be re-used (it essentially never expires)
88 * @param object $accessToken
89 */
90 public function setAccessToken($accessToken) {
91 $accessToken = json_decode($accessToken, true);
92 if ($accessToken == null) {
93 throw new apiAuthException("Could not json decode the access token");
94 }
95 if (! isset($accessToken['key']) || ! isset($accessToken['secret'])) {
96 throw new apiAuthException("Invalid OAuth token, missing key and/or secret");
97 }
98 $this->accessToken = new apiClientOAuthConsumer($accessToken['key'], $accessToken['secret']);
99 }
100
101 /**
102 * Returns the current access token
103 */
104 public function getAccessToken() {
105 return $this->accessToken;
106 }
107
108
109 /**
110 * Set the developer key to use, these are obtained through the API Console
111 */
112 public function setDeveloperKey($developerKey) {
113 $this->developerKey = $developerKey;
114 }
115
116 /**
117 * Upgrades an existing request token to an access token.
118 *
119 * @param apiCache $cache cache class to use (file,apc,memcache,mysql)
120 * @param oauthVerifier
121 */
122 public function upgradeRequestToken($requestToken, $requestTokenSecret, $oauthVerifier) {
123 $ret = $this->requestAccessToken($requestToken, $requestTokenSecret, $oauthVerifier);
124 $matches = array();
125 @parse_str($ret, $matches);
126 if (!isset($matches['oauth_token']) || !isset($matches['oauth_token_secret'])) {
127 throw new apiAuthException("Error authorizing access key (result was: {$ret})");
128 }
129 // The token was upgraded to an access token, we can now continue to use it.
130 $this->accessToken = new apiClientOAuthConsumer(apiClientOAuthUtil::urldecodeRFC3986($matches['oauth_token']), apiClientOAuthUtil::urldecodeRFC3986($matches['oauth_token_secret']));
131 return $this->accessToken;
132 }
133
134 /**
135 * Sends the actual request to exchange an existing request token for an access token.
136 *
137 * @param string $requestToken the existing request token
138 * @param string $requestTokenSecret the request token secret
139 * @return array('http_code' => HTTP response code (200, 404, 401, etc), 'data' => the html document)
140 */
141 protected function requestAccessToken($requestToken, $requestTokenSecret, $oauthVerifier) {
142 $accessToken = new apiClientOAuthConsumer($requestToken, $requestTokenSecret);
143 $accessRequest = apiClientOAuthRequest::from_consumer_and_token($this->consumerToken, $accessToken, "GET", $this->service['access_token_url'], array('oauth_verifier' => $oauthVerifier));
144 $accessRequest->sign_request($this->signatureMethod, $this->consumerToken, $accessToken);
145 $request = apiClient::$io->makeRequest(new apiHttpRequest($accessRequest));
146 if ($request->getResponseHttpCode() != 200) {
147 throw new apiAuthException("Could not fetch access token, http code: " . $request->getResponseHttpCode() . ', response body: '. $request->getResponseBody());
148 }
149 return $request->getResponseBody();
150 }
151
152 /**
153 * Obtains a request token from the specified provider.
154 */
155 public function obtainRequestToken($callbackUrl, $uid) {
156 $callbackParams = (strpos($_SERVER['REQUEST_URI'], '?') !== false ? '&' : '?') . 'uid=' . urlencode($uid);
157 $ret = $this->requestRequestToken($callbackUrl . $callbackParams);
158 $matches = array();
159 preg_match('/oauth_token=(.*)&oauth_token_secret=(.*)&oauth_callback_confirmed=(.*)/', $ret, $matches);
160 if (!is_array($matches) || count($matches) != 4) {
161 throw new apiAuthException("Error retrieving request key ({$ret})");
162 }
163 return new apiClientOAuthToken(apiClientOAuthUtil::urldecodeRFC3986($matches[1]), apiClientOAuthUtil::urldecodeRFC3986($matches[2]));
164 }
165
166 /**
167 * Sends the actual request to obtain a request token.
168 *
169 * @return array('http_code' => HTTP response code (200, 404, 401, etc), 'data' => the html document)
170 */
171 protected function requestRequestToken($callbackUrl) {
172 $requestTokenRequest = apiClientOAuthRequest::from_consumer_and_token($this->consumerToken, NULL, "GET", $this->service['request_token_url'], array());
173 $requestTokenRequest->set_parameter('scope', $this->service['scope']);
174 $requestTokenRequest->set_parameter('oauth_callback', $callbackUrl);
175 $requestTokenRequest->sign_request($this->signatureMethod, $this->consumerToken, NULL);
176 $request = apiClient::$io->makeRequest(new apiHttpRequest($requestTokenRequest));
177 if ($request->getResponseHttpCode() != 200) {
178 throw new apiAuthException("Couldn't fetch request token, http code: " . $request->getResponseHttpCode() . ', response body: '. $request->getResponseBody());
179 }
180 return $request->getResponseBody();
181 }
182
183 /**
184 * Redirect the uset to the (provider's) authorize page, if approved it should kick the user back to the call back URL
185 * which hopefully means we'll end up in the constructor of this class again, but with oauth_continue=1 set
186 *
187 * @param OAuthToken $token the request token
188 * @param string $callbackUrl the URL to return to post-authorization (passed to login site)
189 */
190 public function redirectToAuthorization($token) {
191 $authorizeRedirect = $this->service['authorization_token_url']. $token->key;
192 header("Location: $authorizeRedirect");
193 }
194
195 /**
196 * Sign the request using OAuth. This uses the consumer token and key
197 *
198 * @param string $method the method (get/put/delete/post)
199 * @param string $url the url to sign (http://site/social/rest/people/1/@me)
200 * @param array $params the params that should be appended to the url (count=20 fields=foo, etc)
201 * @param string $postBody for POST/PUT requests, the postBody is included in the signature
202 * @return string the signed url
203 */
204 public function sign(apiHttpRequest $request) {
205 // add the developer key to the request before signing it
206 if ($this->developerKey) {
207 $request->setUrl($request->getUrl() . ((strpos($request->getUrl(), '?') === false) ? '?' : '&') . 'key='.urlencode($this->developerKey));
208 }
209 // and sign the request
210 $oauthRequest = apiClientOAuthRequest::from_request($request->getMethod(), $request->getBaseUrl(), $request->getQueryParams());
211 $params = $this->mergeParameters($request->getQueryParams());
212 foreach ($params as $key => $val) {
213 if (is_array($val)) {
214 $val = implode(',', $val);
215 }
216 $oauthRequest->set_parameter($key, $val);
217 }
218 $oauthRequest->sign_request($this->signatureMethod, $this->consumerToken, $this->accessToken);
219 $authHeaders = $oauthRequest->to_header();
220 $headers = $request->getHeaders();
221 $headers[] = $authHeaders;
222 $request->setHeaders($headers);
223 // and add the access token key to it (since it doesn't include the secret, it's still secure to store this in cache)
224 $request->accessKey = $this->accessToken->key;
225 return $request;
226 }
227
228 /**
229 * Merges the supplied parameters with reasonable defaults for 2 legged oauth. User-supplied parameters
230 * will have precedent over the defaults.
231 *
232 * @param array $params the user-supplied params that will be appended to the url
233 * @return array the combined parameters
234 */
235 protected function mergeParameters($params) {
236 $defaults = array(
237 'oauth_nonce' => md5(microtime() . mt_rand()),
238 'oauth_version' => apiClientOAuthRequest::$version, 'oauth_timestamp' => time(),
239 'oauth_consumer_key' => $this->consumerToken->key
240 );
241 if ($this->accessToken != null) {
242 $params['oauth_token'] = $this->accessToken->key;
243 }
244 return array_merge($defaults, $params);
245 }
246
247 public function createAuthUrl($scope) {
248 // TODO;
249 return null;
250 }
251 }