前面三課(選型三部曲)你已經想清楚要給用戶哪些登入選項,再往前你也選好了資料庫、畫好了 schema,連 users 這張表都準備好了。從這一課開始,我們要把它「安全地實作出來」。第一個要解決的問題是:這張表裡的「人」是怎麼進來的?使用者要怎麼證明「我就是我」,而不是隨便一個人冒充?這就是 Authentication(身分驗證),也是讓 App 從玩具變成產品的第一道門。
登入這件事,本質上只是 App 在問一個問題:「你真的是你說的那個人嗎?」
很多人想到登入,第一反應是「做一個帳號密碼欄位」。我們手動走一次,看看為什麼這條路很危險,以及為什麼把它交出去才是對的決定。
| password 欄存的東西 | |
|---|---|
| ming@x.com | hunter2 |
| hua@x.com | hunter2 |
| 使用者 | ① 明文 | ② MD5(無 salt) | ③ bcrypt(每人不同 salt) |
|---|---|---|---|
| 小明 | hunter2 | f181ec537487dab0 | $2a$10$x9Kf22cbb9461d87abc |
| 小華 | hunter2 | f181ec537487dab0 | $2a$10$Qp7Lm0beae0e3c00533d |
users 表有一個 password 欄,裡面存著看得懂的字串。最該做的是什麼?按「下一步」自己走一遍,看看按下按鈕之後,瀏覽器到底跑去哪、誰在跟誰講話。
一切從這顆按鈕開始。你的 App 不會自己問密碼,而是把使用者交給 Google。
| Name | Method | Status | Type |
|---|---|---|---|
| your-app.com/ | GET | 200 | document |
精準來說,Google 導回來的不是直接可用的 token,而是一段「authorization code」。要再用這段碼跟 Google 換真正的 token,這一步叫 token exchange,而且發生在後端,因為要用到只有後端才有的密鑰,前端永遠拿不到。
為了防止有人中途攔截這段碼,現代流程還會加上 PKCE 和 state 兩道防偽(防 CSRF)。好消息是:用 Firebase 的話,這些它都幫你處理好了,你不用自己實作。知道有這層就好。
前面那趟流程要能跑,前提是先在 Firebase Console 把 Google 這個登入方式開起來。我們模擬一遍實際要點哪裡。
signInWithPopup,卻忘了提「登入方式要先在 Firebase Console 開」「Callback URL 要兩邊登記」。結果你一按就壞,還以為是 code 寫錯。難的都在前面那段設定。真正寫的程式碼只有一個 function 呼叫,按下按鈕就會啟動剛才那趟登入旅程。
const provider = new GoogleAuthProvider(); await signInWithPopup(auth, provider);
就這樣兩行。GoogleAuthProvider 指定用 Google 登入,signInWithPopup 跳出那個熟悉的選帳號視窗。其餘整趟跳轉、換 token、建 session,Firebase 全包了。
登入成功只是開始。App 怎麼「一直記得你」?出問題時又該往哪看?
| Name | Value | Domain | Path | Expires | Size | HttpOnly | Secure | SameSite |
|---|---|---|---|---|---|---|---|---|
| firebaseIdToken | eyJhbGci….SflKx | your-app.com | / | 2024-05-29 18:00 | 412 | ✓ | ✓ | Lax |
| firebaseRefreshToken | v1.Mr8… | your-app.com | / | 2024-08-27 18:00 | 88 | ✓ | ✓ | Lax |
Error 400: redirect_uri_mismatch最可能的原因是?Error: Unsupported provider: provider is not enabled最可能的原因是?登入後沒有報錯,但 session 一直是 null最可能的原因是?現在 App 認得你是誰了。但「登入的人才能看 /dashboard」這件事要怎麼擋?未登入的人闖進來又該怎麼導回登入頁?下一段我們用 Middleware 當網站保全,把「你能做什麼」這條界線畫出來。