1 <?php
  2   3   4   5   6   7   8 
  9 
 10 defined('JPATH_PLATFORM') or die;
 11 
 12 use Joomla\Registry\Registry;
 13 
 14  15  16  17  18  19 
 20 class JOAuth2Client
 21 {
 22      23  24  25 
 26     protected $options;
 27 
 28      29  30  31 
 32     protected $http;
 33 
 34      35  36  37 
 38     protected $input;
 39 
 40      41  42  43 
 44     protected $application;
 45 
 46      47  48  49  50  51  52  53  54  55 
 56     public function __construct(Registry $options = null, JHttp $http = null, JInput $input = null, JApplicationWeb $application = null)
 57     {
 58         $this->options = isset($options) ? $options : new Registry;
 59         $this->http = isset($http) ? $http : new JHttp($this->options);
 60         $this->application = isset($application) ? $application : new JApplicationWeb;
 61         $this->input = isset($input) ? $input : $this->application->input;
 62     }
 63 
 64      65  66  67  68  69  70  71 
 72     public function authenticate()
 73     {
 74         if ($data['code'] = $this->input->get('code', false, 'raw'))
 75         {
 76             $data['grant_type'] = 'authorization_code';
 77             $data['redirect_uri'] = $this->getOption('redirecturi');
 78             $data['client_id'] = $this->getOption('clientid');
 79             $data['client_secret'] = $this->getOption('clientsecret');
 80             $response = $this->http->post($this->getOption('tokenurl'), $data);
 81 
 82             if ($response->code >= 200 && $response->code < 400)
 83             {
 84                 if (strpos($response->headers['Content-Type'], 'application/json') === 0)
 85                 {
 86                     $token = array_merge(json_decode($response->body, true), array('created' => time()));
 87                 }
 88                 else
 89                 {
 90                     parse_str($response->body, $token);
 91                     $token = array_merge($token, array('created' => time()));
 92                 }
 93 
 94                 $this->setToken($token);
 95 
 96                 return $token;
 97             }
 98             else
 99             {
100                 throw new RuntimeException('Error code ' . $response->code . ' received requesting access token: ' . $response->body . '.');
101             }
102         }
103 
104         if ($this->getOption('sendheaders'))
105         {
106             $this->application->redirect($this->createUrl());
107         }
108 
109         return false;
110     }
111 
112     113 114 115 116 117 118 
119     public function isAuthenticated()
120     {
121         $token = $this->getToken();
122 
123         if (!$token || !array_key_exists('access_token', $token))
124         {
125             return false;
126         }
127         elseif (array_key_exists('expires_in', $token) && $token['created'] + $token['expires_in'] < time() + 20)
128         {
129             return false;
130         }
131         else
132         {
133             return true;
134         }
135     }
136 
137     138 139 140 141 142 143 144 
145     public function createUrl()
146     {
147         if (!$this->getOption('authurl') || !$this->getOption('clientid'))
148         {
149             throw new InvalidArgumentException('Authorization URL and client_id are required');
150         }
151 
152         $url = $this->getOption('authurl');
153 
154         if (strpos($url, '?'))
155         {
156             $url .= '&';
157         }
158         else
159         {
160             $url .= '?';
161         }
162 
163         $url .= 'response_type=code';
164         $url .= '&client_id=' . urlencode($this->getOption('clientid'));
165 
166         if ($this->getOption('redirecturi'))
167         {
168             $url .= '&redirect_uri=' . urlencode($this->getOption('redirecturi'));
169         }
170 
171         if ($this->getOption('scope'))
172         {
173             $scope = is_array($this->getOption('scope')) ? implode(' ', $this->getOption('scope')) : $this->getOption('scope');
174             $url .= '&scope=' . urlencode($scope);
175         }
176 
177         if ($this->getOption('state'))
178         {
179             $url .= '&state=' . urlencode($this->getOption('state'));
180         }
181 
182         if (is_array($this->getOption('requestparams')))
183         {
184             foreach ($this->getOption('requestparams') as $key => $value)
185             {
186                 $url .= '&' . $key . '=' . urlencode($value);
187             }
188         }
189 
190         return $url;
191     }
192 
193     194 195 196 197 198 199 200 201 202 203 204 205 206 207 
208     public function query($url, $data = null, $headers = array(), $method = 'get', $timeout = null)
209     {
210         $token = $this->getToken();
211 
212         if (array_key_exists('expires_in', $token) && $token['created'] + $token['expires_in'] < time() + 20)
213         {
214             if (!$this->getOption('userefresh'))
215             {
216                 return false;
217             }
218 
219             $token = $this->refreshToken($token['refresh_token']);
220         }
221 
222         if (!$this->getOption('authmethod') || $this->getOption('authmethod') == 'bearer')
223         {
224             $headers['Authorization'] = 'Bearer ' . $token['access_token'];
225         }
226         elseif ($this->getOption('authmethod') == 'get')
227         {
228             if (strpos($url, '?'))
229             {
230                 $url .= '&';
231             }
232             else
233             {
234                 $url .= '?';
235             }
236 
237             $url .= $this->getOption('getparam') ? $this->getOption('getparam') : 'access_token';
238             $url .= '=' . $token['access_token'];
239         }
240 
241         switch ($method)
242         {
243             case 'head':
244             case 'get':
245             case 'delete':
246             case 'trace':
247             $response = $this->http->$method($url, $headers, $timeout);
248             break;
249             case 'post':
250             case 'put':
251             case 'patch':
252             $response = $this->http->$method($url, $data, $headers, $timeout);
253             break;
254             default:
255             throw new InvalidArgumentException('Unknown HTTP request method: ' . $method . '.');
256         }
257 
258         if ($response->code < 200 || $response->code >= 400)
259         {
260             throw new RuntimeException('Error code ' . $response->code . ' received requesting data: ' . $response->body . '.');
261         }
262 
263         return $response;
264     }
265 
266     267 268 269 270 271 272 273 274 
275     public function getOption($key)
276     {
277         return $this->options->get($key);
278     }
279 
280     281 282 283 284 285 286 287 288 289 
290     public function setOption($key, $value)
291     {
292         $this->options->set($key, $value);
293 
294         return $this;
295     }
296 
297     298 299 300 301 302 303 
304     public function getToken()
305     {
306         return $this->getOption('accesstoken');
307     }
308 
309     310 311 312 313 314 315 316 317 
318     public function setToken($value)
319     {
320         if (is_array($value) && !array_key_exists('expires_in', $value) && array_key_exists('expires', $value))
321         {
322             $value['expires_in'] = $value['expires'];
323             unset($value['expires']);
324         }
325 
326         $this->setOption('accesstoken', $value);
327 
328         return $this;
329     }
330 
331     332 333 334 335 336 337 338 339 340 341 
342     public function refreshToken($token = null)
343     {
344         if (!$this->getOption('userefresh'))
345         {
346             throw new RuntimeException('Refresh token is not supported for this OAuth instance.');
347         }
348 
349         if (!$token)
350         {
351             $token = $this->getToken();
352 
353             if (!array_key_exists('refresh_token', $token))
354             {
355                 throw new RuntimeException('No refresh token is available.');
356             }
357 
358             $token = $token['refresh_token'];
359         }
360 
361         $data['grant_type'] = 'refresh_token';
362         $data['refresh_token'] = $token;
363         $data['client_id'] = $this->getOption('clientid');
364         $data['client_secret'] = $this->getOption('clientsecret');
365         $response = $this->http->post($this->getOption('tokenurl'), $data);
366 
367         if ($response->code >= 200 || $response->code < 400)
368         {
369             if (strpos($response->headers['Content-Type'], 'application/json') === 0)
370             {
371                 $token = array_merge(json_decode($response->body, true), array('created' => time()));
372             }
373             else
374             {
375                 parse_str($response->body, $token);
376                 $token = array_merge($token, array('created' => time()));
377             }
378 
379             $this->setToken($token);
380 
381             return $token;
382         }
383         else
384         {
385             throw new Exception('Error code ' . $response->code . ' received refreshing token: ' . $response->body . '.');
386         }
387     }
388 }
389