自架服務 Pocket ID Passkey OIDC SSO 身份驗證

自架服務每個都要記一組密碼?Pocket ID 用 Passkey 把它們收進單一登入

自架 Immich、Proxmox、Grafana、Vaultwarden 累積到一定數量後,帳號管理會變成災難。Pocket ID 用一個容器、一把 Passkey 提供標準 OIDC SSO,本文拆解它的設計取捨、Docker 部署、與 OAuth2 Proxy 搭配的實務模式。

自架服務有一個一旦超過某個臨界點就會爆炸的問題:帳號管理。Immich 一組密碼、Vaultwarden 一組、Proxmox 一組、Grafana 一組、Nextcloud 一組、Jellyfin 一組……兩年下來,密碼管理器裡躺著幾十條 entry,每加一個服務就要再生一組憑證。Pocket ID 在 2026 年快速竄紅,原因不是它做了什麼新東西,而是它把 Passkey、OIDC、單一容器這三件事組合成一個對自架族群剛好夠用的形狀。

Pocket ID 是一套以 Passkey 為唯一登入方式的 OpenID Connect 身份提供者,由社群在 GitHub 上維護,截至 2026 年 5 月已經發布到 v2.7、累積近八千顆星。它鎖定的場景非常明確:homelab 跟中小規模自架站點,要 SSO、但不想為了 SSO 跑一套 Keycloak。

為什麼 Keycloak 跟 Authelia 在 homelab 撐不起來

提到自架 SSO,多數人會先想到 Keycloak。Keycloak 確實功能完整,SAML、LDAP 同步、複雜 RBAC、社交登入全都有,但代價是一個跑著 JVM 的容器要吃掉 1GB 以上的記憶體,外加一套 PostgreSQL,光起動畫面就跑十幾秒。對單機跑著二十幾個 self-hosted 服務的 VPS 來說,這個基礎建設成本太貴。

Authelia 輕量很多,但它走的是反向代理閘道路線,原生支援 OIDC 是 v4 之後才補上的事,過去長期靠 Header 注入跟 nginx auth_request 模組。這條路在 Traefik 後面跑得順,但要對接 Immich 這種有原生 OIDC client 的應用,反而比直接給它一個 OIDC issuer 來得繞。

Pocket ID 的做法是把範圍砍到最小:不要 SAML、不要 LDAP 雙向同步、不要密碼、不要 TOTP、不要社交登入。只剩下標準 OIDC discovery、Authorization Code Flow、JWKS、UserInfo。剩下的全部交給 Passkey。整套程式用 Go 寫,預設用 SQLite,單一執行檔下記憶體常駐約 50MB。

Passkey-only 不是行銷話術,是把問題刪掉

Passkey 在 2025 年下半年完成關鍵的普及門檻:Apple、Google、Microsoft、1Password、Bitwarden 全部支援同步,硬體層 YubiKey 5 系列、Token2 也支援多個 resident credentials。對使用者來說,登入流程從「點密碼欄、貼上密碼、輸入 TOTP」變成「點登入按鈕、按指紋」。對伺服器來說,整套憑證儲存被 WebAuthn 規格收掉,伺服器只存公鑰,沒有可以被脫庫的密碼雜湊。

Pocket ID 的設計取捨直接把密碼這條路砍掉,是經過權衡的:一旦同時支援密碼跟 Passkey,伺服器就要保留密碼相關的所有 attack surface,包含 reset 流程、暴力破解防護、密碼複雜度規則、外洩資料庫比對。砍掉之後,整套程式碼能省下的不只是邏輯量,還包含維護者要追的安全更新。

實務上的隱憂只有一個:使用者把唯一的 Passkey 弄丟。Pocket ID 的對策是允許管理員為單一帳號註冊多個 Passkey,並提供 one-time code 作為應急登入手段。建議的做法是註冊兩把:一把 Passkey 同步到密碼管理器或平台 keychain,另一把存在實體 YubiKey 鎖進抽屜,純粹當還原碼用。

一個 docker-compose 把它跑起來

最小可行設定其實只有十幾行:

1
2
3
4
5
6
7
8
9
10
11
services:
pocket-id:
image: ghcr.io/pocket-id/pocket-id:latest
restart: unless-stopped
environment:
- APP_URL=https://id.example.com
- TRUST_PROXY=true
volumes:
- ./data:/app/data
ports:
- "127.0.0.1:1411:1411"

APP_URL 必須是 HTTPS 的網址,因為 WebAuthn 規格規定 Passkey 只能在 secure context 下註冊跟使用,這不是 Pocket ID 的限制而是瀏覽器強制執行的硬規定。前端搭配 Caddy 或 Nginx Proxy Manager 處理憑證,把 id.example.com 反向代理到 127.0.0.1:1411 就行。

第一次開啟服務時會進入 setup wizard,要求註冊管理員的初始 Passkey。這一步只能用具備 WebAuthn 能力的瀏覽器加上系統內建的驗證器或外接安全金鑰完成。完成後就會進入後臺,OIDC client、使用者、群組三個分頁都在那裡。

資料庫預設是 SQLite,跑在 /app/data/pocket-id.db。對自架規模來說這完全夠用,因為認證流量稀疏、寫入幾乎都在管理面,正式運行階段的負載集中在 JWKS 端點跟 token 驗證上。若需要遷移到 PostgreSQL,環境變數加上 DB_PROVIDER=postgres 跟 connection string 即可。

