[Node.js] 使用 Passport 套件串接第三方登入完整心法

許多人應該跟我一樣,進入一個新的網站發現要註冊會員,卻沒有提供第三方登入,如果不是真的很需要就按上一頁離開了,註冊新帳號真的很煩人(特別是奇怪的密碼複雜度設定),如果能為網站加上第三方登入就有機會留住更多的訪客。

實作方式

第三方登入可以在前、後端實作,但有登入需求通常表示需要儲存一些用戶資訊,結合後端實作會是比較理想的狀況,前端實作方式只有少數主流平台提供(例如:Google、Facebook),前端實作方式在各平台間沒有太多共通性,就是照著官方文件做就好,本篇文章會介紹後端實作方式及幾個社群平台的申請方式(Google、Facebook、Github、Discord)。


第三方登入流程

這張圖是經過幾次實作後才總結出的流程(前端為 CSR),一開始看也許會看不太懂,但是先大致有個概念,做過一次後再回來看會更加清楚做了什麼事情。
第三方登入流程


是敵是友?

實作第三方登入時會因為各種奇怪問題卡關,以下先提供一點心理建設。

盟友

  • Passport:後端串第三方登入的好朋友,可以大幅省去自幹程式碼的時間。

敵人

  • 第三方登入平台的申請流程:申請設定的流程可以說是越大咖越難搞,特別是 F 什麼、G 什麼的,因為提供的服務種類多元,還有安全性的要求,申請流程會複雜許多。
  • scope:scope 是使用第三方登入時設定要取得哪些用戶的資訊,有些登入服務有完整的說明文件,有些藏很深不知道去哪找,當設定錯誤的 scope 時登入就會失敗,這時會誤以為是第三方平台上的設定錯誤,查了半天才發現是 scope 設定錯誤。

亦敵亦友

  • 各種網路教學文章:第三方登入服務平台介面有時會改版,特別是越大間的越常改(沒錯,又是 F、G…),而且不只是介面改了,scope 也可能會改,這種時候照著教學文章貼肯定會卡住,搜尋教學文時需要注意一下發文時間最好在一年內。

實作 - 後端環境、路由建置

★ 環境:Node.js
★ 使用套件:Express、Passport(請先在後端環境完成安裝)

  1. 先想好自己要串接的社群平台,前往 Passport 搜尋對應的策略(Strategy),Passport 提供超過 500 種策略,基本上想的到的都會有。
  2. 搜尋到策略後點擊進入,盡可能選下載量和星星數最高的,使用起來比較有保障。
  3. 進入策略頁面後請先閱讀第一段落,這邊會提供許多實用的建議,以下圖(Discord)為例,作者說明他正在尋找接班人繼續維護這個策略(目前可能停更),另外提供了兩個連結,第二個連結就是 Discord 申請第三方登入的官方文件,這個連結對後續申請服務幫助很大。
  4. 閱讀完第一段收集到有用資訊後先不急著申請,照著第二段(Usage) 建立好自己的 Node.js 專案環境:
    • 使用 NPM 安裝策略套件。
    • 新增一個檔案 passport.js(名稱自訂),並且在專案主程式(express 是 app.js)中引入這個檔案。
    • 複製 Configure Strategy 內的程式碼到 passport.js 中。
    • 如果要實作多種第三方登入可以貼到同一個檔案內,不需要另外新增檔案來引入。
  5. 上個步驟貼上的程式碼有幾個地方需要調整
    • clientID、clientSecret:需要到第三方登入平台申請完之後才會取得,建議改成引用環境變數,不要直接將敏感資訊暴露在程式碼中。
    • callbackURL:第三方登入認證後會將使用者資訊結合這個網址送出,我們可以先設計好這個路由名稱,例如:http://127.0.0.1:3000/auth/discord/callback(佈署上線後要將 127.0.0.1:3000 改為線上的域名)。
    • scopes:設定第三方回傳的使用者資訊,建議可以先照貼策略裡面寫的,等串好沒問題之後再調整,也不建議參考時間較舊的網路教學文章來設定,很可能會因為無效的 scope 造成登入失敗。
    • function:這個函式是接收到第三方登入回傳資料後要做的動作,裡面有 4 個參數,其中的 profile 是回傳的資料,範例程式碼中使用 findOrCreate 來取得/建立使用者資料,這段可以移除,直接撰寫return cb(null, profile) 進入下一個 callback function 來自訂處理程式,修改後的 function 如下:
      1
      2
      3
      function(accessToken, refreshToken, profile, cb) {
      return cb(null, profile);
      }
  6. 進入第三段(Authentication Requests),在後端專案中加入兩個路由,第一個是讓前端傳送使用第三方登入的請求,第二個是前面說到的 callbackURL。
  7. callback 路由的範例程式碼中,有設定失敗回傳轉址行為,這部份可以拿掉,交由自己撰寫的 callback function 處理,除此之外,如果後端伺服器不使用 session 儲存資訊,可以在這邊加入設定來關閉,程式碼如下:
    1
    2
    3
    4
    app.get('/auth/discord', passport.authenticate('discord'));
    app.get('/auth/discord/callback', passport.authenticate('discord', {
    session: false,
    }), callbackFunction);
  8. 後端環境基本設置到此已完成,策略頁面中剩下的其他段落是針對不同使用情境設定,可以在串接成功後再視需求閱讀參考。

