MCC — 會員管理中心

多租戶 · 多服務 · 標準 OIDC
Google · LINE · Facebook · Apple · Microsoft · GitHub 第三方登入整合平台

📖什麼是 MCC?

MCC(Member Control Center) 是一套多租戶會員管理中心, 專為需要管理多個獨立組織、企業、單位或社群(統稱 Group)的平台營運者所設計。 每個 Group 代表一個獨立的租戶,旗下可建立多個 Service(應用程式或前台服務); 每個 Service 可自行啟用不同的第三方身分提供者(IdP),讓各自的使用者以 Google、LINE、Facebook、Apple、Microsoft 或 GitHub 帳號登入。

MCC 的核心職責是:代理 IdP 驗證流程、建立並管理會員身分、發放 Token、驗證 Session。 您的前後端只需與 MCC 對接,無需自行串接各 IdP 的 OAuth 細節。

⚠️ 會員身分以 Group 為隔離單位:相同 email 在不同 Group 下屬於完全獨立的會員,資料互不共用。

🚀 5 分鐘跑起來(路徑 A · 推薦)
  1. 【後台】 在 MCC Manager 建立 Service → 取得 MCC_CLIENT_IDMCC_CLIENT_SECRET,並把前端回調網址加入 Redirect 白名單。
  2. 【前端】 用任一 OIDC 函式庫(如 NextAuth)指向 MCC 的 discovery URL:{issuer}/.well-known/openid-configuration——換票 / session / refresh 全交給它自動處理。
  3. 【後端】 每次重要 API,用 GROUP_JWT + SERVICE_CODE 呼叫 POST /member/check 確認 session 有效,並以 id_token 的 sub 當帳號主鍵。
完整流程與程式碼範例請看 路徑 A 整合指南;既有專案要用傳統整合,請看 路徑 B 整合指南

核心概念

Group(群組)
一個獨立的組織 / 企業 / 單位。會員、服務、IdP 設定皆隸屬於 Group,跨 Group 資料完全隔離。
Service(服務)
Group 下的應用程式(網站、App、API)。每個 Service 有獨立的 client_id / secret,可啟用不同 IdP。
Member(會員)
隸屬於特定 Group 的使用者。首次透過 IdP 登入時自動建立。每位會員有穩定的 member_suuid(= id_token 的 sub),此為判斷「同一使用者」的唯一依據。
IdP(身分提供者)
Google、LINE、Facebook、Apple、Microsoft、GitHub。由 MCC 統一代理 OAuth / OIDC 流程,您的程式不需直接串接。
⚠️ 帳號主鍵一律使用 sub(= member_suuid),不要用 email
【給「建立 / 儲存使用者」的那一端,通常是後端】
  • sub 是會員的穩定全域唯一 ID,同一會員每次登入都不變 → 用它當你系統裡的帳號主鍵 / 外鍵。
  • sub(路徑 A:id_token)= member_suuid(路徑 B:exchange 回傳),是同一個值
  • email 一定有,但可能因 provider 而不同 → 不能拿來判斷「是不是同一個人」,請一律比對 sub
  • 同一個人若用不同 email 的第三方登入,會被視為不同會員(不同 sub)——這是設計上的預期行為。
❌  findUser({ where: { email } })     // 同一人可能被建成多個帳號
✅  findUser({ where: { mccSub } })    // 找不到才建立;email / name 只當「可更新屬性」
🧭 識別碼速查:哪些其實是同一個值
你會看到的名字實際是什麼用在哪
service_suuidMCC_CLIENT_IDclient_id同一個 UUID(服務的 suuid)前端啟動登入 / 後端 OAuth 換票
member_suuid = id_token 的 sub同一個值(會員唯一 ID)當你系統的帳號主鍵
GROUP_JWTSERVICE_CODE一組後端專用憑證(成對使用)後端呼叫 /member/check/auth/idp/exchange
💡 搞混這幾個是新手最常見的卡關點——先記住「同一列的名字=同一個東西」。

給整合工程師

