1<?php 2/** 3 * Copyright 2017 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 */ 24namespace Facebook\Authentication; 25 26use Facebook\Exceptions\FacebookSDKException; 27 28/** 29 * Class AccessTokenMetadata 30 * 31 * Represents metadata from an access token. 32 * 33 * @package Facebook 34 * @see https://developers.facebook.com/docs/graph-api/reference/debug_token 35 */ 36class AccessTokenMetadata 37{ 38 /** 39 * The access token metadata. 40 * 41 * @var array 42 */ 43 protected $metadata = []; 44 45 /** 46 * Properties that should be cast as DateTime objects. 47 * 48 * @var array 49 */ 50 protected static $dateProperties = ['expires_at', 'issued_at']; 51 52 /** 53 * @param array $metadata 54 * 55 * @throws FacebookSDKException 56 */ 57 public function __construct(array $metadata) 58 { 59 if (!isset($metadata['data'])) { 60 throw new FacebookSDKException('Unexpected debug token response data.', 401); 61 } 62 63 $this->metadata = $metadata['data']; 64 65 $this->castTimestampsToDateTime(); 66 } 67 68 /** 69 * Returns a value from the metadata. 70 * 71 * @param string $field The property to retrieve. 72 * @param mixed $default The default to return if the property doesn't exist. 73 * 74 * @return mixed 75 */ 76 public function getField($field, $default = null) 77 { 78 if (isset($this->metadata[$field])) { 79 return $this->metadata[$field]; 80 } 81 82 return $default; 83 } 84 85 /** 86 * Returns a value from the metadata. 87 * 88 * @param string $field The property to retrieve. 89 * @param mixed $default The default to return if the property doesn't exist. 90 * 91 * @return mixed 92 * 93 * @deprecated 5.0.0 getProperty() has been renamed to getField() 94 * @todo v6: Remove this method 95 */ 96 public function getProperty($field, $default = null) 97 { 98 return $this->getField($field, $default); 99 } 100 101 /** 102 * Returns a value from a child property in the metadata. 103 * 104 * @param string $parentField The parent property. 105 * @param string $field The property to retrieve. 106 * @param mixed $default The default to return if the property doesn't exist. 107 * 108 * @return mixed 109 */ 110 public function getChildProperty($parentField, $field, $default = null) 111 { 112 if (!isset($this->metadata[$parentField])) { 113 return $default; 114 } 115 116 if (!isset($this->metadata[$parentField][$field])) { 117 return $default; 118 } 119 120 return $this->metadata[$parentField][$field]; 121 } 122 123 /** 124 * Returns a value from the error metadata. 125 * 126 * @param string $field The property to retrieve. 127 * @param mixed $default The default to return if the property doesn't exist. 128 * 129 * @return mixed 130 */ 131 public function getErrorProperty($field, $default = null) 132 { 133 return $this->getChildProperty('error', $field, $default); 134 } 135 136 /** 137 * Returns a value from the "metadata" metadata. *Brain explodes* 138 * 139 * @param string $field The property to retrieve. 140 * @param mixed $default The default to return if the property doesn't exist. 141 * 142 * @return mixed 143 */ 144 public function getMetadataProperty($field, $default = null) 145 { 146 return $this->getChildProperty('metadata', $field, $default); 147 } 148 149 /** 150 * The ID of the application this access token is for. 151 * 152 * @return string|null 153 */ 154 public function getAppId() 155 { 156 return $this->getField('app_id'); 157 } 158 159 /** 160 * Name of the application this access token is for. 161 * 162 * @return string|null 163 */ 164 public function getApplication() 165 { 166 return $this->getField('application'); 167 } 168 169 /** 170 * Any error that a request to the graph api 171 * would return due to the access token. 172 * 173 * @return bool|null 174 */ 175 public function isError() 176 { 177 return $this->getField('error') !== null; 178 } 179 180 /** 181 * The error code for the error. 182 * 183 * @return int|null 184 */ 185 public function getErrorCode() 186 { 187 return $this->getErrorProperty('code'); 188 } 189 190 /** 191 * The error message for the error. 192 * 193 * @return string|null 194 */ 195 public function getErrorMessage() 196 { 197 return $this->getErrorProperty('message'); 198 } 199 200 /** 201 * The error subcode for the error. 202 * 203 * @return int|null 204 */ 205 public function getErrorSubcode() 206 { 207 return $this->getErrorProperty('subcode'); 208 } 209 210 /** 211 * DateTime when this access token expires. 212 * 213 * @return \DateTime|null 214 */ 215 public function getExpiresAt() 216 { 217 return $this->getField('expires_at'); 218 } 219 220 /** 221 * Whether the access token is still valid or not. 222 * 223 * @return boolean|null 224 */ 225 public function getIsValid() 226 { 227 return $this->getField('is_valid'); 228 } 229 230 /** 231 * DateTime when this access token was issued. 232 * 233 * Note that the issued_at field is not returned 234 * for short-lived access tokens. 235 * 236 * @see https://developers.facebook.com/docs/facebook-login/access-tokens#debug 237 * 238 * @return \DateTime|null 239 */ 240 public function getIssuedAt() 241 { 242 return $this->getField('issued_at'); 243 } 244 245 /** 246 * General metadata associated with the access token. 247 * Can contain data like 'sso', 'auth_type', 'auth_nonce'. 248 * 249 * @return array|null 250 */ 251 public function getMetadata() 252 { 253 return $this->getField('metadata'); 254 } 255 256 /** 257 * The 'sso' child property from the 'metadata' parent property. 258 * 259 * @return string|null 260 */ 261 public function getSso() 262 { 263 return $this->getMetadataProperty('sso'); 264 } 265 266 /** 267 * The 'auth_type' child property from the 'metadata' parent property. 268 * 269 * @return string|null 270 */ 271 public function getAuthType() 272 { 273 return $this->getMetadataProperty('auth_type'); 274 } 275 276 /** 277 * The 'auth_nonce' child property from the 'metadata' parent property. 278 * 279 * @return string|null 280 */ 281 public function getAuthNonce() 282 { 283 return $this->getMetadataProperty('auth_nonce'); 284 } 285 286 /** 287 * For impersonated access tokens, the ID of 288 * the page this token contains. 289 * 290 * @return string|null 291 */ 292 public function getProfileId() 293 { 294 return $this->getField('profile_id'); 295 } 296 297 /** 298 * List of permissions that the user has granted for 299 * the app in this access token. 300 * 301 * @return array 302 */ 303 public function getScopes() 304 { 305 return $this->getField('scopes'); 306 } 307 308 /** 309 * The ID of the user this access token is for. 310 * 311 * @return string|null 312 */ 313 public function getUserId() 314 { 315 return $this->getField('user_id'); 316 } 317 318 /** 319 * Ensures the app ID from the access token 320 * metadata is what we expect. 321 * 322 * @param string $appId 323 * 324 * @throws FacebookSDKException 325 */ 326 public function validateAppId($appId) 327 { 328 if ($this->getAppId() !== $appId) { 329 throw new FacebookSDKException('Access token metadata contains unexpected app ID.', 401); 330 } 331 } 332 333 /** 334 * Ensures the user ID from the access token 335 * metadata is what we expect. 336 * 337 * @param string $userId 338 * 339 * @throws FacebookSDKException 340 */ 341 public function validateUserId($userId) 342 { 343 if ($this->getUserId() !== $userId) { 344 throw new FacebookSDKException('Access token metadata contains unexpected user ID.', 401); 345 } 346 } 347 348 /** 349 * Ensures the access token has not expired yet. 350 * 351 * @throws FacebookSDKException 352 */ 353 public function validateExpiration() 354 { 355 if (!$this->getExpiresAt() instanceof \DateTime) { 356 return; 357 } 358 359 if ($this->getExpiresAt()->getTimestamp() < time()) { 360 throw new FacebookSDKException('Inspection of access token metadata shows that the access token has expired.', 401); 361 } 362 } 363 364 /** 365 * Converts a unix timestamp into a DateTime entity. 366 * 367 * @param int $timestamp 368 * 369 * @return \DateTime 370 */ 371 private function convertTimestampToDateTime($timestamp) 372 { 373 $dt = new \DateTime(); 374 $dt->setTimestamp($timestamp); 375 376 return $dt; 377 } 378 379 /** 380 * Casts the unix timestamps as DateTime entities. 381 */ 382 private function castTimestampsToDateTime() 383 { 384 foreach (static::$dateProperties as $key) { 385 if (isset($this->metadata[$key]) && $this->metadata[$key] !== 0) { 386 $this->metadata[$key] = $this->convertTimestampToDateTime($this->metadata[$key]); 387 } 388 } 389 } 390} 391