이 글의 소개는 EIP-4361 이더리움 로그인 규칙을 따릅니다.

SIWE(Sign-In with Ethereum)는 이더리움에서 사용자 신원을 확인하는 방법으로, 지갑이 거래를 시작하는 것과 유사하며 사용자가 지갑을 제어할 수 있음을 나타냅니다.

현재 인증 방법은 매우 간단합니다. 지갑 플러그인에 있는 정보만 서명하면 됩니다. 일반 지갑 플러그인에서는 이미 지원하고 있습니다.

이 글에서 고려한 서명 시나리오는 이더리움에 관한 것이며 Solana, SUI 등과 같은 다른 시나리오는 이 글의 범위를 벗어납니다.

SIWE 사용자 매뉴얼: Dapp을 더욱 강력하게 만드는 방법은 무엇입니까?

귀하의 프로젝트에 SIWE가 필요합니까?

SIWE는 지갑 주소의 인증 문제를 해결하기 위한 것이므로 다음과 같은 요구 사항이 있는 경우 SWIE 사용을 고려할 수 있습니다.

    • 귀하의 Dapp에는 자체 사용자 시스템이 있습니다.
    • 쿼리할 정보는 사용자 개인정보 보호와 관련되어 있습니다.

하지만 귀하의 Dapp이 etherscan과 같은 애플리케이션과 같은 쿼리 기반 기능인 경우 SIWE가 없어도 괜찮습니다.

궁금한 점이 있을 수 있습니다. Dapp에서 지갑을 통해 연결하면 지갑의 소유권을 갖게 되는 것 아닌가요?

예, 하지만 완전히 옳지는 않습니다. 프론트엔드의 경우 지갑을 통해 접속한 후 신원을 명시하는 것이 사실이나, 백엔드 지원이 필요한 일부 인터페이스 호출의 경우 단순히 신원을 전달할 방법이 없습니다. 인터페이스 주소가 있으면 누구나 귀하의 신원을 "빌릴" 수 있습니다. 결국 주소는 공개 정보입니다.

SIWE 원칙 및 프로세스

SIWE의 프로세스는 지갑 연결 - 서명 - 신원 획득의 세 단계로 요약될 수 있습니다. 이 세 단계를 자세히 소개합니다.

SIWE 사용자 매뉴얼: Dapp을 더욱 강력하게 만드는 방법은 무엇입니까?

SIWE 사용자 매뉴얼: Dapp을 더욱 강력하게 만드는 방법은 무엇입니까?

SIWE 사용자 매뉴얼: Dapp을 더욱 강력하게 만드는 방법은 무엇입니까?

지갑 연결

지갑 연결은 일반적인 WEB3 작업입니다. 지갑 플러그인을 통해 Dapp에서 지갑을 연결할 수 있습니다.

징후

SIWE에서 서명 단계에는 Nonce 값 획득, 지갑 서명 및 백엔드 서명 확인이 포함됩니다.

Nonce 값을 얻는 것은 ETH 트랜잭션의 Nonce 값 설계를 참조해야 하며 이를 얻으려면 백엔드 인터페이스를 호출해야 합니다. 요청을 받은 후 백엔드는 임의의 Nonce 값을 생성하고 이를 현재 주소와 연결하여 후속 서명을 준비합니다.

프런트 엔드는 Nonce 값을 획득한 후 서명 콘텐츠를 구성해야 합니다. SIWE가 디자인할 수 있는 서명 콘텐츠에는 획득한 Nonce 값, 도메인 이름, 체인 ID, 서명 콘텐츠 등이 포함됩니다. 우리는 일반적으로 에서 제공하는 서명 방법을 사용합니다. 콘텐츠 서명을 수행하는 지갑입니다.

서명이 생성된 후 최종적으로 서명이 백엔드로 전송됩니다.

getgetidentity

백엔드가 서명을 확인하고 전달한 후 해당 사용자 ID(JWT일 수 있음)를 반환합니다. 그런 다음 프런트엔드는 백엔드 요청을 보낼 때 해당 주소와 ID를 가져와 지갑의 소유권을 나타낼 수 있습니다.

연습해 보세요

