一般使用facebook PHP SDK做登入都是採用getLoginUrl函式取得連結(如官方此範例),認證完後會轉址回來並帶些facebook認證確認的state、code等get參數,在使用者體驗上非常的不好,甚至有可能在已帶有get參數的頁面上再轉址回來後會導致異常。

在此使用Facebook PHP SDK 3.0搭配javascript的方式,用popup window做認證。今年年初Facebook PHP SDK 3.0的更新中已有搭配javascript的範例,但有認證成功後無窮reload迴圈的問題,網路上普遍是IE發生問題,筆者本身是在Mac的chrome有此問題(問題出在下方的程式碼)。


FB.Event.subscribe('auth.login', function(response) {
 // do something with response
 login();
});

故我們得自己創建popup authorization window,流程如下:

  1. 使用者按下Facebook登入/認證按鈕
  2. javascript產生一個window並且將其導向facebook登入/認證的網址
  3. 認證完成後該popup window會導回所給予的redirect_uri參數網址
  4. 在此導回的網頁中,我們根據session內容確認已得到facebook認可,並在該網址給予一個get參數代表為認證成功後的轉址
  5. 當step 4.的facebook認證與get參數皆成立時,即close window並使main window更新頁面

前置作業

```

function curPageURL(){
$pageURL = 'http';
$pageURL .= "://";
$pageURL .= $_SERVER['HTTP_HOST'].$_SERVER["REQUEST_URI"];
return $pageURL;
}

$base = realpath(dirname(FILE));
include_once "$base/../fb_src/base_facebook.php";
include_once "$base/../fb_src/facebook.php";

$config['baseurl'] = curPageURL();

$facebook = new Facebook(array(
'appId' => '你的appId',
'secret' => '你的app secret',
'cookie' => true,
));
$user = $facebook->getUser();


利用curPageURL有個好處便是此部份程式碼可以用layouts的方式以require/include方式render在所有頁面都可以works。

註:getUser為3.0替代2.0中的getSession函式
<h2><span style="line-height: 22px; color: #3366ff;">產生facebook登入網址</span></h2>

if ($user) {
$logoutUrl = $facebook->getLogoutUrl(
array(
'redirect_uri' => $config['baseurl'],
)
);
} else {

// 解決baseurl已帶get參數問題
$appendSign = "?";
if (strpos($config['baseurl'], "?"))
{
$appendSign = "&";
}

$loginUrl = $facebook->getLoginUrl(
array(
'redirect_uri' => $config['baseurl'] . $appendSign . 'loginsucc=1', /loginsucc=1表示為認證成功轉址回來/
'scope' => 'publish_stream,email,user_about_me',
)
);
}


在redirect_uri部分我們給予一個get參數名叫loginsucc(亦可為其他名),用來告訴我們的php script是facebook認證後所轉址回來的。

但避免baseurl本身已帶有get參數,所以在appendSign部分判斷baseurl若有"?"則在redirect_uri的loginsucc以"&amp;"串接。

*註:redirect_uri在2.0為next、scrop在2.0為req_perms
<h2><span style="color: #3366ff;">認證成功後關閉popup window</span></h2>

//only if valid session found and loginsucc is set
if ($user && isset($_REQUEST['loginsucc'])){

echo "<script>
window.close();
window.opener.location.reload();
</script>";
}


當條件式成立後,代表此為認證成功後轉址回來的,所以將目前這個window關閉(認證視窗是由我們自行創建的),並告訴開啟這個視窗的parent(即main window)做頁面更新的動作(使我們的頁面重新抓取facebook session)。

*註:過去php sdk 2.0版本須自行把session內容存成cookie,但3.0在創建Facebook Instance時即可把cookie設為true,讓SDK幫我們建立
<h2><span style="color: #3366ff;">產生popup認證window</span></h2>

<?php if (!$user) { ?>
<script type="text/javascript">
var newwindow;
var intId;
function login(){
var screenX = typeof window.screenX != 'undefined' ? window.screenX : window.screenLeft,
screenY = typeof window.screenY != 'undefined' ? window.screenY : window.screenTop,
outerWidth = typeof window.outerWidth != 'undefined' ? window.outerWidth : document.body.clientWidth,
outerHeight = typeof window.outerHeight != 'undefined' ? window.outerHeight : (document.body.clientHeight - 22),
width = 500,
height = 270,
left = parseInt(screenX + ((outerWidth - width) / 2), 10),
top = parseInt(screenY + ((outerHeight - height) / 2.5), 10),
features = (
'width=' + width +
   ',height=' + height +
   ',left=' + left +
   ',top=' + top
);
newwindow=window.open('<?=$loginUrl?>','Login_by_facebook',features);

if (window.focus) {newwindow.focus()}

return false;
}
</script>

<?php } ?></pre>
<h2><span style="color: #3366ff;">facebook登入/登出按鈕html</span></h2>
<pre>

<?php if ($user) { ?>
<a href="<?=$logoutUrl?>"><img src="http://static.ak.fbcdn.net/rsrc.php/z2Y31/hash/cxrz4k7j.gif" border="0">
</a>
<?php } else{ ?>
<a href="#" onclick="login();return false;"><img src="http://static.ak.fbcdn.net/rsrc.php/zB6N8/hash/4li2k73z.gif" border="0">
</a>
<?php
}
?>


可參考網路上別人以sdk 2.0寫的<a href="http://thinkdiff.net/demo/newfbconnect1/fbpopup/index.php" target="_blank">demo</a>。

reference from: <a href="http://thinkdiff.net/facebook/create-facebook-popup-authentication-window-using-php-and-javascript/" target="_blank">Create Facebook PopUp Authentication Window using PHP and javascript</a>