昨天做好的回報功能,不需要知道你是誰。只要內容符合規則,網站就收下。
但收藏不一樣。如果我今天在手機上收藏了一張專輯,晚上回家打開電腦,我希望看到的是同一份清單,而不是重新收藏一次。可是現在的收藏存在瀏覽器裡,換一支手機、換一個瀏覽器,就全部不見了。
要讓收藏跟著人走,網站得先解決一件事:這兩次操作,是同一個人嗎?
今天的重點就在於實作登入功能,來辨識現在使用的人是誰
我先問 Codex:「如果這個專案要做使用者登入功能,你建議怎麼做?」它讀完專案後,建議直接沿用現有的 Supabase Auth,不要另外引入其他服務,也不要自己做一套。它會這樣建議,是因為看到專案已經在用 Supabase;換成全新的專案,答案可能就不一樣了。AI 的建議,取決於它看到什麼。
這次 AI 提出的方案很合理,但還是要看得懂它為什麼這樣選。因為登入要處理的事,遠比「確認帳號密碼對不對」多:
| 要處理的事 | 為什麼 |
|---|---|
| 密碼不能直接存 | 要先經過不可逆的雜湊處理才能存。注意是雜湊,不是加密:加密可以解回原本的密碼,雜湊不行 |
| 記住登入狀態 | 總不能每換一頁,就再問一次密碼 |
| 驗證信箱 | 確認這個信箱真的是他的 |
| 忘記密碼 | 要能安全地讓使用者重設 |
| 擋暴力猜測 | 有人用程式一直試 |
這些事,AI 不一定做不到。問題是,我們有必要為了一個登入功能,自己維護整套身分驗證機制嗎?因此我的選擇是畫面自己設計,但底層的身分驗證,優先交給成熟的服務或套件。
常見的選擇有這幾種:
| 選擇 | 特色 |
|---|---|
| Supabase Auth | 跟資料庫在同一個平台,權限規則可以直接認得「目前是誰」 |
| Firebase Authentication | Google 生態系的身分驗證服務 |
| Clerk、Auth0 | 專門做登入的服務,連登入畫面都幫你做好 |
| NextAuth.js、Better Auth | 裝在自己專案裡的套件,資料存在自己的資料庫 |
登入頁可以完全照自己的設計來做。但真正的身分驗證、帳號和登入狀態的管理,直接交給 Supabase 處理。用了 Supabase Auth,不代表登入頁就得長得像 Supabase。
要選擇哪一種,可以自行評估,這篇以 Supabase Auth 舉例。
Supabase Auth 登入的方式其實很多種:
| 方式 | 使用流程 | 考量 | 這次的決定 |
|---|---|---|---|
| 信箱加密碼 | 先註冊,之後用密碼登入 | 大家最熟悉;但要做註冊、忘記密碼、重設密碼,還要處理弱密碼 | 不做 |
| 信箱連結(Magic link) | 輸入信箱,點收到的一次性連結 | 不用記密碼;但常常要在同一個瀏覽器打開才能登入 | 實作(暫時) |
| 信箱驗證碼(OTP) | 輸入信箱,再輸入收到的六位數 | 不用記密碼,也能換裝置看信;但要自訂信件範本 | 之後再做 |
| Google 登入 (或其他第三方登入) | 交給 Google (其他第三方平台)確認身分 | 一鍵完成;要到 Google (其他第三方)後台設定 | 實作 |
不做密碼登入,是暫時採納了 Codex 的建議:收藏網站使用頻率不固定,很多人會忘記密碼,不做密碼就省掉了忘記密碼、重設密碼、弱密碼這些問題。(但後續可能會加上,畢竟目前很多電子裝置都支援記憶密碼的功能。)
我本來比較想用驗證碼,因為它可以換裝置看信。但驗證碼必須要在信件範本裡加上驗證碼的欄位,而 Supabase 從 2026 年 6 月起,新建立的免費專案如果使用內建的寄信服務,就不能修改信件範本,要先接上自己的寄信服務(SMTP)。所以先用預設的信箱連結來進行示範,過幾天的文章會提到怎麼設定自己的寄信服務,到時候有時間再改成驗證碼。
參考資訊:Supabase:Changes to Email Template Customisation on Free Tier
信箱連結/驗證碼或是 Google 登入還有另外一個好處:可以不用另外做註冊,第一次輸入信箱就是註冊,之後就是登入。
決定好方向之後,我先問 Codex:
我想要先完善登入功能,等登入功能完善了,再開始做收藏功能。如果我決定實作 Email 連結+ Google 帳號登入,具體來說需要做哪些事情?有哪些你可以處理?有哪些是需要我去操作的?
接著我又追問了前端、後端、資料庫各會改什麼,以及需不需要加環境變數。
| AI 能處理 | 要你自己操作 |
|---|---|
| 登入頁、登入狀態、登出 | 在 Google Cloud 建立 OAuth 應用程式 |
| 處理登入回來的那一步(callback) | 在 Supabase 後台開啟 Google、貼上金鑰 |
| 讓伺服器也讀得到登入狀態 | 設定 Redirect URLs |
| 檢查 next、處理錯誤 | 寄信服務、網域、DNS |
| 測試與 build | 產品決定,例如要不要開放自動註冊 |
右邊那欄,我選擇自己處理:有的涉及外部帳號、金鑰和第三方設定,有的是需要我確認的產品決定。有些 AI 工具其實能操作瀏覽器、碰到這些後台,但不是所有能自動化的事,都有必要交給 AI 去做。
AI 給出計畫之後,我會拿這份登入專用的清單對一遍:
| 要問的問題 | 為什麼 |
|---|---|
| 登入狀態存在哪裡? | Next.js 的頁面在伺服器上執行。如果登入狀態只存在瀏覽器裡,伺服器就不知道你是誰,明天的權限規則也會出問題。要存在 cookie 裡,讓伺服器也讀得到 |
| 伺服器有沒有再確認一次身分? | 伺服器不能直接相信瀏覽器傳來的登入資料,要用 Supabase 建議的方式,驗證這份身分是真的 |
| 登入失敗的訊息,會不會洩漏資訊? | 不該透露某個信箱有沒有註冊過 |
| 登入完要跳轉的網址,有沒有檢查? | 第六節細講 |
| 哪些功能這次先不做? | 大頭貼、修改信箱、刪除帳號、雙重驗證,都可以之後再加 |
實作時,我先做信箱連結,Google 留到下一節。
信箱連結幾乎是開箱即用:Supabase 預設就開著信箱登入,內建的寄信服務也能直接寄出登入連結,信件範本不用改。唯一需要做的,是在 Supabase 的 Redirect URLs 加上 http://localhost:3000/auth/confirm,原因看完下面這張圖就知道。
登入頁:輸入信箱,按下寄送
│ 網站請 Supabase 寄信,並附上「登入完要回哪裡」
│ (/auth/confirm?next=...)
▼
Supabase 寄出信件
│
▼
使用者點信裡的連結(連到 Supabase,帶著一次性的 token)
│ Supabase 確認:連結有效、沒過期、沒被用過
▼
回到專案中的 /auth/confirm(網址帶著 code)
│ 用 code,加上瀏覽器先前留下的另一半憑證,
│ 換成登入狀態,存進 cookie
▼
跳到指定的登入前頁面
這張圖能解釋三件事:
/auth/confirm:Supabase 只會把使用者送回允許清單裡的網址,不認得的,就退回首頁next 走
【圖1|Supabase Redirect URL 設定】
所以如果沒有把
http://localhost:3000/auth/confirm加到Supabase 的 Redirect URLs 的話,其實還是可以登入,只是登入完就會直接回到首頁,不會有我們想要的回到登入前的頁面的功能。
後續有自己的網域之後,Redirect URLs 的地方也要新增
http://{{正式網域}}/auth/confirm
測試的時候,很有可能會遇到一個限制:內建的寄信服務,一小時只能寄兩封信。 測了兩次之後,網站就會顯示「寄送次數過多,請稍後再試」。這時可以先用 Google 登入測試跳轉、登出這些流程;正式上線前,建議要接上自己的寄信服務,這件事留到 Day 24。

