<?php
/**
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
require_once "base_facebook.php";
/**
* Extends the BaseFacebook class with the intent of using
* PHP sessions to store user ids and access tokens.
*/
class FacebookCustom extends BaseFacebook
{
/**
* Cookie prefix
*/
const FBSS_COOKIE_NAME = 'fbss';
/**
* We can set this to a high number because the main session
* expiration will trump this.
*/
const FBSS_COOKIE_EXPIRE = 31556926; // 1 year
/**
* Stores the shared session ID if one is set.
*
* @var string
*/
protected $sharedSessionID;
/**
* Identical to the parent constructor, except that
* we start a PHP session to store the user ID and
* access token if during the course of execution
* we discover them.
*
* @param array $config the application configuration. Additionally
* accepts "sharedSession" as a boolean to turn on a secondary
* cookie for environments with a shared session (that is, your app
* shares the domain with other apps).
*
* @see BaseFacebook::__construct
*/
public function __construct( $config )
{
if ( ( function_exists( 'session_status' ) && session_status() !== PHP_SESSION_ACTIVE ) || !session_id() ) {
session_start();
}
parent::__construct( $config );
if ( !empty( $config[ 'sharedSession' ] ) ) {
$this->initSharedSession();
// re-load the persisted state, since parent
// attempted to read out of non-shared cookie
$state = $this->getPersistentData( 'state' );
if ( !empty( $state ) ) {
$this->state = $state;
} else {
$this->state = null;
}
}
}
/**
* Supported keys for persistent data
*
* @var array
*/
protected static $kSupportedKeys = array( 'state', 'code', 'access_token', 'user_id' );
/**
* Initiates Shared Session
*/
protected function initSharedSession()
{
$cookie_name = $this->getSharedSessionCookieName();
if ( isset( $_COOKIE[ $cookie_name ] ) ) {
$data = $this->parseSignedRequest( $_COOKIE[ $cookie_name ] );
if ( $data && !empty( $data[ 'domain' ] ) && self::isAllowedDomain( $this->getHttpHost(), $data[ 'domain' ] ) ) {
// good case
$this->sharedSessionID = $data[ 'id' ];
return;
}
// ignoring potentially unreachable data
}
// evil/corrupt/missing case
$base_domain = $this->getBaseDomain();
$this->sharedSessionID = md5( uniqid( mt_rand(), true ) );
$cookie_value = $this->makeSignedRequest( array(
'domain' => $base_domain,
'id' => $this->sharedSessionID
) );
$_COOKIE[ $cookie_name ] = $cookie_value;
if ( !headers_sent() ) {
$expire = time() + self::FBSS_COOKIE_EXPIRE;
setcookie( $cookie_name, $cookie_value, $expire, '/', '.' . $base_domain );
} else {
// @codeCoverageIgnoreStart
self::errorLog( 'Shared session ID cookie could not be set! You must ensure you ' . 'create the Facebook instance before headers have been sent. This ' . 'will cause authentication issues after the first request.' );
// @codeCoverageIgnoreEnd
}
}
/**
* Provides the implementations of the inherited abstract
* methods. The implementation uses PHP sessions to maintain
* a store for authorization codes, user ids, CSRF states, and
* access tokens.
*/
/**
* {@inheritdoc}
*
* @see BaseFacebook::setPersistentData()
*/
protected function setPersistentData( $key, $value )
{
if ( !in_array( $key, self::$kSupportedKeys ) ) {
self::errorLog( 'Unsupported key passed to setPersistentData.' );
return;
}
$session_var_name = $this->constructSessionVariableName( $key );
$_SESSION[ $session_var_name ] = $value;
}
/**
* {@inheritdoc}
*
* @see BaseFacebook::getPersistentData()
*/
protected function getPersistentData( $key, $default = false )
{
if ( !in_array( $key, self::$kSupportedKeys ) ) {
self::errorLog( 'Unsupported key passed to getPersistentData.' );
return $default;
}
$session_var_name = $this->constructSessionVariableName( $key );
return isset( $_SESSION[ $session_var_name ] ) ? $_SESSION[ $session_var_name ] : $default;
}
/**
* {@inheritdoc}
*
* @see BaseFacebook::clearPersistentData()
*/
protected function clearPersistentData( $key )
{
if ( !in_array( $key, self::$kSupportedKeys ) ) {
self::errorLog( 'Unsupported key passed to clearPersistentData.' );
return;
}
$session_var_name = $this->constructSessionVariableName( $key );
if ( isset( $_SESSION[ $session_var_name ] ) ) {
unset( $_SESSION[ $session_var_name ] );
}
}
/**
* {@inheritdoc}
*
* @see BaseFacebook::clearAllPersistentData()
*/
protected function clearAllPersistentData()
{
foreach ( self::$kSupportedKeys as $key ) {
$this->clearPersistentData( $key );
}
if ( $this->sharedSessionID ) {
$this->deleteSharedSessionCookie();
}
}
/**
* Deletes Shared session cookie
*/
protected function deleteSharedSessionCookie()
{
$cookie_name = $this->getSharedSessionCookieName();
unset( $_COOKIE[ $cookie_name ] );
$base_domain = $this->getBaseDomain();
setcookie( $cookie_name, '', 1, '/', '.' . $base_domain );
}
/**
* Returns the Shared session cookie name
*
* @return string The Shared session cookie name
*/
protected function getSharedSessionCookieName()
{
return self::FBSS_COOKIE_NAME . '_' . $this->getAppId();
}
/**
* Constructs and returns the name of the session key.
*
* @see setPersistentData()
* @param string $key The key for which the session variable name to construct.
*
* @return string The name of the session key.
*/
protected function constructSessionVariableName( $key )
{
$parts = array(
'fb',
$this->getAppId(),
$key
);
if ( $this->sharedSessionID ) {
array_unshift( $parts, $this->sharedSessionID );
}
return implode( '_', $parts );
}
}
?>