[區塊鏈] 實作使用 MetaMask 錢包登入 (Login With MetaMask),後端搭配 Firebase

這次大班哥來研究,如何以電子錢包當作登入的帳戶,在目前的登入方式中,多一種登入方式,以電子錢包作為登入,是更匿名且不再需要提供 email 其他個資等等

使用情境

▼ 使用情境:登入/註冊頁面

提供「使用者用 MetaMask 錢包當作登入選項」

登入後能以這個帳號 follow 別人的買賣,點贊別人的操作等,

以上的行為其實在目前的 web 世界早就行之有年,背後就是需要實作一個會員系統跟登入介面,

而區塊鏈應用的網站,在驗證登入這部分,就可以結合電子錢包地址數位簽章來達到驗證,

以下大班哥會用自己的理解來嘗試實作一個錢包登入的介面跟搭配 Firebase,來解釋背後驗證的流程

後端要如何驗證錢包的擁有者?

「後端要如何驗證你就是錢包的擁有者?」相信這是要做錢包登入的第一個問題,

其實就是透過密碼學的應用 – 數位簽名

我們可以拿一個隨機數字,拿給前端錢包的擁有者,請他簽名後返回後,

後端可以透過密碼學相關演算法來驗證,是否是錢包擁有者所簽的名

而拿的隨機數字,我們稱之為 nonce,nonce 在加密通訊中之會被使用一次,

所以同位 user 做多次的簽名驗算,每次都是會拿到不依樣的 nonce 亂數,

這樣的目的是為了防止駭客有機會拿之前的簽名來試圖騙過後端的驗證,此種攻擊或稱 replay attacks

電子錢包登入的驗證流程

▼ 大班哥會用以下這張圖來說明整個流程

由位於左上角的「User 提供 wallet address」 入口開始流程

整個登入授權可以拆分出三個大步驟講解:

Step 1. 前端取得 nonce 加密簽名

前端在這部分是用 React 去打 API /nonce 跟後端溝通拿到 nonce,大班哥是用 uuid 這套 node lib 去產生一組 uuid 數字來當作返回的 nonce,前端拿到 nonce 後,

▼ 會請 ether.js 去跟 Metamask 溝通跳出需要簽名的視窗出來請 user 確認簽名,可以參考一下 code

const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const signature = await signer.signMessage(nonce); // request user to sign the nonce and get the signature

Step 2. 驗證簽名產生 Token

這邊大班哥另外提供 API /verify 需要帶參數 noncesignature

這個 verify API 需要做的事,就是要驗證前端回傳的 signature 是否為該錢包使用者

▼ 可以使用 ether.jsverifyMessage() 來做

const signerAddress = ethers.utils.verifyMessage(nonce, signature);

if (signerAddress !== walletAddress) {
    throw new Error ("wrong_signature");
}      

如果確認完 signer 的 address 後,即代表錢包驗證完成,即可結合 firebase admin 的 auth,

這部分可以參考 firebase admin 的 Create Custom Tokens

簡單理解:就是後端請 firebase admin 產生一個授權 token (JWT) 返回給前端,而前端再拿著 token 來在跟 firebase 登入,

▼ 要注意 createCustomToken 需要帶一個 id,文件上是建議自己 DB 裡的 user ID,因為之後需要也方便查驗,

At a minimum, you need to provide a uid, which can be any string but should uniquely identify the user or device you are authenticating.
const firebaseToken = await getAuth().createCustomToken(userId);

Step 3. 拿 Token 跟 firebase 授權登入

拿到 token (JWT) 後,

可以參考 firebase 文件 其實就是 call signInWithCustomToken() 即可登入,

能夠取得 firebase auth 回傳的 user credental 基本上就是登入成功囉

const auth = getAuth()
const userCredential = await signInWithCustomToken(auth, token);

Demo 流程



尚未有留言,成為第一個留言的人吧!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。