개발자가 지갑 연결 및 SIWE에 빠르게 액세스할 수 있도록 지원하는 구성 요소와 라이브러리가 이미 많이 있습니다. 연습 목표는 Dapp이 사용자 신원 확인을 위해 JWT를 반환할 수 있도록 하는 것입니다.

본 DEMO는 SIWE의 기본 프로세스를 소개하는 용도로만 사용되며, 프로덕션 환경에서 사용 시 보안 문제가 발생할 수 있습니다.

미리 준비하세요

이 글에서는 nextjs를 사용하여 애플리케이션을 개발하므로 개발자는 nodejs 환경을 준비해야 합니다. nextjs를 사용하면 프론트엔드와 백엔드 프로젝트를 나누지 않고도 풀스택 프로젝트를 직접 개발할 수 있다는 장점이 있습니다.

종속성 설치

먼저 프로젝트 디렉터리에 다음 명령줄을 입력하여 nextjs를 설치합니다.

 npx create-next-app@14

프롬프트에 따라 nextjs를 설치하면 다음 내용을 볼 수 있습니다.

SIWE 사용자 매뉴얼: Dapp을 더욱 강력하게 만드는 방법은 무엇입니까?

프로젝트 디렉토리에 들어가면 nextjs 스캐폴딩이 우리를 위해 많은 작업을 수행했음을 알 수 있습니다. 프로젝트 디렉터리에서 프로젝트를 실행할 수 있습니다.

 npm run dev

그런 다음 터미널 프롬프트에 따라 localhost: 3000 입력하면 기본 nextjs 프로젝트가 실행되는 것을 확인할 수 있습니다.

SIWE 사용자 매뉴얼: Dapp을 더욱 강력하게 만드는 방법은 무엇입니까?

SIWE 관련 종속성 설치