【圖2|Email 登入信件寄信過於頻繁】
沒有密碼,不代表沒有濫用的風險:風險從「猜密碼」,變成「一直寄信」和「猜連結」。前端的重寄倒數只是讓使用者知道狀況,真正擋得住的,是 Supabase 那一端的次數限制。這跟 Day 17 講的是同一件事:前端擋不住,後端才是真的門。
Google 登入的路線,跟剛剛的信箱連結很像,只是幫你確認身分的,從信箱換成了 Google:
| 誰幫你確認身分 | 最後回到網站的哪裡 | |
|---|---|---|
| 信箱連結 | 你的信箱(收得到信,就代表是你) | /auth/confirm |
| Google 登入 | /auth/callback |
兩條路都是:離開網站 → 讓別人確認你是誰 → 帶著憑證回來 → 換成登入狀態。
不同的是,信箱連結開箱即用,Google 登入一定要先設定兩個後台才能用:Supabase 得先跟 Google「認識」。
專案的登入頁
│ 按下「用 Google 登入」
▼
Supabase Auth
│
▼
Google 登入與授權畫面
│ 登入成功,Google 回到 Supabase
▼
Supabase 的回呼網址(/auth/v1/callback)
│ 確認無誤,回到你的網站
▼
專案的 /auth/callback
│ 完成登入、記住登入狀態
▼
目標頁面
Google 回到 Supabase,Supabase 才回到我的網站。
登入失敗時,先確認自己卡在哪一段,不要一看到網址錯誤,就到處亂加網址。
這趟往返還藏著一個問題:使用者原本正在看的那張專輯,會不會在離開網站、繞過 Google 和 Supabase 之後,就不見了?所以除了設定網址,還要確認「登入前要回去的頁面」,能一路被帶到最後一步。第六節會細講。
整個流程裡,要填網址的地方有這幾個:
| 在哪裡 | 填什麼 | 要填幾次 |
|---|---|---|
| Google 後台:已授權的JavaScript 來源 (Authorized JavaScript origins) | 允許的網站來源,依你使用的整合方式設定,例如:http://localhost:3000 或是正式網域 |
依實際需要 |
| Google 後台:已授權的重新導向 URI(Authorized redirect URIs) | Supabase 的回呼網址:https://你的專案代號.supabase.co/auth/v1/callback |
只要一次 |
| Supabase 後台(Redirect URLs) | 你網站的回呼網址:/auth/callback(Google)和 /auth/confirm(信箱連結),localhost、ngrok、正式網域各一組 |
每多一個網址,就要加一次 |
| 程式碼裡 | 這次登入完,要回到你網站的哪個 callback | AI 會處理 |
在 Google Cloud 設定:
http://localhost:3000 (或是http://正式網域 或http://{{ngrok 測試連結}}