實作 - 申請第三方登入服務

關鍵字

每個平台的操作介面都不同,但是需要設定和取得的資訊幾乎都能在以下這些關鍵字中找到:

  • OAuth、OAuth 2、OAuth 2.0
  • ClientID、AppID
  • ClientSecret、AppSecret
  • Callback URL、Callback URI、Redirect

因為操作介面可能會改來改去,以下流程不會有太多圖片說明,而是提供「按鈕名稱」,可以在操作面板上尋找這些按鈕關鍵字。

Google

Google 最多人串,也最麻煩,步驟非常多:

  1. 前往 Google Cloud Platform
  2. 點擊按鈕「新增專案」(按鈕位置要找一下)。
  3. 輸入「專案名稱」(可以用預設)後點擊按鈕「建立」,稍等一下就會完成新專案的建立。
  4. 專案建立好後會跳轉到操作儀表板,左側選單項目非常的多,需要選擇「API 和服務」,如果找不到也可以直接在上方的搜尋列搜尋「API」就可以到設定頁面。
  5. 進入 API 和服務頁面後,左側選單選擇「OAuth 同意畫面」。
  6. 右側介面選擇「外部」,再點擊按鈕「建立」。
  7. 把必填的欄位填好,應用程式名稱(網站名稱)、聯絡信箱…等填完後就可以點擊「儲存並繼續」。
  8. 接著是設定授權範圍的頁面(也就是前面提到的 scopes),可以設定用戶點擊第三方登入時需要提供哪些資訊的授權,這邊可以不做設定,直接點擊「儲存並繼續」。
  9. 最後是測試者的設定頁面,請把自己的信箱加進去,就可以點擊「儲存並繼續」,同意畫面的設定到此完成。
  10. 接著在左側選單選擇「憑證」,右側介面點擊按鈕「+建立憑證 > OAuth 用戶端 ID」。
  11. 「應用程式類型」選擇「網頁應用程式」。
  12. 「名稱」可以改成自己網站名稱。
  13. 「已授權的重新導向 URI」填入在後端設定好的 callback url。
  14. 完成設定後點擊按鈕「建立」,建立完成後就會彈出畫面,上面有 ClientID、ClientSecret(介面上是寫 您的用戶端 ID 和 您的用戶端密碼)
  15. 完成啟用後將 ClientID、ClientSecret 設定到後端專案後就可以測試成果了。

Facebook

