把 opolyx 裝到手機上——並且無需密碼即可登入
opolyx 現在是一款真正可安裝好的 App,並且有四種登入方式:魔法連結、郵件驗證碼、Google 或 Apple。其中驗證碼是在任何環境下都能用的那一種。

通俗版
你可以把 opolyx 新增到主畫面,像其他 App 一樣開啟它:擁有自己的圖示、沒有瀏覽器工具欄、直接進入大廳。今天在 Android 和 iOS 上都可以透過瀏覽器選單完成,而且這與我們正在為 App Store打包的是同一份構建。
- 全螢幕啟動後直接進入大廳,而不是營銷頁面;
- 擁有自己的圖示和啟動畫面,而不是一個瀏覽器快捷方式;
- 離線時顯示一個正經的 opolyx 頁面,而不是瀏覽器報錯;
- 絕不快取任何來自進行中對局的資料,因此已安裝好的 App 與網站一樣新。
現在登入有四條路徑,你可以選擇最適合當前裝置的一種:
| 方式 | 工作原理 |
|---|---|
| 魔法連結 | 郵件裡的一個連結——點一下即可,無需密碼 |
| 郵件驗證碼 | 一個 8 位驗證碼,在登入頁面輸入 |
| 標準的帳號選擇介面 | |
| Apple | 包括 Hide My Email,其背後的地址我們從來看不到 |
透過郵件傳送的驗證碼是在任何環境下都行得通的那條路——包括在 App 外殼內部,因為在那裡連結會在另一個瀏覽器中開啟,無法把會話交回來。
技術細節
Service worker 的範圍是刻意收窄的。它是一份白名單,而不是通吃:導航請求走"網路優先",失敗時回退到離線頁面;帶雜湊的靜態資源採用 stale-while-revalidate;其餘的一切——資料庫、遊戲伺服器、即時通道、身份驗證、服務端渲染的載荷——統統繞過,因此它絕不可能提供過期的棋盤,也不可能弄壞一局進行中的對戰。
// public/sw.js — allow-list, not catch-all
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if (url.origin !== self.location.origin) return; // Supabase, daemon: untouched
if (BYPASS.some((p) => url.pathname.startsWith(p))) return; // /auth, /api, RSC
if (event.request.mode === 'navigate') return event.respondWith(networkFirst(event));
if (HASHED.test(url.pathname)) return event.respondWith(staleWhileRevalidate(event));
});真正棘手的是被封裝的 iOS 版本:在那裡沒有任何一種登入方式能夠完成,而在所有瀏覽器裡一切正常。原因是打包工具替你寫入的一項 WebKit 設定:啟用"App 繫結域名"後,WebKit 會靜默取消任何指向未列入清單主機的頂層導航——而 OAuth 流程的第一跳恰恰就是這樣的導航。另外,Google 出於政策拒絕嵌入式 webview,這一點任何配置都無法解決。
魔法連結在那裡同樣不可能奏效:它會在系統瀏覽器中開啟,而 App 的 webview 看不到系統瀏覽器的 Cookie 儲存。正因如此,輸入驗證碼才是唯一同源的郵件路徑——它在提出請求的那個 webview 中完成。
對於 Google,官方認可的做法是在系統瀏覽器中跑完流程,再把會話交回來。這座橋樑在構造授權 URL 時刻意不使用 PKCE,於是流程會在回撥片段中返回令牌,供外殼讀取——若返回授權碼,則只有持有校驗值的那個瀏覽器才能兌換,而那恰恰不是我們需要的那一個。