本文件是前後端整合的主要參考。整合前請確認:

  1. 已在 MCC Manager 建立 Group 與 Service,並取得 GROUP_JWTSERVICE_CODEMCC_CLIENT_IDMCC_CLIENT_SECRET
  2. 已在 Service 的 Redirect URI 白名單中加入前端的回調頁網址
  3. 已在 Service 的 IdP 設定中啟用所需的第三方登入,並在各 IdP 開發者後台填入 MCC 的 Callback URL
  4. 選擇整合路徑:新專案建議 路徑 A(OIDC 標準),相容所有 OIDC 函式庫;既有專案可用路徑 B(傳統)
  5. 整合期建議:start URL 帶 response_mode=diag,登入失敗會直接在 MCC 端 render 完整錯誤頁(含修正建議),不會被前端框架吞成 generic error。詳見路徑 A 整合指南的「整合除錯模式」

🌐環境與 group 慣例

串接 MCC 一律使用正式網址https://members.managers.center

測試與正式不是用不同網址區分,而是用不同 group 區分:

用途怎麼做
測試 開一個「測試 group」,你所有產品都在這個 group 底下測試
正式 另開一個「正式 group

兩個 group 各自有獨立的 MCC_CLIENT_ID / MCC_CLIENT_SECRET / GROUP_JWT / SERVICE_CODE,資料與會員完全隔離;切換時只換這組憑證即可,網址不變

🧭 整合架構(請先閱讀)

路徑 A OIDC 標準(推薦)

MCC 為標準 OIDC Provider(RFC 6749 + OIDC Core 1.0)。NextAuth、oidc-client-ts、AppAuth 等任何 OIDC 函式庫皆可直接對接。

  • Discovery: /.well-known/openid-configuration
  • 授權: GET /oauth/authorize
  • 換票: POST /oauth/token(Basic auth)
  • client_id = ServiceClient.suuid(UUID,全域唯一)
  • Token 為 RS256,可用 JWKS 在客戶端本地驗簽
  • 支援 refresh_token 自動輪換(single-use rotation)
✅ 新專案優先選路徑 A;session 管理、token refresh、登出皆由 OIDC client 自動處理。
📘 完整流程與 NextAuth 程式碼範例 → 路徑 A 整合指南

路徑 B 傳統整合

前端直接呼叫 /auth/idp/{provider}/start,MCC 完成 IdP 驗證後帶一次性 code 導回前端,再由後端持 Group Bearer 兌換 accessToken。

  • 啟動: GET /auth/idp/{provider}/start
  • 兌換: POST /auth/idp/exchange(Group Bearer)
  • code 一次性,TTL 5 分鐘
  • 無 refresh token;accessToken 過期需重新登入
⚠️ 【既有專案才需要】新專案請直接用路徑 A,本節可略過。路徑 B 僅供無法使用 OIDC 函式庫的既有系統,與路徑 A 可同時並存。
📗 完整流程與程式碼範例 → 路徑 B 整合指南
🛠️ 整合卡關看不到錯誤?在 start URL 加 response_mode=diag, MCC 會直接顯示完整診斷頁(含上游 IdP 原始錯誤與修正建議)。僅整合期使用,正式環境一律 code。 詳見 路徑 A 整合指南

🔑 兩條路徑共同的後端保護模式

登入完成後,前端取得 member_suuidaccess_token【後端】每次呼叫重要 API 時,後端必須向 MCC 驗證會員 session 仍有效——MCC 是 session 的唯一真實來源。

1
前端 → 您的後端:攜帶 Authorization: Bearer {access_token}X-Member-Suuid: {member_suuid}
2
您的後端 → MCCPOST /member/check,帶上 GROUP_JWT + SERVICE_CODE + 會員憑證,確認 session 是否存活
3
MCC → 您的後端:回傳 { success, data: { member_suuid, email, display_name, ttl } }ttl 為 session 剩餘秒數,success: false 表示已登出或 token 無效
4
後端確認後執行業務邏輯,並將結果回傳前端
合約閘門:Manager 每次「產生 Group Bearer」都會更新 ServiceClient.expires_at。 合約到期後,POST /oauth/token(路徑 A)與 POST /member/check(兩條路徑) 分別回傳 invalid_grant / group_contract_expired,前端應呼叫 signOut

🚀 快速開始

在 Manager 建立服務並產生 Group Bearer

登入 MCC Manager,建立群組與服務後點「產生 Bearer Token」(同步更新 ServiceClient.expires_at 合約期)。

