テイラーワークス社内実験「Play! Web3」について#4:〜認証スマートコントラクト(EIP-712)〜
こんにちは。テイラーワークスの澤田です。
プロダクトチームでWeb3エンジニアをやっています。
Web3を中心に、気になるテーマについてブログにまとめていけたらと思います。
「Play! Web3」というWeb3体験まつり(社内実験)で扱った技術について紹介する連載4回目。
今回は、スマートコントラクトを使った認証、認証スマートコントラクト(EIP-712) について紹介したいと思います。
※ブログ内で引用しているコードは、社内で実験したものを紹介しています。安全性や脆弱性の検査は行っていません。参考にされる場合は、ご自身の責任の元、ご対応ください
連載一覧
- テイラーワークス社内実験「Play! Web3」について#1:〜トークンの発行〜
- テイラーワークス社内実験「Play! Web3」について#2:〜NFT編〜
- テイラーワークス社内実験「Play! Web3」について#3:〜SoulBound Token編(ソウルバウンドトークン・SBT)〜
- テイラーワークス社内実験「Play! Web3」について#4:〜認証スマートコントラクト(EIP-712)〜 ←今回の記事です
認証って?
認証とは、アクセスしてきたユーザーが本当にそのユーザーであるか確かめる行為を指します。
IDやメールアドレスとパスワードを入力してログインする、あれですね。
多くのサイトではユーザーからの入力が正しいときに、サービスにログインできるようになっています。
Web3を使った認証方法として、ブロックチェーンの技術を使用する認証というのがあります。
EIP-712
EIP-712 という署名の仕様をベースに実装しています。
EIP-712というのは Ethereum Improvement Proposals(EIP)の一つで、メッセージや構造体に対する署名を行います。
Play! Web3で実装した認証処理を見ていきましょう。
こちらが「Play! Web3」のログイン画面です。IDやパスワードを入力するフォームはありません。
ログインボタンをクリックすると、MetaMask が署名を要求してきます。(署名の要求 が表示されます)
この画面で署名を行うと、ウェブサイトにログインします。
仕組み
処理の流れは以下のようになっています。
- サイトにアクセス(ユーザー)
- イーサリアムアドレスをサイト側に渡す(ブラウザ)
- 推測できないランダムな文字列などを返す(API)
- 署名を実行するか、ユーザーに問いかける(ブラウザ)
- 「署名」をクリックする(ユーザー)
- 署名を実行し、署名で得られた文字列をサイト側に渡す(ブラウザ)
- 受け取った文字列を検証し、結果を返す(認証スマートコントラクト)
- 認証結果を返す(API)
- 認証の結果を表示(ブラウザ)
APIを経由しないで直接スマートコントラクトに認証の処理を行わせるという実装は、今回はやりませんでした。
トランザクション処理が高速なブロックチェーンを使用していても、結果が得られるまでユーザーを待たせることになるため、ログインには不向きと判断しました。
ですので、署名する元の内容の生成はAPIが行い、ブラウザ側(MetaMask)で署名を行い、署名をAPIで送信し、APIからスマートコントラクトを使用し署名の検証を行うような形で実装しています。
主な仕様
- APIで作成したログイン情報(一度きりの内容)をウェブフロントで MetaMask を使用し署名
- 署名をAPIで送信し、APIが認証スマートコントラクトで正しい署名であるかを検証
- TWトークンのスマートコントラクトに対し、署名から得られたイーサリアムアドレスがメンバーであるかを問いかけ
署名する項目
EIP-712の基本の構造体は、標準仕様に準拠させています。
struct EIP712Domain {
string name;
string version;
uint256 chainId;
address verifyingContract;
bytes32 salt;
}
ブラウザ側で署名する内容はこのようになっています。
- 認証ID(APIが生成・連番)
- ログインを要求したユーザーのイーサリアムアドレス
- 認証文字列(APIが生成・推測しにくい文字列)
struct Auth {
uint256 authId;
address user;
bytes32 key;
}
検証処理
署名の検証は verify()
関数にて行います。署名文字列、署名した内容(元の情報)を引数にして実行します。
本人が正しく行った署名である場合は、検証結果として本人のイーサリアムアドレスが得られます。
得られたイーサイリアムアドレスが本人のものであるかの比較で、認証の可否を判断するという仕組みです。
// @title Calculate EIP712Domain TypeHash
function hashDomain(EIP712Domain memory eip712Domain) internal pure returns (bytes32) {
return keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256(bytes(eip712Domain.name)),
keccak256(bytes(eip712Domain.version)),
eip712Domain.chainId,
eip712Domain.verifyingContract,
eip712Domain.salt
)
);
}
// @title Calculate Auth TypeHash
function hashAuthentication(Auth memory auth) private view returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(AUTH_TYPEHASH, auth.authId, auth.user, auth.key))));
}
// @title Verify signature: Obtain EOA address from signature
// @param bytes _signature
// @param uint256 authId
// @param address user
// @param bytes32 key
// @return address EOA address obtained from signature
function verify(bytes memory _signature, uint256 authId, address user, bytes32 key) public view returns (address) {
Auth memory auth = Auth({authId: authId, user: user, key: key});
bytes32 hash = hashAuthentication(auth);
return hash.recover(_signature);
}
おわりに
今回は、テイラーワークスの社内のWeb3体験イベント「Play! Web3」で行った スマートコントラクトを使用したユーザー認証 について紹介しました。
次回は ブロックチェーンで乱数を生成する方法 について紹介したいと思います。
テイラーワークスは、エンジニア採用強化中
テイラーワークスは、エンジニア採用強化中です!
▼少しでも興味をお持ちいただけましたら、採用ページもチェックしてみてください
Discussion