1<?php 2/** 3 * Copyright 2011 Facebook, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); you may 6 * not use this file except in compliance with the License. You may obtain 7 * a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 * License for the specific language governing permissions and limitations 15 * under the License. 16 */ 17 18require_once "base_facebook.php"; 19 20/** 21 * Extends the BaseFacebook class with the intent of using 22 * PHP sessions to store user ids and access tokens. 23 */ 24class Facebook extends BaseFacebook 25{ 26 const FBSS_COOKIE_NAME = 'fbss'; 27 28 // We can set this to a high number because the main session 29 // expiration will trump this. 30 const FBSS_COOKIE_EXPIRE = 31556926; // 1 year 31 32 // Stores the shared session ID if one is set. 33 protected $sharedSessionID; 34 35 /** 36 * Identical to the parent constructor, except that 37 * we start a PHP session to store the user ID and 38 * access token if during the course of execution 39 * we discover them. 40 * 41 * @param Array $config the application configuration. Additionally 42 * accepts "sharedSession" as a boolean to turn on a secondary 43 * cookie for environments with a shared session (that is, your app 44 * shares the domain with other apps). 45 * @see BaseFacebook::__construct in facebook.php 46 */ 47 public function __construct($config) { 48 if (!session_id()) { 49 session_start(); 50 } 51 parent::__construct($config); 52 if (!empty($config['sharedSession'])) { 53 $this->initSharedSession(); 54 } 55 } 56 57 protected static $kSupportedKeys = 58 array('state', 'code', 'access_token', 'user_id'); 59 60 protected function initSharedSession() { 61 $cookie_name = $this->getSharedSessionCookieName(); 62 if (isset($_COOKIE[$cookie_name])) { 63 $data = $this->parseSignedRequest($_COOKIE[$cookie_name]); 64 if ($data && !empty($data['domain']) && 65 self::isAllowedDomain($this->getHttpHost(), $data['domain'])) { 66 // good case 67 $this->sharedSessionID = $data['id']; 68 return; 69 } 70 // ignoring potentially unreachable data 71 } 72 // evil/corrupt/missing case 73 $base_domain = $this->getBaseDomain(); 74 $this->sharedSessionID = md5(uniqid(mt_rand(), true)); 75 $cookie_value = $this->makeSignedRequest( 76 array( 77 'domain' => $base_domain, 78 'id' => $this->sharedSessionID, 79 ) 80 ); 81 $_COOKIE[$cookie_name] = $cookie_value; 82 if (!headers_sent()) { 83 $expire = time() + self::FBSS_COOKIE_EXPIRE; 84 setcookie($cookie_name, $cookie_value, $expire, '/', '.'.$base_domain); 85 } else { 86 // @codeCoverageIgnoreStart 87 self::errorLog( 88 'Shared session ID cookie could not be set! You must ensure you '. 89 'create the Facebook instance before headers have been sent. This '. 90 'will cause authentication issues after the first request.' 91 ); 92 // @codeCoverageIgnoreEnd 93 } 94 } 95 96 /** 97 * Provides the implementations of the inherited abstract 98 * methods. The implementation uses PHP sessions to maintain 99 * a store for authorization codes, user ids, CSRF states, and 100 * access tokens. 101 */ 102 protected function setPersistentData($key, $value) { 103 if (!in_array($key, self::$kSupportedKeys)) { 104 self::errorLog('Unsupported key passed to setPersistentData.'); 105 return; 106 } 107 108 $session_var_name = $this->constructSessionVariableName($key); 109 $_SESSION[$session_var_name] = $value; 110 } 111 112 protected function getPersistentData($key, $default = false) { 113 if (!in_array($key, self::$kSupportedKeys)) { 114 self::errorLog('Unsupported key passed to getPersistentData.'); 115 return $default; 116 } 117 118 $session_var_name = $this->constructSessionVariableName($key); 119 return isset($_SESSION[$session_var_name]) ? 120 $_SESSION[$session_var_name] : $default; 121 } 122 123 protected function clearPersistentData($key) { 124 if (!in_array($key, self::$kSupportedKeys)) { 125 self::errorLog('Unsupported key passed to clearPersistentData.'); 126 return; 127 } 128 129 $session_var_name = $this->constructSessionVariableName($key); 130 unset($_SESSION[$session_var_name]); 131 } 132 133 protected function clearAllPersistentData() { 134 foreach (self::$kSupportedKeys as $key) { 135 $this->clearPersistentData($key); 136 } 137 if ($this->sharedSessionID) { 138 $this->deleteSharedSessionCookie(); 139 } 140 } 141 142 protected function deleteSharedSessionCookie() { 143 $cookie_name = $this->getSharedSessionCookieName(); 144 unset($_COOKIE[$cookie_name]); 145 $base_domain = $this->getBaseDomain(); 146 setcookie($cookie_name, '', 1, '/', '.'.$base_domain); 147 } 148 149 protected function getSharedSessionCookieName() { 150 return self::FBSS_COOKIE_NAME . '_' . $this->getAppId(); 151 } 152 153 protected function constructSessionVariableName($key) { 154 $parts = array('fb', $this->getAppId(), $key); 155 if ($this->sharedSessionID) { 156 array_unshift($parts, $this->sharedSessionID); 157 } 158 return implode('_', $parts); 159 } 160} 161