Facebook login implemented

This commit is contained in:
Joey Lai
2017-01-31 16:12:22 +01:00
parent 08f668859c
commit 8e98001217
82 changed files with 11126 additions and 133 deletions

View File

@@ -0,0 +1,160 @@
<?php
/**
* Copyright 2014 Facebook, Inc.
*
* You are hereby granted a non-exclusive, worldwide, royalty-free license to
* use, copy, modify, and distribute this software in source code or binary
* form for use in connection with the web services and APIs provided by
* Facebook.
*
* As with any software that integrates with the Facebook platform, your use
* of this software is subject to the Facebook Developer Principles and
* Policies [http://developers.facebook.com/policy/]. This copyright notice
* shall be included in all copies or substantial portions of the software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace Facebook\Authentication;
/**
* Class AccessToken
*
* @package Facebook
*/
class AccessToken
{
/**
* The access token value.
*
* @var string
*/
protected $value = '';
/**
* Date when token expires.
*
* @var \DateTime|null
*/
protected $expiresAt;
/**
* Create a new access token entity.
*
* @param string $accessToken
* @param int $expiresAt
*/
public function __construct($accessToken, $expiresAt = 0)
{
$this->value = $accessToken;
if ($expiresAt) {
$this->setExpiresAtFromTimeStamp($expiresAt);
}
}
/**
* Generate an app secret proof to sign a request to Graph.
*
* @param string $appSecret The app secret.
*
* @return string
*/
public function getAppSecretProof($appSecret)
{
return hash_hmac('sha256', $this->value, $appSecret);
}
/**
* Getter for expiresAt.
*
* @return \DateTime|null
*/
public function getExpiresAt()
{
return $this->expiresAt;
}
/**
* Determines whether or not this is an app access token.
*
* @return bool
*/
public function isAppAccessToken()
{
return strpos($this->value, '|') !== false;
}
/**
* Determines whether or not this is a long-lived token.
*
* @return bool
*/
public function isLongLived()
{
if ($this->expiresAt) {
return $this->expiresAt->getTimestamp() > time() + (60 * 60 * 2);
}
if ($this->isAppAccessToken()) {
return true;
}
return false;
}
/**
* Checks the expiration of the access token.
*
* @return boolean|null
*/
public function isExpired()
{
if ($this->getExpiresAt() instanceof \DateTime) {
return $this->getExpiresAt()->getTimestamp() < time();
}
if ($this->isAppAccessToken()) {
return false;
}
return null;
}
/**
* Returns the access token as a string.
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Returns the access token as a string.
*
* @return string
*/
public function __toString()
{
return $this->getValue();
}
/**
* Setter for expires_at.
*
* @param int $timeStamp
*/
protected function setExpiresAtFromTimeStamp($timeStamp)
{
$dt = new \DateTime();
$dt->setTimestamp($timeStamp);
$this->expiresAt = $dt;
}
}

View File

