Skip to content
This repository was archived by the owner on Jan 13, 2022. It is now read-only.

Commit ceb0e69

Browse files
author
Fosco Marotto
committed
Merge pull request #129 from SammyK/decouple-access-token-handling
Decoupled access token handling
2 parents 24a51fb + eb58eb7 commit ceb0e69

File tree

5 files changed

+684
-98
lines changed

5 files changed

+684
-98
lines changed

src/Facebook/Entities/AccessToken.php

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
<?php
2+
/**
3+
* Copyright 2014 Facebook, Inc.
4+
*
5+
* You are hereby granted a non-exclusive, worldwide, royalty-free license to
6+
* use, copy, modify, and distribute this software in source code or binary
7+
* form for use in connection with the web services and APIs provided by
8+
* Facebook.
9+
*
10+
* As with any software that integrates with the Facebook platform, your use
11+
* of this software is subject to the Facebook Developer Principles and
12+
* Policies [http://developers.facebook.com/policy/]. This copyright notice
13+
* shall be included in all copies or substantial portions of the software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18+
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21+
* DEALINGS IN THE SOFTWARE.
22+
*
23+
*/
24+
namespace Facebook\Entities;
25+
26+
use Facebook\FacebookRequest;
27+
use Facebook\FacebookRequestException;
28+
use Facebook\FacebookSession;
29+
use Facebook\GraphSessionInfo;
30+
31+
/**
32+
* Class AccessToken
33+
* @package Facebook
34+
*/
35+
class AccessToken
36+
{
37+
38+
/**
39+
* The access token.
40+
*
41+
* @var string
42+
*/
43+
protected $accessToken;
44+
45+
/**
46+
* A unique ID to identify a client.
47+
*
48+
* @var string
49+
*/
50+
protected $machineId;
51+
52+
/**
53+
* Date when token expires.
54+
*
55+
* @var \DateTime|null
56+
*/
57+
protected $expiresAt;
58+
59+
/**
60+
* Create a new access token entity.
61+
*
62+
* @param string $accessToken
63+
* @param int $expiresAt
64+
* @param string|null machineId
65+
*/
66+
public function __construct($accessToken, $expiresAt = 0, $machineId = null)
67+
{
68+
$this->accessToken = $accessToken;
69+
if ($expiresAt) {
70+
$this->setExpiresAtFromTimeStamp($expiresAt);
71+
}
72+
$this->machineId = $machineId;
73+
}
74+
75+
/**
76+
* Setter for expires_at.
77+
*
78+
* @param int $timeStamp
79+
*/
80+
protected function setExpiresAtFromTimeStamp($timeStamp)
81+
{
82+
$dt = new \DateTime();
83+
$dt->setTimestamp($timeStamp);
84+
$this->expiresAt = $dt;
85+
}
86+
87+
/**
88+
* Getter for expiresAt.
89+
*
90+
* @return \DateTime|null
91+
*/
92+
public function getExpiresAt()
93+
{
94+
return $this->expiresAt;
95+
}
96+
97+
/**
98+
* Getter for machineId.
99+
*
100+
* @return string|null
101+
*/
102+
public function getMachineId()
103+
{
104+
return $this->machineId;
105+
}
106+
107+
/**
108+
* Determines whether or not this is a long-lived token.
109+
*
110+
* @return bool
111+
*/
112+
public function isLongLived()
113+
{
114+
if ($this->expiresAt) {
115+
return $this->expiresAt->getTimestamp() > time() + (60 * 60 * 2);
116+
}
117+
return false;
118+
}
119+
120+
/**
121+
* Checks the validity of the access token.
122+
*
123+
* @param string|null $appId Application ID to use
124+
* @param string|null $appSecret App secret value to use
125+
* @param string|null $machineId
126+
*
127+
* @return boolean
128+
*/
129+
public function isValid($appId = null, $appSecret = null, $machineId = null)
130+
{
131+
$accessTokenInfo = $this->getInfo($appId, $appSecret);
132+
$machineId = $machineId ?: $this->machineId;
133+
return static::validateAccessToken($accessTokenInfo, $appId, $machineId);
134+
}
135+
136+
/**
137+
* Ensures the provided GraphSessionInfo object is valid,
138+
* throwing an exception if not. Ensures the appId matches,
139+
* that the machineId matches if it's being used,
140+
* that the token is valid and has not expired.
141+
*
142+
* @param GraphSessionInfo $tokenInfo
143+
* @param string|null $appId Application ID to use
144+
* @param string|null $machineId
145+
*
146+
* @return boolean
147+
*/
148+
public static function validateAccessToken(GraphSessionInfo $tokenInfo,
149+
$appId = null, $machineId = null)
150+
{
151+
$targetAppId = FacebookSession::_getTargetAppId($appId);
152+
153+
$appIdIsValid = $tokenInfo->getAppId() == $targetAppId;
154+
$machineIdIsValid = $tokenInfo->getProperty('machine_id') == $machineId;
155+
$accessTokenIsValid = $tokenInfo->isValid();
156+
157+
// Not all access tokens return an expiration. E.g. an app access token.
158+
if ($tokenInfo->getExpiresAt() instanceof \DateTime) {
159+
$accessTokenIsStillAlive = $tokenInfo->getExpiresAt()->getTimestamp() >= time();
160+
} else {
161+
$accessTokenIsStillAlive = true;
162+
}
163+
164+
return $appIdIsValid && $machineIdIsValid && $accessTokenIsValid && $accessTokenIsStillAlive;
165+
}
166+
167+
/**
168+
* Get a valid access token from a code.
169+
*
170+
* @param string $code
171+
* @param string|null $appId
172+
* @param string|null $appSecret
173+
* @param string|null $machineId
174+
*
175+
* @return AccessToken
176+
*/
177+
public static function getAccessTokenFromCode($code, $appId = null, $appSecret = null, $machineId = null)
178+
{
179+
$params = array(
180+
'code' => $code,
181+
'redirect_uri' => '',
182+
);
183+
184+
if ($machineId) {
185+
$params['machine_id'] = $machineId;
186+
}
187+
188+
return static::requestAccessToken($params, $appId, $appSecret);
189+
}
190+
191+
/**
192+
* Get a valid code from an access token.
193+
*
194+
* @param AccessToken|string $accessToken
195+
* @param string|null $appId
196+
* @param string|null $appSecret
197+
*
198+
* @return AccessToken
199+
*/
200+
public static function getCodeFromAccessToken($accessToken, $appId = null, $appSecret = null)
201+
{
202+
$accessToken = (string) $accessToken;
203+
204+
$params = array(
205+
'access_token' => $accessToken,
206+
'redirect_uri' => '',
207+
);
208+
209+
return static::requestCode($params, $appId, $appSecret);
210+
}
211+
212+
/**
213+
* Exchanges a short lived access token with a long lived access token.
214+
*
215+
* @param string|null $appId
216+
* @param string|null $appSecret
217+
*
218+
* @return AccessToken
219+
*/
220+
public function extend($appId = null, $appSecret = null)
221+
{
222+
$params = array(
223+
'grant_type' => 'fb_exchange_token',
224+
'fb_exchange_token' => $this->accessToken,
225+
);
226+
227+
return static::requestAccessToken($params, $appId, $appSecret);
228+
}
229+
230+
/**
231+
* Request an access token based on a set of params.
232+
*
233+
* @param array $params
234+
* @param string|null $appId
235+
* @param string|null $appSecret
236+
*
237+
* @return AccessToken
238+
*
239+
* @throws FacebookRequestException
240+
*/
241+
public static function requestAccessToken(array $params, $appId = null, $appSecret = null)
242+
{
243+
$response = static::request('/oauth/access_token', $params, $appId, $appSecret);
244+
$data = $response->getResponse();
245+
246+
/**
247+
* @TODO fix this malarkey - getResponse() should always return an object
248+
* @see https://github.com/facebook/facebook-php-sdk-v4/issues/36
249+
*/
250+
if (is_array($data)) {
251+
if (isset($data['access_token'])) {
252+
$expiresAt = isset($data['expires']) ? time() + $data['expires'] : 0;
253+
return new static($data['access_token'], $expiresAt);
254+
}
255+
} elseif($data instanceof \stdClass) {
256+
if (isset($data->access_token)) {
257+
$expiresAt = isset($data->expires_in) ? time() + $data->expires_in : 0;
258+
$machineId = isset($data->machine_id) ? (string) $data->machine_id : null;
259+
return new static((string) $data->access_token, $expiresAt, $machineId);
260+
}
261+
}
262+
263+
throw FacebookRequestException::create(
264+
$response->getRawResponse(),
265+
$data,
266+
401
267+
);
268+
}
269+
270+
/**
271+
* Request a code from a long lived access token.
272+
*
273+
* @param array $params
274+
* @param string|null $appId
275+
* @param string|null $appSecret
276+
*
277+
* @return string
278+
*
279+
* @throws FacebookRequestException
280+
*/
281+
public static function requestCode(array $params, $appId = null, $appSecret = null)
282+
{
283+
$response = static::request('/oauth/client_code', $params, $appId, $appSecret);
284+
$data = $response->getResponse();
285+
286+
if (isset($data->code)) {
287+
return (string) $data->code;
288+
}
289+
290+
throw FacebookRequestException::create(
291+
$response->getRawResponse(),
292+
$data,
293+
401
294+
);
295+
}
296+
297+
/**
298+
* Send a request to Graph with an app access token.
299+
*
300+
* @param string $endpoint
301+
* @param array $params
302+
* @param string|null $appId
303+
* @param string|null $appSecret
304+
*
305+
* @return \Facebook\FacebookResponse
306+
*
307+
* @throws FacebookRequestException
308+
*/
309+
protected static function request($endpoint, array $params, $appId = null, $appSecret = null)
310+
{
311+
$targetAppId = FacebookSession::_getTargetAppId($appId);
312+
$targetAppSecret = FacebookSession::_getTargetAppSecret($appSecret);
313+
314+
if (!isset($params['client_id'])) {
315+
$params['client_id'] = $targetAppId;
316+
}
317+
if (!isset($params['client_secret'])) {
318+
$params['client_secret'] = $targetAppSecret;
319+
}
320+
321+
// The response for this endpoint is not JSON, so it must be handled
322+
// differently, not as a GraphObject.
323+
$request = new FacebookRequest(
324+
FacebookSession::newAppSession($targetAppId, $targetAppSecret),
325+
'GET',
326+
$endpoint,
327+
$params
328+
);
329+
return $request->execute();
330+
}
331+
332+
/**
333+
* Get more info about an access token.
334+
*
335+
* @param string|null $appId
336+
* @param string|null $appSecret
337+
*
338+
* @return GraphSessionInfo
339+
*/
340+
public function getInfo($appId = null, $appSecret = null)
341+
{
342+
$params = array('input_token' => $this->accessToken);
343+
344+
$request = new FacebookRequest(
345+
FacebookSession::newAppSession($appId, $appSecret),
346+
'GET',
347+
'/debug_token',
348+
$params
349+
);
350+
$response = $request->execute()->getGraphObject(GraphSessionInfo::className());
351+
352+
// Update the data on this token
353+
if ($response->getExpiresAt()) {
354+
$this->expiresAt = $response->getExpiresAt();
355+
}
356+
357+
return $response;
358+
}
359+
360+
/**
361+
* Returns the access token as a string.
362+
*
363+
* @return string
364+
*/
365+
public function __toString()
366+
{
367+
return $this->accessToken;
368+
}
369+
370+
}

0 commit comments

Comments
 (0)