Facebook 改名為 Meta,開發者介面也改變了,一些老文章的圖片無法參考,除此之外,Facebook 的 callback url 只允許設定 https 協定,所以要在本地測試的話得掛上 https(可以參考 Mkcert - 在 localhost 掛 HTTPS 神器),以下是 Facebook 應用設定流程:

  1. 前往 Meta for Developers
  2. 點擊右上角的按鈕「Create App」,建立新的應用程式。
  3. 選取應用程式類型(Select an app type),不同類型能取得的使用者資訊會不一樣,自己決定要用哪個類型後點擊按鈕「next」進入下一步。
  4. 在「Display name」輸入名稱(網站名稱)後就可以點擊按鈕「Create app」完成建置,這時候會需要再次輸入自己 Facebook 的密碼,成功後就會跳轉到應用的介面。
  5. 左側選單選擇「Settings > Basic」就可以拿到 ClientID、ClientSecret(介面上是寫 AppID 和 App secret)。
  6. 左側選單選擇「Dashboard」。
  7. 右側介面找到「Facebook Login」的區塊,點擊按鈕「Set up」,就會跳轉到設定頁面(此時左側選單也會多出 Facebook Login 的選項)。
  8. 左側選單選擇「Facebook Login > Settings」。
  9. 在設定介面的「Valid OAuth Redirect URIs」中加入後端設定好的 callback url,完成後點擊右下方的按鈕「Save changes」。
  10. 如果介面上方有「App Mode」按鈕,切換成 Live 狀態,如果無法切換可能是有一些基本資訊要補充設定,設定好就可以啟用了,我只有在新建的第一個 app 有出現這個開關,後續新增的都沒有。
  11. 完成啟用後將 ClientID、ClientSecret 設定到後端專案後就可以測試成果了。

Github

Github 的設定超簡單,幾個步驟迅速搞定:

  1. 前往 Github 的個人設定頁面 Settings / Developer settings
  2. 左邊選單選取「OAuth Apps」。
  3. 右方區塊點擊「New OAuth App」,建立新的應用程式。
  4. 「Application name」填入應用名稱(網站名稱)、「Homepage URL」填入網站網址、「Authorization callback URL」填入在後端設定好的 callback url。
  5. 點擊「Register application」。
  6. 建置好之後進入剛剛設置的 App 就可以拿到 ClientID、ClientSecret。
  7. 將 ClientID、ClientSecret 設定到後端專案後就可以測試成果了。

Discord

Discord 的步驟也很簡單,還有機器人的功能,有閒可以玩玩看:

  1. 前往 Discord Developer Portal
  2. 點擊右上方「New Application」按鈕,建立新的應用程式。
  3. 輸入名稱(網站名稱)後,點擊「Create」建置應用後就會跳轉到應用的介面。
  4. 左側選單選擇「OAuth2」。
  5. 右側區塊會看到 ClientID,但是 ClientSecret 區塊是空的,點擊「Reset Secret」按鈕就會產生新的 ClientSecret。
  6. 右側區塊點擊「Add Redirect」,加入在後端設定好的 callback url。
  7. 偵測到設定變更之後下方會談出提醒對話框,點擊「Save Changes」儲存設定。
  8. 將 ClientID、ClientSecret 設定到後端專案後就可以測試成果了。
  9. Discord 的 官方文件 中可以查看有哪些 scopes 可以取得。

登入測試

測試的方法很簡單,後端設置步驟中我們已經設定了兩個路由:

1
2
3
4
app.get('/auth/discord', passport.authenticate('discord'));
app.get('/auth/discord/callback', passport.authenticate('discord', {
session: false,
}), callbackFunction);

可以先簡單撰寫 callbackFunction,測試階段我們只要能成功看到回傳成果就好,大致如下:

1
2
3
4
5
6
7
8
9
10
const callbackFunction = (req, res, next) => {
// Passport 會把回傳資訊放到 req.user
const user = req.user;

// 在後端 console 查看結果
console.log(user);

// 前端查看結果
res.send({user});
}

接著開啟瀏覽器,輸入第一個路由,以本地為例,網址是 http://localhost:3000/auth/discord(自行調整),成功的話會跳轉到登入授權頁面。

完成授權後,查看後端 console 和瀏覽器跳轉畫面有沒有出現使用者資訊,有的話就是成功了!