こんにちは、サイオステクノロジーの和田です。今回はEthereumのウォレットアドレス(EOA)の本人確認をMetaMaskの署名機能を使って試したので、実装方法などを書いていきたいと思います。
署名の検証方法
署名の検証を実装するために、以下のツールを使用しました。
- フレームワーク
- Next
- ライブラリ
- React
- Web3
- ethers
- ブラウザ拡張機能
- MetaMask
署名の検証プロセスは以下の手順で行います。
- MetaMaskでウォレットを接続する
- MetaMaskで署名を作成する
- 署名とメッセージをサーバーに送信する
- サーバー側で署名を検証する
- クライアント側で署名の検証結果を受け取る
実装
それでは実装していきたいと思います。以下に実際に試したソースコードを記載します。
app/components/Web3Auth.tsx
"use client";
import { useState, useEffect } from "react";
import Web3 from "web3";
declare global {
interface Window {
ethereum?: any;
}
}
const Web3AuthClient = () => {
const [account, setAccount] = useState<string | null>(null);
const [message, setMessage] = useState<string>("Sign this message to login.");
const [web3, setWeb3] = useState<Web3 | null>(null);
const [recoveredAddress, setRecoveredAddress] = useState<string>("");
useEffect(() => {
if (window.ethereum) {
setWeb3(new Web3(window.ethereum));
}
}, []);
const connectWallet = async () => {
if (!web3) {
alert("MetaMaskをインストールしてください。");
return;
}
try {
const accounts = await window.ethereum.request({
method: "eth_requestAccounts",
});
setAccount(accounts[0]);
} catch (error) {
console.error(error);
}
};
const signMessage = async () => {
if (!web3 || !account) return;
try {
const signedMessage = await web3.eth.personal.sign(message, account, "");
// API に署名データを送信して検証
const response = await fetch("/api/verify-signature", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, account, signature: signedMessage }),
});
const result = await response.json();
if (result.verified) {
alert("署名の検証に成功しました。");
setRecoveredAddress(result.recoveredAddress);
} else {
alert("署名の検証に失敗しました。");
}
} catch (error) {
console.error(error);
}
};
return (
<div className="p-4 border rounded shadow-md">
<h2 className="text-lg font-bold mb-2">Web3 認証</h2>
{account ? (
<>
<p>
接続したアカウント: <strong>{account}</strong>
</p>
<p>
復元されたアドレス: <strong>{recoveredAddress.toLowerCase()}</strong>
</p>
<button onClick={signMessage} className="px-4 py-2 bg-blue-500 text-white rounded mt-2">
署名を検証する
</button>
</>
) : (
<button onClick={connectWallet} className="px-4 py-2 bg-green-500 text-white rounded">
ウォレットに接続する
</button>
)}
</div>
);
};
export default Web3AuthClient;
app/api/verify-signature/route.ts
import { NextRequest, NextResponse } from "next/server";
import { verifyMessage } from "ethers";
export async function POST(req: NextRequest) {
try {
const { message, account, signature } = await req.json();
if (!message || !account || !signature) {
return NextResponse.json({ error: "Missing parameters" }, { status: 400 });
}
const recoveredAddress = verifyMessage(message, signature);
const isValid = recoveredAddress.toLowerCase() === account.toLowerCase();
return NextResponse.json({ verified: isValid, recoveredAddress }, { status: isValid ? 200 : 401 });
} catch (error) {
console.error("Signature verification error:", error);
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}
app/page.tsx
import Web3Auth from "./components/Web3Auth";
export default function Home() {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<Web3Auth />
</div>
);
}
動作確認
上記で作成したアプリを動かしてみます。npm run dev
で起動し、localhost:3000
にアクセスします。すると以下のような画面が表示されます。

ウォレットに接続するをクリックするとメタマスクのパスワードを聞かれるので入力してロック解除します。

すると以下のような画面が表示されます。メタマスクで選択されているアカウントが表示されています。

署名を検証するをクリックします。クリック後、以下のような署名要求のポップアップが表示されるので、確認をクリックします。

確認をクリック後、以下のように署名の検証結果が表示されます。

OKをクリックすると、以下のように復元されたアドレスが表示されます。サーバー側で署名から復元したアドレスと接続したアカウントのアドレスが一致していることが確認できました。

まとめ
今回は MetaMask の署名機能を利用して EOA の所有者を検証する簡単なアプリケーションを実装しました。署名の検証プロセスを通じて、EOA 所有者の本人確認を実現できることが確認できました。