Discourse 內建有 SSO 登入功能取名叫做 Discourse connect,要打開這個功能的話在 admin 頁面裡找到 enable discourse connect 功能並啟用。
Discourse connect 需要實作一個 discourse connect url
提供驗證,當嘗試登入時 Discourse 會開啟該 url 並帶有 sso
跟 sig
參數,然後經由以下步驟驗證登入。
discourse_connect_secret
作為密鑰對 PAYLOAD
進行 HMAC-SHA256 的運算結果等於 sig
(sig
會是十六進制編碼的)。sso
解密後的參數執行所需的任何身份驗證。SiteSetting.auth_overrides_username
被設置。SiteSetting.auth_overrides_name
被設置。SiteSetting.discourse_connect_overrides_avatar
被設置。avatar_url
是否已更改。SiteSetting.discourse_connect_overrides_bio
被設置。discourse_connect_secret
作為密鑰,對 Base64 編碼的參數進行 HMAC-SHA256 的運算,輸出字串。sso
和 sig
參數的 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 頁面生成。
用主應用的用戶 id 查詢 Discourse 的用戶 id (對照上面的 external_id) :
GET {discourse_base_url}/u/by-external/:id.json
Respond:
{
...
user : {
id: 3
}
}
登出 Discourse 用戶
POST {discourse_base_url}/admin/users/:id/log_out.json
Respond:
{
"success": "OK"
}