@@ -0,0 +1,390 @@
<?php
/**
* Copyright 2014 Facebook, Inc.
*
* You are hereby granted a non-exclusive, worldwide, royalty-free license to
* use, copy, modify, and distribute this software in source code or binary
* form for use in connection with the web services and APIs provided by
* Facebook.
*
* As with any software that integrates with the Facebook platform, your use
* of this software is subject to the Facebook Developer Principles and
* Policies [http://developers.facebook.com/policy/]. This copyright notice
* shall be included in all copies or substantial portions of the software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace Facebook\Authentication;
use Facebook\Exceptions\FacebookSDKException;
/**
* Class AccessTokenMetadata
*
* Represents metadata from an access token.
*
* @package Facebook
* @see https://developers.facebook.com/docs/graph-api/reference/debug_token
*/
class AccessTokenMetadata
{
/**
* The access token metadata.
*
* @var array
*/
protected $metadata = [];
/**
* Properties that should be cast as DateTime objects.
*
* @var array
*/
protected static $dateProperties = ['expires_at', 'issued_at'];
/**
* @param array $metadata
*
* @throws FacebookSDKException
*/
public function __construct(array $metadata)
{
if (!isset($metadata['data'])) {
throw new FacebookSDKException('Unexpected debug token response data.', 401);
}
$this->metadata = $metadata['data'];
$this->castTimestampsToDateTime();
}
/**
* Returns a value from the metadata.
*
* @param string $field The property to retrieve.
* @param mixed $default The default to return if the property doesn't exist.
*
* @return mixed
*/
public function getField($field, $default = null)
{
if (isset($this->metadata[$field])) {
return $this->metadata[$field];
}
return $default;
}
/**
* Returns a value from the metadata.
*
* @param string $field The property to retrieve.
* @param mixed $default The default to return if the property doesn't exist.
*
* @return mixed
*
* @deprecated 5.0.0 getProperty() has been renamed to getField()
* @todo v6: Remove this method
*/
public function getProperty($field, $default = null)
{
return $this->getField($field, $default);
}
/**
* Returns a value from a child property in the metadata.
*
* @param string $parentField The parent property.
* @param string $field The property to retrieve.
* @param mixed $default The default to return if the property doesn't exist.
*
* @return mixed
*/
public function getChildProperty($parentField, $field, $default = null)
{
if (!isset($this->metadata[$parentField])) {
return $default;
}
if (!isset($this->metadata[$parentField][$field])) {
return $default;
}
return $this->metadata[$parentField][$field];
}
/**
* Returns a value from the error metadata.
*
* @param string $field The property to retrieve.
* @param mixed $default The default to return if the property doesn't exist.
*
* @return mixed
*/
public function getErrorProperty($field, $default = null)
{
return $this->getChildProperty('error', $field, $default);
}
/**
* Returns a value from the "metadata" metadata. *Brain explodes*
*
* @param string $field The property to retrieve.
* @param mixed $default The default to return if the property doesn't exist.
*
* @return mixed
*/
public function getMetadataProperty($field, $default = null)
{
return $this->getChildProperty('metadata', $field, $default);
}
/**
* The ID of the application this access token is for.
*
* @return string|null
*/
public function getAppId()
{
return $this->getField('app_id');
}
/**
* Name of the application this access token is for.
*
* @return string|null
*/
public function getApplication()
{
return $this->getField('application');
}
/**
* Any error that a request to the graph api
* would return due to the access token.
*
* @return bool|null
*/
public function isError()
{
return $this->getField('error') !== null;
}
/**
* The error code for the error.
*
* @return int|null
*/
public function getErrorCode()
{
return $this->getErrorProperty('code');
}
/**
* The error message for the error.
*
* @return string|null
*/
public function getErrorMessage()
{
return $this->getErrorProperty('message');
}
/**
* The error subcode for the error.
*
* @return int|null
*/
public function getErrorSubcode()
{
return $this->getErrorProperty('subcode');
}
/**
* DateTime when this access token expires.
*
* @return \DateTime|null
*/
public function getExpiresAt()
{
return $this->getField('expires_at');
}
/**
* Whether the access token is still valid or not.
*
* @return boolean|null
*/
public function getIsValid()
{
return $this->getField('is_valid');
}
/**
* DateTime when this access token was issued.
*
* Note that the issued_at field is not returned
* for short-lived access tokens.
*
* @see https://developers.facebook.com/docs/facebook-login/access-tokens#debug
*
* @return \DateTime|null
*/
public function getIssuedAt()
{
return $this->getField('issued_at');
}
/**
* General metadata associated with the access token.
* Can contain data like 'sso', 'auth_type', 'auth_nonce'.
*
* @return array|null
*/
public function getMetadata()
{
return $this->getField('metadata');
}
/**
* The 'sso' child property from the 'metadata' parent property.
*
* @return string|null
*/
public function getSso()
{
return $this->getMetadataProperty('sso');
}
/**
* The 'auth_type' child property from the 'metadata' parent property.
*
* @return string|null
*/
public function getAuthType()
{
return $this->getMetadataProperty('auth_type');
}
/**
* The 'auth_nonce' child property from the 'metadata' parent property.
*
* @return string|null
*/
public function getAuthNonce()
{
return $this->getMetadataProperty('auth_nonce');
}
/**
* For impersonated access tokens, the ID of
* the page this token contains.
*
* @return string|null
*/
public function getProfileId()
{
return $this->getField('profile_id');
}
/**
* List of permissions that the user has granted for
* the app in this access token.
*
* @return array
*/
public function getScopes()
{
return $this->getField('scopes');
}
/**
* The ID of the user this access token is for.
*
* @return string|null
*/
public function getUserId()
{
return $this->getField('user_id');
}
/**
* Ensures the app ID from the access token
* metadata is what we expect.
*
* @param string $appId
*
* @throws FacebookSDKException
*/
public function validateAppId($appId)
{
if ($this->getAppId() !== $appId) {
throw new FacebookSDKException('Access token metadata contains unexpected app ID.', 401);
}
}
/**
* Ensures the user ID from the access token
* metadata is what we expect.
*
* @param string $userId
*
* @throws FacebookSDKException
*/
public function validateUserId($userId)
{
if ($this->getUserId() !== $userId) {
throw new FacebookSDKException('Access token metadata contains unexpected user ID.', 401);
}
}
/**
* Ensures the access token has not expired yet.
*
* @throws FacebookSDKException
*/
public function validateExpiration()
{
if (!$this->getExpiresAt() instanceof \DateTime) {
return;
}
if ($this->getExpiresAt()->getTimestamp() < time()) {
throw new FacebookSDKException('Inspection of access token metadata shows that the access token has expired.', 401);
}
}
/**
* Converts a unix timestamp into a DateTime entity.
*
* @param int $timestamp
*
* @return \DateTime
*/
private function convertTimestampToDateTime($timestamp)
{
$dt = new \DateTime();
$dt->setTimestamp($timestamp);
return $dt;
}
/**
* Casts the unix timestamps as DateTime entities.
*/
private function castTimestampsToDateTime()
{
foreach (static::$dateProperties as $key) {
if (isset($this->metadata[$key])) {
$this->metadata[$key] = $this->convertTimestampToDateTime($this->metadata[$key]);
}
}
}
}