이전 소개에 따르면 SIWE는 로그인 시스템에 의존해야 하므로 프로젝트를 지갑에 연결해야 합니다. 여기서는 다음과 같은 이유로 Ant Design Web3( https://web3.ant.design/ )을 사용합니다.

  1. 완전히 무료이며 현재 적극적으로 유지관리되고 있습니다.
  2. WEB3 구성 요소 라이브러리로서 추가적인 정신적 부담 없이 일반 구성 요소 라이브러리와 사용 경험이 유사합니다.
  3. 그리고 SIWE를 지원합니다.

터미널에 다음을 입력해야 합니다.

 npm install antd @ant-design/web3 @ant-design/web3-wagmi wagmi viem @tanstack/react-query --save

와그미를 소개합니다

Ant Design Web3의 SIWE는 Wagmi 라이브러리를 사용하여 구현하므로 프로젝트에 관련 구성 요소를 도입해야 합니다. 전체 프로젝트에서 Wagmi가 제공하는 Hooks를 사용할 수 있도록 해당 Provider를 layout.tsx 에 도입합니다.

먼저 WagmiProvider의 구성을 정의합니다. 코드는 다음과 같습니다.

 "use client"; import { getNonce, verifyMessage } from "@/app/api"; import { Mainnet, MetaMask, OkxWallet, TokenPocket, WagmiWeb3ConfigProvider, WalletConnect, } from "@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 ( (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, }} wallets={[ MetaMask(), WalletConnect(), TokenPocket({ group: "Popular", }), OkxWallet(), ]} queryClient={queryClient} > {children} ); }; export default WagmiProvider;

Ant Design Web3에서 제공하는 Provider를 사용하고 SIWE의 일부 인터페이스를 정의했습니다. 구체적인 인터페이스의 구현은 나중에 소개하겠습니다.

추후에는 지갑 연결 버튼을 소개하여 프런트엔드에 연결 입구를 추가할 수 있도록 하겠습니다.

이 시점에서 이미 SIWE에 연결했더라도 단계는 매우 간단합니다.

그 후 지갑과 서명을 연결하기 위한 연결 버튼을 정의해야 합니다. 코드는 다음과 같습니다.

 "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"; export default function App() { const jwt = React.useContext(JwtProvider); const renderSignBtnText = ( defaultDom: React.ReactNode, account?: Account ) => { const { address } = account ?? {}; const ellipsisAddress = address ? `${address.slice(0, 6)}...${address.slice(-6)}` : ""; return `Sign in as ${ellipsisAddress}`; }; return ( <>

{jwt}

 ); }

이러한 방식으로 우리는 가장 간단한 SIWE 로그인 프레임워크를 구현했습니다.

인터페이스 구현

위의 소개에 따르면 SIWE에는 백엔드가 사용자의 신원을 확인하는 데 도움이 되는 몇 가지 인터페이스가 필요합니다. 이제 간단하게 구현해 보겠습니다.

목하

Nonce의 목적은 지갑에서 생성된 서명 내용이 서명될 때마다 변경되도록 하여 서명의 신뢰성을 높이는 것입니다. 이 Nonce 생성은 검증의 정확성을 높이기 위해 사용자가 전달한 주소와 연결되어야 합니다.

Nonce의 구현은 매우 간단합니다. 먼저 문자와 숫자로 생성된 임의의 문자열을 생성한 다음 Nonce를 주소에 연결합니다.

 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"); if (!address) { throw new Error("Invalid address"); } const nonce = randomBytes(16).toString("hex"); addressMap.set(address, nonce); return Response.json({ data: nonce, }); }

로그인메시지

signMessage의 기능은 콘텐츠에 서명하는 것입니다. 이 부분은 일반적으로 지갑 플러그인을 통해 완료됩니다. 일반적으로 구성할 필요는 없으며 이 데모에서는 Wagmi의 서명 방법만 지정하면 됩니다. 사용된.

확인 메시지

사용자가 콘텐츠에 서명한 후 서명 전 콘텐츠와 서명을 백엔드로 전송하여 확인해야 합니다. 백엔드는 비교를 위해 서명에서 해당 콘텐츠를 구문 분석하여 일치하면 확인을 통과합니다.

또한 서명된 콘텐츠의 Nonce 값이 사용자에게 보내는 Nonce 값과 일치하는지 여부 등 서명된 콘텐츠에 대해 일부 보안 확인을 수행해야 합니다. 검증이 통과된 후 후속 권한 검증을 위해 해당 사용자 JWT를 반환해야 합니다. 샘플 코드는 다음과 같습니다.

 import { createPublicClient, http } from "viem"; import { mainnet } from "viem/chains"; import jwt from "jsonwebtoken"; import { parseSiweMessage } from "viem/siwe"; import { addressMap } from "../cache"; const JWT_SECRET = "your-secret-key"; // 请使用更安全的密钥,并添加对应的过期校验等const publicClient = createPublicClient({ chain: mainnet, transport: http(), }); export async function POST(request: Request) { const { signature, message } = await request.json(); const { nonce, address = "0x" } = parseSiweMessage(message); console.log("nonce", nonce, address, addressMap); // 校验nonce 值是否一致if (!nonce || nonce !== addressMap.get(address)) { throw new Error("Invalid nonce"); } // 校验签名内容const valid = await publicClient.verifySiweMessage({ message, address, signature, }); if (!valid) { throw new Error("Invalid signature"); } // 生成jwt 并返回const token = jwt.sign({ address }, JWT_SECRET, { expiresIn: "1h" }); return Response.json({ data: token, }); }

현재 SIWE 로그인을 기본적으로 구현하는 Dapp이 개발되었습니다.

일부 최적화 항목

이제 SIWE에 로그인할 때 기본 RPC 노드를 사용하면 확인 프로세스가 거의 30초가 걸리므로 인터페이스의 응답 시간을 향상시키기 위해 전용 노드 서비스를 사용하는 것이 좋습니다 . 이 문서에서는 ZAN의 노드 서비스( https://zan.top/home/node-service?chInfo=ch_WZ )를 사용합니다. ZAN 노드 서비스 콘솔로 이동하여 해당 RPC 연결을 얻을 수 있습니다.

SIWE 사용자 매뉴얼: Dapp을 더욱 강력하게 만드는 방법은 무엇입니까?

Ethereum 메인 네트워크에 대한 HTTPS RPC 연결을 얻은 후 코드에서 publicClient 의 기본 RPC를 바꿉니다.

 const publicClient = createPublicClient({ chain: mainnet, transport: http('https://api.zan.top/node/v1/eth/mainnet/xxxx'), //获取到的ZAN 节点服务RPC });

교체 후 검증 시간이 크게 줄어들고 인터페이스 속도가 크게 빨라집니다.