iT邦幫忙

2024 iThome 鐵人賽

DAY 28
0

Discourse 內建有 SSO 登入功能取名叫做 Discourse connect,要打開這個功能的話在 admin 頁面裡找到 enable discourse connect 功能並啟用。

https://res.cloudinary.com/dhcsjvhjg/image/upload/v1728736211/Screenshot_2024-05-07_at_9.39.55_AM_eqsnxz.png

Discourse connect 需要實作一個 discourse connect url 提供驗證,當嘗試登入時 Discourse 會開啟該 url 並帶有 ssosig 參數,然後經由以下步驟驗證登入。

  • 驗證簽名:確保使用 discourse_connect_secret 作為密鑰對 PAYLOAD 進行 HMAC-SHA256 的運算結果等於 sigsig 會是十六進制編碼的)。
  • 利用 sso 解密後的參數執行所需的任何身份驗證。
  • 建立一組包含至少 nonceemailexternal_id 的參數。你也可以提供一些額外的資料,以下是 Discourse 可以識別的所有鍵的列表:
    • nonce 應從輸入參數中複製。
    • email 必須是已驗證的電子郵件地址。如果電子郵件地址尚未驗證,請將 require_activation 設置為 "true"。
    • external_id 是任何不會改變的唯一識別碼,即使用戶的電子郵件、名稱等改變。建議的值是資料庫中的 'id' 行號。
    • username 將成為用戶在 Discourse 上的使用者名稱,若用戶是新用戶或者 SiteSetting.auth_overrides_username 被設置。
    • name 將成為用戶在 Discourse 上的全名,若用戶是新用戶或者 SiteSetting.auth_overrides_name 被設置。
    • avatar_url 將被下載並設為用戶的頭像,若用戶是新用戶或者 SiteSetting.discourse_connect_overrides_avatar 被設置。
    • avatar_force_update 是一個布林值。如果設為 true,將強制 Discourse 更新用戶的頭像,無論 avatar_url 是否已更改。
    • bio 將成為用戶的個人簡介內容,若用戶是新用戶、簡介為空或者 SiteSetting.discourse_connect_overrides_bio 被設置。
    • 額外的布林值("true" 或 "false")包括:adminmoderatorsuppress_welcome_message
  • 將上記參數進行 Base64 編碼。
  • 使用 discourse_connect_secret 作為密鑰,對 Base64 編碼的參數進行 HMAC-SHA256 的運算,輸出字串。
  • 重新導向帶有 ssosig 參數的 return_sso_url(例如:http://discourse_site/session/sso_login?sso=payload&sig=sig)。

上記步驟用 Typescript 實作的話會像這樣:

import crypto from 'crypto';
import { get } from 'lodash';
import qs from 'qs';

const sso =
  '{sso from discourse}'; //payload
const sig = '{sig from discourse}';
const discourse_connect_secret = '{the secret code}';

// decode the base64 encoded string
const decoded = Buffer.from(sso, 'base64').toString('utf-8');
console.log(decoded);

const params = qs.parse(decoded);
console.log(params);

const returnUrl = get(params, 'return_sso_url');

// Validate the signature: ensure that HMAC-SHA256 of PAYLOAD (using discourse_connect_secret, as the key) is equal to the sig (sig will be hex encoded).
const checkSig = crypto
  .createHmac('sha256', discourse_connect_secret)
  .update(sso)
  .digest('hex');

console.log(checkSig === sig);

const authPayload = {
  nonce: params.nonce,
  email: 'test@example.com',
  external_id: 1,
};

// Base64 encode payload
const returnSso = Buffer.from(qs.stringify(authPayload)).toString('base64');

// Calculate a HMAC-SHA256 hash of the returnSso using discourse_connect_secret as the key and Base64 encoded payload as text
const returnSig = crypto
  .createHmac('sha256', discourse_connect_secret)
  .update(returnSso)
  .digest('hex');

console.log({
  sso: returnSso,
  sig: returnSig,
});

// Get return url
console.log(returnUrl + '?' + qs.stringify({ sso: returnSso, sig: returnSig }));

使用這個功能,就能在主應用跟 Discourse 間同步用戶頭像跟名稱等資訊。

另外如果想在主應用登出時同步登出 Discourse 帳號,就要經由 Discourse API

步驟:

直接經由伺服器呼叫 Discourse API 會需要 api key,在 admin → API keys 頁面生成。

  1. 用主應用的用戶 id 查詢 Discourse 的用戶 id (對照上面的 external_id) :

    GET {discourse_base_url}/u/by-external/:id.json
    

    Respond:

    {
    	...
    	user : {
    		 id: 3
    	}
    }
    
  2. 登出 Discourse 用戶

    POST {discourse_base_url}/admin/users/:id/log_out.json
    

    Respond:

    {
        "success": "OK"
    }
    

上一篇
社群功能 Discourse
下一篇
Discourse 客製化
系列文
Awesome self hosted 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言