View File

@@ -0,0 +1,292 @@
<?php
/**
* Copyright 2014 Facebook, Inc.
*
* You are hereby granted a non-exclusive, worldwide, royalty-free license to
* use, copy, modify, and distribute this software in source code or binary
* form for use in connection with the web services and APIs provided by
* Facebook.
*
* As with any software that integrates with the Facebook platform, your use
* of this software is subject to the Facebook Developer Principles and
* Policies [http://developers.facebook.com/policy/]. This copyright notice
* shall be included in all copies or substantial portions of the software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
namespace Facebook\Authentication;
use Facebook\Facebook;
use Facebook\FacebookApp;
use Facebook\FacebookRequest;
use Facebook\FacebookResponse;
use Facebook\FacebookClient;
use Facebook\Exceptions\FacebookResponseException;
use Facebook\Exceptions\FacebookSDKException;
/**
* Class OAuth2Client
*
* @package Facebook
*/
class OAuth2Client
{
/**
* @const string The base authorization URL.
*/
const BASE_AUTHORIZATION_URL = 'https://www.facebook.com';
/**
* The FacebookApp entity.
*
* @var FacebookApp
*/
protected $app;
/**
* The Facebook client.
*
* @var FacebookClient
*/
protected $client;
/**
* The version of the Graph API to use.
*
* @var string
*/
protected $graphVersion;
/**
* The last request sent to Graph.
*
* @var FacebookRequest|null
*/
protected $lastRequest;
/**
* @param FacebookApp $app
* @param FacebookClient $client
* @param string|null $graphVersion The version of the Graph API to use.
*/
public function __construct(FacebookApp $app, FacebookClient $client, $graphVersion = null)
{
$this->app = $app;
$this->client = $client;
$this->graphVersion = $graphVersion ?: Facebook::DEFAULT_GRAPH_VERSION;
}
/**
* Returns the last FacebookRequest that was sent.
* Useful for debugging and testing.
*
* @return FacebookRequest|null
*/
public function getLastRequest()
{
return $this->lastRequest;
}
/**
* Get the metadata associated with the access token.
*
* @param AccessToken|string $accessToken The access token to debug.
*
* @return AccessTokenMetadata
*/
public function debugToken($accessToken)
{
$accessToken = $accessToken instanceof AccessToken ? $accessToken->getValue() : $accessToken;
$params = ['input_token' => $accessToken];
$this->lastRequest = new FacebookRequest(
$this->app,
$this->app->getAccessToken(),
'GET',
'/debug_token',
$params,
null,
$this->graphVersion
);
$response = $this->client->sendRequest($this->lastRequest);
$metadata = $response->getDecodedBody();
return new AccessTokenMetadata($metadata);
}
/**
* Generates an authorization URL to begin the process of authenticating a user.
*
* @param string $redirectUrl The callback URL to redirect to.
* @param array $scope An array of permissions to request.
* @param string $state The CSPRNG-generated CSRF value.
* @param array $params An array of parameters to generate URL.
* @param string $separator The separator to use in http_build_query().
*
* @return string
*/
public function getAuthorizationUrl($redirectUrl, $state, array $scope = [], array $params = [], $separator = '&')
{
$params += [
'client_id' => $this->app->getId(),
'state' => $state,
'response_type' => 'code',
'sdk' => 'php-sdk-' . Facebook::VERSION,
'redirect_uri' => $redirectUrl,
'scope' => implode(',', $scope)
];
return static::BASE_AUTHORIZATION_URL . '/' . $this->graphVersion . '/dialog/oauth?' . http_build_query($params, null, $separator);
}
/**
* Get a valid access token from a code.
*
* @param string $code
* @param string $redirectUri
*
* @return AccessToken
*
* @throws FacebookSDKException
*/
public function getAccessTokenFromCode($code, $redirectUri = '')
{
$params = [
'code' => $code,
'redirect_uri' => $redirectUri,
];
return $this->requestAnAccessToken($params);
}
/**
* Exchanges a short-lived access token with a long-lived access token.
*
* @param AccessToken|string $accessToken
*
* @return AccessToken
*
* @throws FacebookSDKException
*/
public function getLongLivedAccessToken($accessToken)
{
$accessToken = $accessToken instanceof AccessToken ? $accessToken->getValue() : $accessToken;
$params = [
'grant_type' => 'fb_exchange_token',
'fb_exchange_token' => $accessToken,
];
return $this->requestAnAccessToken($params);
}
/**
* Get a valid code from an access token.
*
* @param AccessToken|string $accessToken
* @param string $redirectUri
*
* @return AccessToken
*
* @throws FacebookSDKException
*/
public function getCodeFromLongLivedAccessToken($accessToken, $redirectUri = '')
{
$params = [
'redirect_uri' => $redirectUri,
];
$response = $this->sendRequestWithClientParams('/oauth/client_code', $params, $accessToken);
$data = $response->getDecodedBody();
if (!isset($data['code'])) {
throw new FacebookSDKException('Code was not returned from Graph.', 401);
}
return $data['code'];
}
/**
* Send a request to the OAuth endpoint.
*
* @param array $params
*
* @return AccessToken
*
* @throws FacebookSDKException
*/
protected function requestAnAccessToken(array $params)
{
$response = $this->sendRequestWithClientParams('/oauth/access_token', $params);
$data = $response->getDecodedBody();
if (!isset($data['access_token'])) {
throw new FacebookSDKException('Access token was not returned from Graph.', 401);
}
// Graph returns two different key names for expiration time
// on the same endpoint. Doh! :/
$expiresAt = 0;
if (isset($data['expires'])) {
// For exchanging a short lived token with a long lived token.
// The expiration time in seconds will be returned as "expires".
$expiresAt = time() + $data['expires'];
} elseif (isset($data['expires_in'])) {
// For exchanging a code for a short lived access token.
// The expiration time in seconds will be returned as "expires_in".
// See: https://developers.facebook.com/docs/facebook-login/access-tokens#long-via-code
$expiresAt = time() + $data['expires_in'];
}
return new AccessToken($data['access_token'], $expiresAt);
}
/**
* Send a request to Graph with an app access token.
*
* @param string $endpoint
* @param array $params
* @param string|null $accessToken
*
* @return FacebookResponse
*
* @throws FacebookResponseException
*/
protected function sendRequestWithClientParams($endpoint, array $params, $accessToken = null)
{
$params += $this->getClientParams();
$accessToken = $accessToken ?: $this->app->getAccessToken();
$this->lastRequest = new FacebookRequest(
$this->app,
$accessToken,
'GET',
$endpoint,
$params,
null,
$this->graphVersion
);
return $this->client->sendRequest($this->lastRequest);
}
/**
* Returns the client_* params for OAuth requests.
*
* @return array
*/
protected function getClientParams()
{
return [
'client_id' => $this->app->getId(),
'client_secret' => $this->app->getSecret(),
];
}
}