接服務的兩種路線:原生 OIDC 與 OAuth2 Proxy

Pocket ID 接服務有兩條路。第一條是應用本身就支援 OIDC client,這是首選。Immich、Nextcloud、Grafana、Proxmox、Gitea、Forgejo、Jellyfin、Vikunja、Outline、Minio 都有原生支援,整個流程很標準:在 Pocket ID 後臺建立一個 OIDC client、設定 callback URL、複製 client ID 跟 secret 到應用的設定檔,重啟。整個過程不到三分鐘。

需要留意的是 callback URL 一定要寫完整,包括 scheme、host、path。Immich 比較特殊,桌面版跟手機 app 要分別填 https://photos.example.com/auth/loginapp.immich:/https://photos.example.com/user-settings,少一個就會在某個入口跳錯誤。Proxmox 則要在自己的 datacenter 設定裡新增一個 OpenID Connect realm,並指定 username claim 用 preferred_username 而不是預設的 sub,否則使用者帳號會變成一串 UUID。

第二條路是應用沒有 OIDC client,例如 Vaultwarden 的 admin panel、Code Server、Octoprint、靜態文件站。這時候用 OAuth2 Proxy 當前置認證閘道。架構大致長這樣:使用者 → 反向代理 → OAuth2 Proxy → 真正的應用。OAuth2 Proxy 把 Pocket ID 當 OIDC provider,認證成功後再把流量轉發到後端。

OAuth2 Proxy 部署有一個值得糾正的常見誤區:很多教學會把它做成「一個 OAuth2 Proxy 包多個 upstream」。這在 v7 之後雖然支援,但 cookie domain、登出邏輯、skip_auth_routes 規則很容易互相打架。每個受保護的應用配一個獨立的 OAuth2 Proxy 容器,反而設定檔最乾淨、debug 也快。多花幾十 MB 記憶體換來的是不會在某個禮拜三早上爬一小時 log 找出某個 URL 為什麼被導去登入回圈。

群組對應跟授權的實務細節

Pocket ID 內建群組機制,但它不做權限決策——權限是由應用端讀到 ID Token 裡的 groups claim 之後自己判斷的。這個設計分得很乾淨,但會踩到的人不少。

Grafana 的 OIDC 設定要明確啟用 role_attribute_path 用 JMESPath 表達式從 groups 對應到 Admin/Editor/Viewer。Proxmox 要在 realm 之外另外把 Pocket ID 的群組名手動建立成 Proxmox group,然後手動指派權限。Nextcloud 比較自動,搭配 user_oidc app 可以直接讀 groups claim 並建立對應群組。

實務建議是 Pocket ID 那邊只開兩三個粗粒度群組——例如 adminsfamilyguests——細部權限由各應用自己管。一旦在 Pocket ID 試圖建立精細到「Grafana viewer」、「Nextcloud uploader」等級的群組,整套同步邏輯就會散落在各個服務的 attribute mapping,未來換 SSO 提供者會非常痛。

它不適合誰

把 Pocket ID 塞進企業環境之前要先想清楚邊界。組織需要 SAML(很多 SaaS 還只支援 SAML,特別是合規重的場域)、LDAP 雙向同步、複雜 RBAC、可委派的多管理層級、稽核日誌規範、密碼跟 Passkey 並存的過渡期——這些 Keycloak 跟 Authentik 才是答案。

另外,所有使用者都要有能力管理自己的 Passkey。對技術圈這完全不是問題,但要拉著非技術背景的家人共用 homelab 的時候,初次設定那十分鐘的引導不可省略。把備援 Passkey 寫進操作手冊,比事後幫人重置帳號省事得多。

自架族群該不該換過去

對於正在自架五個服務以下的人,Pocket ID 可能還沒到必要。每個服務獨立帳號加上瀏覽器密碼管理器,運作起來其實沒那麼糟。但只要服務數超過十個,或開始有第二個使用者加入這套自架體系,把認證集中到一個 OIDC issuer 的成本回收速度會超出預期——新加服務的設定時間從「想一組密碼存進管理器」變成「貼一組 client ID/secret」,而所有使用者只要熟悉一種登入方式。

把 Pocket ID 跟一台穩定的 VPS 放在一起特別重要:它是整個自架系統的單一登入點,這個容器要是掛了,所有靠它做 SSO 的服務全部進不去。建議放在最穩定那台機器上,配上單獨的健康監測(Beszel 或 Uptime Kuma 都行)、定期備份 /app/data 那顆 SQLite。

NCSE Network 提供位於臺灣是方電訊機房、採用 Intel Gold CPU 與 NVMe SSD 的 VPS 主機,搭配 99% SLA 與多上游 IP Transit,適合用來放這類「整套自架服務的最底層基礎建設」。需要為 homelab 或正式環境準備一台穩定主機跑 Pocket ID 與其後的服務群,可以到 ncse.tw 瞭解 VPS 與相關服務方案。

需要穩定的雲端主機?

NCSE Network 提供企業級 VPS,7 天免費試用,臺灣是方電訊機房,99% SLA 保證。

查看 VPS 方案 →