【圖3|進入 Google Cloud Console 新增專案】

【圖4|選擇 「API與服務」】

【圖5|選擇 「OAuth 同意畫面」】

【圖6|「建立 OAuth 用戶端」】

【圖7|應用程式類型選擇「網頁應用程式」】

【圖8|填寫對應授權URI、重新導向URI(填寫內容可見上方設定文字敘述第5.6點)】

【圖9|取得用戶端ID、密碼】
在 Supabase 設定:
http://localhost:3000/auth/callback(Google 用)和 http://localhost:3000/auth/confirm(信箱連結用),之後有 ngrok 或正式網域,再一一加上去(上方圖1)
【圖 10|Supabase 啟用 Google 登入】

【圖 11|將剛剛取得用戶端ID、密碼貼上 Supabase】
ngrok 的網址每一次都會變,所以如果要透過 ngrok 測試登入功能,每一次都要去 supabase Redirect URLs更新 Redirect URLs
此外,可以發現有提供很多第三方登入,也可以去研究。有一些審核可能會比較麻煩(如Facebook),有一些需要付錢(如 Apple,需成為付費開發者才可以設定。)
另一個坑:如果 Google 的同意畫面還在「測試中」,而且使用者類型是「外部」,就只有你手動加進去的測試帳號能登入。你自己測都沒問題,但別人用他的 Google 帳號,就可能會被擋下來。如果有遇到這類問題,可以先確認一下測試的帳號是否是在測試中,填些一些必要資訊後,發布出去,應該就會解決這個問題。
想像使用者正在看 aespa 的《Armageddon》專輯頁面,他按下收藏,網站發現他還沒登入,就帶他去登入頁。登入完成後,他卻回到了首頁,只好再搜尋一次、再找一次那張專輯。
這不是登入功能能不能運作的問題,而是使用者體驗的問題。就算登入成功,如果還得重新搜尋一次剛剛的專輯,整段操作也算不上順暢。
如果沒有特別交代,登入完通常就是回到首頁或某個固定的頁面。要讓使用者回到原本的地方,做法並不複雜:
/login?next=原本的頁面
next,把他送回那一頁這也是 Day 11 網址參數的延伸:網址不只能記住篩選條件,也能記住「等一下要回去哪裡」。
next只是這次取的參數名稱,意思是「登入完要跳轉到哪裡」。名稱可以自己決定,有些網站會用redirect、redirectTo或returnUrl,做的是同一件事。
原本的頁面如果也帶參數,例如 /albums/aespa-armageddon?version=superbeing,不能直接塞進 next,否則後面的參數會被當成登入頁自己的。先編碼,把 / 變成 %2F、? 變成 %3F:
/login?next=%2Falbums%2Faespa-armageddon%3Fversion%3Dsuperbeing
另外,信箱連結和 Google 登入都會先離開網站,所以 next 要一路帶到 /auth/confirm 或 /auth/callback,在最後一步才使用。
網址參數是使用者可以自己改的。如果有人寄給你 /login?next=https://長得很像的假網站.com,你登入完可能就會被帶到假網站,被要求「再輸入一次密碼」。這叫做開放式跳轉。
所以 next 必須設定只接受本站內部的路徑,其他一律回首頁。判斷方式比想像中複雜(// 開頭、反斜線、編碼過的字元都要擋),所以我請 AI 寫成共用的檢查函式,再直接拿惡意網址去測。
昨天,我們不能相信前端送來的
albumId;今天,也不能相信網址裡的next。
這個檢查不只放在登入頁,真正執行跳轉的 /auth/confirm、/auth/callback 也要使用同一套規則。
| 坑 | 原因 | 解法 |
|---|---|---|
| 信件範本改不了 | 2026 年 6 月起,新的免費專案用內建寄信服務,就不能改範本 | 先用預設的信箱連結;要自訂範本,得接上自己的寄信服務(或是付錢升級 supabase 方案) |
| 寄送次數過多 | 內建寄信服務每小時的額度很少 | 同上,或是先用 Google 登入測其他流程;正式上線前接上寄信服務 |
| 登出後一直顯示「登出中…」 | 登出的流程卡在瀏覽器那一端 | 改由伺服器清除登入狀態,再直接跳回首頁(由 AI 處理) |
| 登出了,右上角還顯示已登入 | 伺服器已經清掉登入狀態,但畫面上那份「目前是誰」沒有跟著更新 | 伺服器回傳新的登入狀態時,畫面同步更新。瀏覽器和伺服器各記著一份登入狀態,兩邊要同步(由 AI 處理) |
| 從專輯頁登入,回來卻停在帳號頁 | 初始版本的登入按鈕沒有記下目前在哪一頁,跳轉的預設值為帳號頁或首頁 | 點登入時帶上目前的頁面,連網址後面的參數一起保留(由 AI 處理) |
| 信箱連結登入後,一律回首頁 | Redirect URLs 只加了 Google 用的 /auth/callback,忘了加信箱連結用的 /auth/confirm。Supabase 不認得要回去的網址,就退回預設的首頁 |
補上 /auth/confirm,並確認登入前的頁面有一路帶到最後一步。允許回到 /auth/confirm,跟保留登入前的頁面,是兩個條件 |
| 換瀏覽器打開信箱連結,登不進去 | 開始登入的瀏覽器會先留下一半的憑證 | 已知限制,改成驗證碼就不會有這個問題,因此也可能需要付費升級或是街上自己的信件服務 |
| 測試 | 預期 |
|---|---|
| 第一次用信箱連結登入 | 收到信,點連結後登入,並自動建立帳號 |
| 換一個瀏覽器打開信箱連結 | 照實記錄結果(已知限制) |
| 重新整理、關掉分頁再打開 | 還是登入狀態 |
| Google 第一次登入 | 建立帳號,回到網站 |
| 同一個信箱,先用信箱連結、再用 Google 登入 | 不會變成兩個帳號 |
next=https://別的網站 |
被擋下來,跳回首頁 |
next=//別的網站 |
被擋下來,跳回首頁 |
next=/\別的網站(反斜線) |
被擋下來,跳回首頁 |
| 登出 | 跳回首頁,右上角立刻變成「登入」 |
這次先不做的事
密碼登入、信箱驗證碼(等 Day 24 接上寄信服務)、大頭貼、暱稱、修改信箱、刪除帳號、雙重驗證。今天暫時只示範一套能支援明天收藏功能的身分系統。

【圖 12|AI 實作的登入頁面】

【圖 13|填寫 Email 後顯示已寄出】

【圖 14|信箱收到的登入連結】
收到登入信的時候,你可能會覺得說信很醜,或是寄件人跟我的網站無關,看起來不是很專業。前者的話,可以透過 Supabase 後台調整信件模板,透過 HTML 美化信件(但一樣需要設定 SMTP 或是付費升級);後者則是可以用 SMTP 來處理,這在後續的文章會提到。
| 產品 | 真正需要辨認的是什麼 |
|---|---|
| 記帳 App | 同一個使用者,換了裝置也能看到自己的帳目(當然,如果是APP,也可以保存在手機本地,但這樣就無法跨裝置同步) |
| 線上課程 | 使用者是誰,以及他有沒有買過這堂課 |
| 公司內部系統 | 是不是這個組織的成員(或是說每個成員的權限差異) |
登入處理的是「你是誰」;課程能不能看、資料能不能讀,則是下一層的問題。
今天,網站終於能回答一個問題:你是誰?
AI 做出一個登入按鈕很容易;但按下去登入成功,不等於整段流程都走通了。信寄不寄得到、Google 的往返、登入後回到哪裡,建議稍微暸解一下,遇到錯誤的時候比較好找出問題在哪裡。
不過,知道你是誰,跟你可以做什麼,是兩件事。我登入之後,網站知道我是誰了。那我是不是就能看到別人的收藏?如果我改一下請求裡的使用者 id,會不會就讀到別人的資料?如果我登出,再換另一個帳號登入,又該看到誰的收藏?
明天,我們來實作收藏功能,並替資料庫加上真正的規則:每個人,只能碰自己的東西。
我們明天見。