由於現在整合 Facebook Login API 時必須強制使用 HTTPS 以確保 Facebook 使用者的安全性,所以在本機(localhost) 整合 Facebook Login 時稍微會麻煩一些。

本文記錄如何在本機的 Flask 專案整合 Facebook login 。

本文環境

  • Python 3.6.5
  • macOS 10.14.3
  • Flask 1.0.2

申請 Facebook 應用程式

開始本文之前需先至 Facebook for developers 申請應用程式,以取得應用程式編號與應用程式密鑰。

目前整合網頁版的 Facebook login, 通常都會使用 Facebook 所提供的 Javascript 版的 SDK ,詳情請閱讀 搭配 JavaScript SDK 的網頁版「Facebook 登入」 ,故本文就不贅述如何撰寫 Facebook login 的 Javascript 程式碼,

設定有效的 OAuth 重新導向 URI

在網頁上設定好 Facebook login 的 Javascript 程式碼之後,還需要特別至 Facebook for developers 管理介面設定好 有效的 OAuth 重新導向 URI ,該 SDK 才能夠正常運作,這是由於 Facebook SDK 會自動偵測使用者的所在網址是否符合所設定的 URI 才能夠運作。

因為本文希望在本機環境載入 Facebook 登入頁時,由 Facebook 所提供的 Facebook SDK 時能夠正常運作,所以我們將 https://dev.localhost 設定為有效的 OAuth 重新導向 URI (當然各位也可以直接使用 https://localhost ,因為個人比較喜歡用 dev.localhost 區分開發環境,所以本文使用 dev.localhost 作為示範) 。

設定如下圖所示:

產生 dev.localhost 的 SSL 憑證

由於 HTTPS 需要 SSL 憑證,因此利用以下指令產生 dev.localhost 的憑證:

$ openssl req -x509 -out dev.localhost.crt -keyout dev.localhost.key \
  -newkey rsa:2048 -nodes -sha256 \
  -subj '/CN=dev.localhost' -extensions EXT -config <( \
   printf "[dn]\nCN=dev.localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:dev.localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

如果是單純想使用 localhost 的人可以使用以下指令:

$ openssl req -x509 -out localhost.crt -keyout localhost.key \
  -newkey rsa:2048 -nodes -sha256 \
  -subj '/CN=localhost' -extensions EXT -config <( \
   printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

p.s. CN 指的是 Common Name , 由 host name 加 domain name 所組成。

成功之後會有 2 個檔案產生( dev.localhost.crt & dev.localhost.key ),這就是建立 HTTPS 環境所需要的憑證與私鑰。

編輯 /etc/hosts

接著編輯 /ect/hosts 添加以下內容:

127.0.0.1   dev.localhost

上述設定的用意是將 dev.localhost 指向 IP 位址 127.0.0.1 ,因此當我們在瀏覽器輸入 https://dev.localhost 時,我們就會向本機發出 HTTP 請求。

p.s. 由於 /etc/hosts 原本就有 127.0.0.1 localhost 的設定,因此不需額外添加

架設 Flask server

設定完 Facebook 管理介面與產生 HTTPS 環境所需的憑證之後,最後就輪到 Flask 出場執行 Web sever 囉!

以下是本文的資料夾與檔案結構:

.
├── app/
│    ├── app.py
│    └── templates/
│         └── index.html
├── dev.localhost.crt
└── dev.localhost.key

app/ 資料夾內 app.py 是簡單的 Flask 程式,主要用來執行 Web server 並載入 templates/ 資料夾內的 index.html 以顯示 Facebook 登入頁給使用者。

app.py 內容如下:

# -*- coding: utf-8 -*-
from flask import Flask
from flask import render_template


def create_app(app_env=None):
    flask_app = Flask(__name__)
    return flask_app


app = create_app()


@app.route('/')
def index():
    return render_template('index.html')

index.html 內容如下(請將 your_app_id 替換成你的應用程式編號):

<html>
  <head>
    <title>Sign in</title>
  </head>
  <body>
    <script>

      (function(d, s, id){
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) {return;}
        js = d.createElement(s); js.id = id;
        js.src = "https://connect.facebook.net/en_US/sdk.js";
        fjs.parentNode.insertBefore(js, fjs);
      }(document, 'script', 'facebook-jssdk'));

      function statusChangeCallback(response) {
        console.log(response);
      }

      function checkLoginState() {
        FB.getLoginStatus(function(response) {
          statusChangeCallback(response);
        });
      }

      window.fbAsyncInit = function() {

        FB.init({
          appId      : 'your_app_id',
          cookie     : true,
          xfbml      : true,
          version    : 'v3.2'
        });

        FB.AppEvents.logPageView();

        FB.getLoginStatus(function(response) {
            statusChangeCallback(response);
        });

      };

    </script>

    <fb:login-button
      scope="public_profile,email"
      onlogin="checkLoginState();">
    </fb:login-button>
  </body>
</html>

準備好環境之後就能夠用以下指令執行支援 HTTPS 的 Flask server:

$ sudo env FLASK_APP=app.app flask run --cert=dev.localhost.crt --key=dev.localhost.key --port=443
Password:
 * Serving Flask app "app.app"
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on https://127.0.0.1:443/ (Press CTRL+C to quit)

執行成功後就能夠用瀏覽器打開 https://dev.localhost 看到 Facebook 的登入按鈕,並且能夠進行登入:

最後只要修改 index.html 內的 statusChangeCallback 函式,利用從 Facebook 拿到的 access token 呼叫 Facebook API 達成你所需要的目標。

以上就是在本機利用 Flask 整合 Facebook login 的過程! Happy Coding!

References

https://letsencrypt.org/docs/certificates-for-localhost/