Hướng dẫn thực hiện xác thực danh tính Ethereum: SIWE
SIWE(Đăng Nhập với Ethereum) là một phương thức xác thực danh tính người dùng dựa trên Ethereum, tương tự như việc ví khởi tạo giao dịch, được sử dụng để chứng minh quyền kiểm soát của người dùng đối với ví cụ thể. Hiện nay, các plugin ví phổ biến đã hỗ trợ phương thức xác thực đơn giản này, chỉ cần ký thông tin trong plugin.
Bài viết này sẽ tập trung vào việc triển khai SIWE trên Ethereum, không đề cập đến các chuỗi công khai khác như Solana, SUI.
Tình huống sử dụng SIWE
Nếu ứng dụng phi tập trung của bạn (Dapp) có các yêu cầu sau, bạn có thể xem xét việc sử dụng SIWE:
Có hệ thống người dùng độc lập
Cần truy vấn thông tin liên quan đến danh tính người dùng
Đối với các ứng dụng chủ yếu là tra cứu ( như trình duyệt blockchain ), thì có thể không sử dụng SIWE.
Mặc dù việc kết nối qua ví trong Dapp có thể thể hiện danh tính, nhưng điều này chỉ có hiệu lực đối với frontend. Đối với các cuộc gọi API cần hỗ trợ backend, chỉ việc truyền thông tin địa chỉ là không đủ để chứng minh danh tính, vì địa chỉ là thông tin công khai.
Nguyên lý hoạt động của SIWE
Quy trình SIWE có thể được tóm tắt thành ba bước: kết nối ví, ký tên, lấy danh tính.
kết nối ví
Đây là thao tác Web3 phổ biến, kết nối ví của người dùng trong Dapp thông qua plugin ví.
chữ ký
Các bước ký bao gồm lấy giá trị Nonce, ký ví và xác minh chữ ký từ backend.
Phía sau tạo ra giá trị Nonce ngẫu nhiên và liên kết với địa chỉ, chuẩn bị cho quá trình ký tiếp theo. Phía trước nhận giá trị Nonce sau đó xây dựng nội dung ký, bao gồm giá trị Nonce, tên miền, ID chuỗi và các thông tin khác, sử dụng phương pháp do ví cung cấp để ký. Cuối cùng, gửi chữ ký đến phía sau để xác thực.
Lấy danh tính
Sau khi xác minh chữ ký ở phía backend thành công, trả về danh tính người dùng ( như JWT ). Khi thực hiện các yêu cầu tiếp theo từ phía frontend, mang theo địa chỉ và danh tính, có thể chứng minh quyền sở hữu ví.
Ví dụ về triển khai SIWE
Dưới đây sẽ trình bày cách thực hiện chức năng SIWE trong dự án Nextjs, giúp Dapp có thể trả về JWT để xác thực danh tính người dùng.
Chuẩn bị môi trường
Ví dụ này được phát triển dựa trên Nextjs, cần chuẩn bị môi trường Node.js. Sử dụng Nextjs giúp phát triển dự án full-stack một cách thuận tiện, không cần tách biệt front-end và back-end.
Cài đặt phụ thuộc
Đầu tiên cài đặt Nextjs:
npx create-next-app@14
Sau khi hoàn thành cài đặt theo hướng dẫn, vào thư mục dự án và khởi động:
npm run dev
Truy cập localhost:3000 để xem dự án Nextjs cơ bản.
Cài đặt các phụ thuộc liên quan đến SIWE
Chúng tôi sẽ sử dụng Ant Design Web3 để thực hiện kết nối ví, nó miễn phí, được duy trì liên tục, và trải nghiệm sử dụng tương tự như thư viện thành phần thông thường, hỗ trợ chức năng SIWE.
typescript
“use client”;
import type { Account } from “@ant-design/web3”;
import { ConnectButton, Connector } from “@ant-design/web3”;
import { Flex, Space } from “antd”;
import React from “react”;
import { JwtProvider } from “./JwtProvider”;
xuất khẩu mặc định chức năng App() {
const jwt = React.useContext(JwtProvider);
Để nâng cao tốc độ xác thực, nên sử dụng dịch vụ nút chuyên dụng. Lấy dịch vụ nút ZAN làm ví dụ, sau khi nhận được kết nối HTTPS RPC của mạng chính Ethereum, hãy thay thế RPC mặc định của publicClient:
Trang này có thể chứa nội dung của bên thứ ba, được cung cấp chỉ nhằm mục đích thông tin (không phải là tuyên bố/bảo đảm) và không được coi là sự chứng thực cho quan điểm của Gate hoặc là lời khuyên về tài chính hoặc chuyên môn. Xem Tuyên bố từ chối trách nhiệm để biết chi tiết.
Lựa chọn mới cho xác thực danh tính Ethereum: Giải thích chi tiết SIWE và hướng dẫn thực chiến
Hướng dẫn thực hiện xác thực danh tính Ethereum: SIWE
SIWE(Đăng Nhập với Ethereum) là một phương thức xác thực danh tính người dùng dựa trên Ethereum, tương tự như việc ví khởi tạo giao dịch, được sử dụng để chứng minh quyền kiểm soát của người dùng đối với ví cụ thể. Hiện nay, các plugin ví phổ biến đã hỗ trợ phương thức xác thực đơn giản này, chỉ cần ký thông tin trong plugin.
Bài viết này sẽ tập trung vào việc triển khai SIWE trên Ethereum, không đề cập đến các chuỗi công khai khác như Solana, SUI.
Tình huống sử dụng SIWE
Nếu ứng dụng phi tập trung của bạn (Dapp) có các yêu cầu sau, bạn có thể xem xét việc sử dụng SIWE:
Đối với các ứng dụng chủ yếu là tra cứu ( như trình duyệt blockchain ), thì có thể không sử dụng SIWE.
Mặc dù việc kết nối qua ví trong Dapp có thể thể hiện danh tính, nhưng điều này chỉ có hiệu lực đối với frontend. Đối với các cuộc gọi API cần hỗ trợ backend, chỉ việc truyền thông tin địa chỉ là không đủ để chứng minh danh tính, vì địa chỉ là thông tin công khai.
Nguyên lý hoạt động của SIWE
Quy trình SIWE có thể được tóm tắt thành ba bước: kết nối ví, ký tên, lấy danh tính.
kết nối ví
Đây là thao tác Web3 phổ biến, kết nối ví của người dùng trong Dapp thông qua plugin ví.
chữ ký
Các bước ký bao gồm lấy giá trị Nonce, ký ví và xác minh chữ ký từ backend.
Phía sau tạo ra giá trị Nonce ngẫu nhiên và liên kết với địa chỉ, chuẩn bị cho quá trình ký tiếp theo. Phía trước nhận giá trị Nonce sau đó xây dựng nội dung ký, bao gồm giá trị Nonce, tên miền, ID chuỗi và các thông tin khác, sử dụng phương pháp do ví cung cấp để ký. Cuối cùng, gửi chữ ký đến phía sau để xác thực.
Lấy danh tính
Sau khi xác minh chữ ký ở phía backend thành công, trả về danh tính người dùng ( như JWT ). Khi thực hiện các yêu cầu tiếp theo từ phía frontend, mang theo địa chỉ và danh tính, có thể chứng minh quyền sở hữu ví.
Ví dụ về triển khai SIWE
Dưới đây sẽ trình bày cách thực hiện chức năng SIWE trong dự án Nextjs, giúp Dapp có thể trả về JWT để xác thực danh tính người dùng.
Chuẩn bị môi trường
Ví dụ này được phát triển dựa trên Nextjs, cần chuẩn bị môi trường Node.js. Sử dụng Nextjs giúp phát triển dự án full-stack một cách thuận tiện, không cần tách biệt front-end và back-end.
Cài đặt phụ thuộc
Đầu tiên cài đặt Nextjs:
npx create-next-app@14
Sau khi hoàn thành cài đặt theo hướng dẫn, vào thư mục dự án và khởi động:
npm run dev
Truy cập localhost:3000 để xem dự án Nextjs cơ bản.
Cài đặt các phụ thuộc liên quan đến SIWE
Chúng tôi sẽ sử dụng Ant Design Web3 để thực hiện kết nối ví, nó miễn phí, được duy trì liên tục, và trải nghiệm sử dụng tương tự như thư viện thành phần thông thường, hỗ trợ chức năng SIWE.
Cài đặt các phụ thuộc liên quan:
npm install antd @ant-design/web3 @ant-design/web3-wagmi wagmi viem @tanstack/react-query --save
cấu hình Wagmi
Trong layout.tsx, nhập WagmiProvider:
typescript “use client”; import { getNonce, verifyMessage } from “@/app/api”; import { Mainnet, MetaMask, OkxWallet, TokenPocket, WagmiWeb3ConfigProvider, WalletConnect, } từ “@ant-design/web3-wagmi”; import { QueryClient } from “@tanstack/react-query”; import React from “react”; import { createSiweMessage } from “viem/siwe”; import { http } from “wagmi”; import { JwtProvider } from “./JwtProvider”;
const YOUR_WALLET_CONNECT_PROJECT_ID = “c07c0051c2055890eade3556618e38a6”; const queryClient = new QueryClient();
const WagmiProvider: React.FC = ({ children }) => { const [jwt, setJwt] = React.useState(null);
return ( <wagmiweb3configprovider siweconfig=“{{” getnonce:=“” async=“” (address)=“”> (await getNonce(address)).data, createMessage: (props) => { return createSiweMessage({ …props, statement: “Ant Design Web3” }); }, verifyMessage: async (message, signature) => { const jwt = (await verifyMessage(message, signature)).data; setJwt(jwt); return !!jwt; }, }} chains={[Mainnet]} transports={{ [Mainnet.id]: http(), }} walletConnect={{ projectId: YOUR_WALLET_CONNECT_PROJECT_ID, }} ví={[ MetaMask(), WalletConnect(), TokenPocket({ nhóm: “Phổ biến”, }), OkxWallet(), ]} queryClient={queryClient} > {children} </wagmiweb3configprovider> ); };
xuất khẩu mặc định WagmiProvider;
thực hiện nút kết nối
Thêm nút kết nối ví và ký.
typescript “use client”; import type { Account } from “@ant-design/web3”; import { ConnectButton, Connector } from “@ant-design/web3”; import { Flex, Space } from “antd”; import React from “react”; import { JwtProvider } from “./JwtProvider”;
xuất khẩu mặc định chức năng App() { const jwt = React.useContext(JwtProvider);
const renderSignBtnText = ( defaultDom: React.ReactNode, tài khoản?: Tài khoản ) => { const { address } = account ?? {}; const ellipsisAddress = address ? ${address.slice(0, 6)}…${address.slice(-6)} : “”; trở lại Đăng nhập với ${ellipsisAddress}; };
return ( <> <flex vertical=“” gap=“middle”> <space> <connectbutton> <connector.siwebutton renderbuttontext=“{renderSignBtnText}”> </connector.siwebutton></connectbutton></space> <div>JWT: {jwt}</div> </flex> <!----> ); }
triển khai giao diện backend
Giao diện Nonce
Tạo nonce ngẫu nhiên và liên kết với địa chỉ:
typescript import { randomBytes } from “crypto”; import { addressMap } from “…/cache”;
export async function GET(request: Request) { const { searchParams } = new URL(request.url); const address = searchParams.get(“address”);
nếu (!address) { throw new Error(“Địa chỉ không hợp lệ”); } const nonce = randomBytes(16).toString(“hex”); addressMap.set(address, nonce); return Response.json({ data: nonce, }); }
Giao diện xác thực tin nhắn
Xác thực chữ ký và trả về JWT:
typescript import { createPublicClient, http } from “viem”; import { mainnet } from “viem/chains”; import jwt từ “jsonwebtoken”; import { parseSiweMessage } from “viem/siwe”; import { addressMap } from “…/cache”;
const JWT_SECRET = “your-secret-key”;
const publicClient = createPublicClient({ chuỗi: mainnet, vận chuyển: http(), });
export async function POST(request: Request) { const { signature, message } = await request.json();
const { nonce, address = “0x” } = parseSiweMessage(message);
nếu (!nonce || nonce !== addressMap.get(address)) { throw new Error(“Invalid nonce”); }
const valid = await publicClient.verifySiweMessage({ thông điệp, địa chỉ, chữ ký, });
nếu (!valid) { throw new Error(“Chữ ký không hợp lệ”); }
const token = jwt.sign({ address }, JWT_SECRET, { expiresIn: “1h” }); return Response.json({ dữ liệu: token, }); }
Tối ưu hiệu suất
Để nâng cao tốc độ xác thực, nên sử dụng dịch vụ nút chuyên dụng. Lấy dịch vụ nút ZAN làm ví dụ, sau khi nhận được kết nối HTTPS RPC của mạng chính Ethereum, hãy thay thế RPC mặc định của publicClient:
typescript const publicClient = createPublicClient({ chuỗi: mainnet, vận chuyển: http('), //Dịch vụ RPC nút ZAN });
Điều này có thể giảm đáng kể thời gian xác thực, cải thiện tốc độ phản hồi của giao diện.