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