取得:SERVICE_CODEGROUP_JWT、合約有效期
設定白名單:將所有 redirect_uri 加入白名單(需完全匹配,含協定+域名+路徑)
OIDC 憑證(路徑 A 必須):到服務頁「重設 OAuth client_secret」一次取得 MCC_CLIENT_ID(= service.suuid,UUID)與 MCC_CLIENT_SECRET明文僅顯示一次,請立即存入環境變數

設定 IdP(第三方登入)

MCC 支援以下六種第三方登入。在 MCC Manager 服務 → IdP 設定頁面啟用並填入各 provider 的 Client ID 與 Secret。

支援的 IdP:🔵 Google 🟢 LINE 🔷 Facebook ⚫ Apple 🟦 Microsoft ⬛ GitHub

所有 provider 共用同一個 Callback URL,填入各 IdP 開發者後台:
https://members.managers.center/auth/callback

⚠️ Apple 使用 response_mode=form_post,callback 是 HTTP POST 而非 GET。在 Apple Developer 後台將此 URL 加入 Return URLs 時須選 POST。
不同 Group 下相同 email 屬不同會員(隔離設計)。

前端整合(擇一)

路徑 A(推薦):設定 NextAuth(或任何 OIDC client)指向 MCC discovery URL; OIDC client 自動處理 redirect / 換票 / session / refresh。

路徑 B:前端導向 /auth/idp/{provider}/start?service_suuid=...&redirect_uri=...&response_mode=code, 回調頁取得 code 後傳給後端兌換。

後端保護 API(所有重要路由必須執行)

【前端】呼叫後端時帶 access_token(路徑 A:Bearer token;路徑 B:從 session 取出)與 member_suuid【後端】執行業務邏輯前,以 Group Bearer 呼叫 POST /member/check 確認 session 有效。

呼叫頻率建議:不需要每個 API 都呼叫,但所有涉及會員資料讀寫、金融交易、訂單操作等敏感業務, 建議在請求入口處呼叫一次。對效能敏感的場景可搭配短 TTL 快取(如 10–30 秒)。

📋 快速參考

🔑整合參數

參數說明使用位置
MCC_CLIENT_ID OIDC client_id(= ServiceClient.suuid,全域唯一 UUID) 後端環境變數
MCC_CLIENT_SECRET OIDC client_secret(重設後僅顯示一次) 後端環境變數
GROUP_JWT 群組 Bearer Token(用於 /member/check、/auth/idp/exchange;用於 /oauth/token) 後端環境變數
SERVICE_CODE 服務代碼(X-Service-Code,與 GROUP_JWT 搭配) 後端環境變數
service_suuid 服務識別碼(路徑 B 啟動登入用,值等於 MCC_CLIENT_ID) 前端可見

⚠️ MCC_CLIENT_SECRETGROUP_JWTSERVICE_CODE 為高敏感資訊, 務必僅存於後端環境變數,絕不暴露於瀏覽器或前端原始碼。

⚠️ client_id 必須使用 service.suuid(UUID 字串),不可用人類可讀的 service.aud。 跨群組同名 aud 時 /oauth/token 會回 ambiguous_client_id

🔗關鍵 API 端點

GET
/.well-known/openid-configuration
OIDC Discovery(NextAuth wellKnown 自動讀取)
GET
/.well-known/jwks.json
公開 RS256 公鑰,供本地驗簽 JWT 使用
GET
/oauth/authorize
OIDC 授權端點(路徑 A 入口;params: client_id, redirect_uri, response_type=code, scope, state, nonce)
POST
/oauth/token
換票(authorization_code / refresh_token;Authorization: Basic {client_id}:{secret})
GET
/auth/idp/{provider}/start
啟動第三方登入(路徑 B;params: service_suuid, redirect_uri, response_mode=code)
POST
/auth/idp/exchange
路徑 B:兌換一次性 code(需 GROUP_JWT + SERVICE_CODE)
POST
/member/check
驗證會員 session(兩條路徑皆用;後端重要 API 必呼叫)
headers: GROUP_JWT + SERVICE_CODE + X-Member-Suuid + X-Member-Access-Token
GET
/member/profile
取得會員詳細資料(需 GROUP_JWT + 會員 token)

📖 資源連結