X
    Categories: My_Note

Google、Facebook oAuth2 登入如何帶回傳的參數(state)

想要在網站使用 Google (Google+)、Facebook 的登入,在任何頁面登入,登入完成後,都要自動導向登入前看的頁面,常見的作法就是帶 from 的參數給 G / FB,然後 G / FB 再將參數帶回來即可。

不過這個作法在新版採用 Strict Mode 的情況下,只能在 G / FB 設定固定的網址,不能在產生網址時直接帶入,要怎麼解決呢?

Google、Facebook oAuth2 登入如何帶回傳的參數(state)

先說說研究的結論,state 的設定在下面文章都有寫,不過因為 Facebook SDK 需要使用 session,若 G /FB 都需要使用的話,建議要帶回來的值,都自己寫入 session 就好了。

Google'Facebook 底層都是走 oAuth 的標準,基本上都有下述這四個參數:(節錄自此篇:手動建立登入流程 - Facebook 登入)

https://www.facebook.com/v2.12/dialog/oauth?                                                                                                                            client_id={app-id}                                                                                                                                                           &redirect_uri={redirect-uri}                                                                                                                                                 &state={state-param}

此端點的必要參數如下:

  • client_id。位於應用程式主控板的應用程式編號。
  • redirect_uri。要將登入用戶重新導向到的網址。此網址會擷取「登入」對話方塊的回應。如果是在桌面版應用程式內的 WebView 中使用,這必須設為 https://www.facebook.com/connect/login_success.html。您可以在應用程式主控板中確認您為應用程式設定的網址正確無誤。在應用程式主控板左側的導覽功能表中,點擊產品下方的 Facebook 登入,然後點擊設定。在用戶端 OAuth 設定部分,驗證有效的 OAuth 重新導向 URI。
  • state。此為您應用程式建立來保持要求和回呼間狀態的字串值。此參數應該用來防止跨網站偽造要求,且會原封不動地傳回到您的重新導向 URI。

範例

https://www.facebook.com/v2.12/dialog/oauth?                                                                                                                            client_id={app-id}                                                                                                                                                           
&redirect_uri={"https://www.domain.com/login"}                                                                                                                               
&state={"{st=state123abc,ds=123456789}"}

從說明文件裡面有說到,state 本身是為了防止 CSRF 用得,不過另外是可以來帶我們需要的值,在登入完成 redirect 回來的參數裡面,會有 code 和 state 被帶回來。

  • 註:於產生出來點擊登入的連結,就可以看到 state 的參數值,可以先確認是否有塞成功。

先整理 Google 與 Facebook state 的設定方式

Google+ 與 Facebook 的文件都很少關於 state 設定的說明,所以在這邊順便把追 Code 的經過做點紀錄。

Facebook 設定 state 值的作法

  • $helper->getPersistentDataHandler()->set('state', $state); // 主要寫法是這樣
  • 建議寫法,部份參考 G+ state 文件
    <?php
    $s = sha1(openssl_random_pseudo_bytes(1024));
    $_SESSION['FBRLHstate'] = $s; // 這個變數自己隨意定義,之後接收回傳值自己驗證即可
    $helper->getPersistentDataHandler()->set('state', jsonencode(['state' => $s, 'from' => '/')); // 所以可以在此帶值進去
    ?>

    <?php $helper->getPersistentDataHandler()->set('state', jsonencode(['state' => $SESSION['FBRLH_state'], 'from' => '/')); ?>
  • 不過上述作法修改後,state 的驗證部份就得自己做,並且要注意 SDK 要怎麼處理

下述為 Facebook state 追 Code 的一些紀錄

  1. src/Facebook/Helpers/FacebookRedirectLoginHelper.php
    1. $state = $this->getState(); // $this->getInput('state'); = $GET['state']
    2. $savedState = $this->persistentDataHandler->get('state');
    3. $this->persistentDataHandler->set('state', null);
    4. // persistentDataHandler = class FacebookSessionPersistentDataHandler
  2. src/Facebook/PersistentData/FacebookSessionPersistentDataHandler.php
    • 猜想 state 大致寫法
    • use Facebook\PersistentData\FacebookSessionPersistentDataHandler;
    • use Facebook\PersistentData\PersistentDataInterface;
    • $fb->persistentDataHandler->set('state', '{"from": "/"}');
  3. src/Facebook/Helpers/FacebookRedirectLoginHelper.php
    1. $state = $this->persistentDataHandler->get('state') ?: $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRFLENGTH);
    2. $this->persistentDataHandler->set('state', $state);
    3. $SESSION[$this->sessionPrefix . $key] = $value; // 最後將值 set 到 session 去 ($SESSION['FBRLHstate'])
  4. src/Facebook/Helpers/FacebookRedirectLoginHelper.php # 247
  5. src/Facebook/polyfills.php
    1. function hash_equals($knownString, $userString)
    2. 直接對這個 return true; 應該就可以避開這個驗證,不過別亂用這種作法。 XD
  6. 註:Facebook SDK 裡面有參數會組合傳過去的 referer_url,然後去跟 Facebook 官方設定的參數做比對,不在上面的 code 裡面。

Google+ 設定 state 值的作法

  • Google 預設沒有設定 state,不過避免 CSRF,官方還是有建議範例,參考範例:
    <?php
    $s = sha1(openssl_random_pseudo_bytes(1024));
    $_SESSION['GPLUS_state'] = $s; // 這個變數自己隨意定義,之後接收回傳值自己驗證即可
    $gclient->setState(json_encode(['state' => $s, 'from' => '/']));
    ?>

下述為 Google state 追 Code 的一些紀錄

    1. src/Google/Client.php
      1. $this->getAuth()->setState($state);
      2. $this->getAuth()->authenticate($code, $crossClient);
      3. public function getAuth()
        $class = $this->config->getAuthClass();
        $this->auth = new $class($this);
    2. src/Google/Config.php
      'auth_class' => 'Google_Auth_OAuth2'
    3. src/Google/Auth/OAuth2.php # Google_Auth_OAuth2
      1. class Google_Auth_OAuth2 extends Google_Auth_Abstract
      2. public function setState($state) {
        $this->state = $state;
        }
      3. if (isset($this->state)) {
        $params['state'] = $this->state;
        }
      4. 產生 Login url (帶入 state)
    4. 找驗證部份
      1. if ($code && $gclient->authenticate($code)) {
      2. src/Google/Client.php
        public function authenticate($code, $crossClient = false) {
        return $this->getAuth()->authenticate($code, $crossClient);
        }
      3. $token = $gclient->getAccessToken();
        public function getAccessToken() {
        $token = $this->getAuth()->getAccessToken();
        }
      4. getAuth 是 Google_Auth_OAuth2 (src/Google/Auth/OAuth2.php) 的 getAuth()
        public function getAccessToken() {
        return json_encode($this->token);
        }
      5. => Google+ 使用 code、client_id、client_secret 驗證,不需要 state

Facebook SDK 設定 state 相關參考網頁

Google+ 設定 state 相關參考網頁

Tsung: 對新奇的事物都很有興趣, 喜歡簡單的東西, 過簡單的生活.
Related Post