Amazon Web Services ブログ

AWS AppSyncを使ったNFT保有者のみがアクセス可能なサービスを実装する

はじめに

Web3のテーマが盛り上がりを見せる中、NFTのユースケースはデジタルコレクティブの用途に限らず、ゲームアイテムの管理・権利関係の管理・電子チケットなど様々な用途に広がりを見せています。NFTを使ったサービスを実装するにあたり「NFTの保有者にだけアクセスを許可したい」ニーズが増えてきています。
たとえば、

  •  特定のNFT保有者のみがアクセス可能なチャットルーム(コミュニティ)を作りたい
  •  会員権相当のNFT保有者のみが新曲の先行配信サイトにアクセスできるようにしたい
  • NFTをアクセストークンとして扱い、NFT保有者に対してのみコンテンツのダウンロードを許可したい

本ブログでは、AWS AppSyncの独自のAPI認証ロジックとAmazon Managed Blockchainを使用して、NFT保有者のみがアクセス可能なチャットルームを例と想定したサービスで、今回は特にAPI認証ロジックに関して解説していきます。

ソリューション概要

AWS AppSync は AWS Lambda を使用して独自のAPI認証ロジックを実装できます。
今回は「ユーザーの本人性確認」と「 独自のAPI認証ロジック」について解説していきます。

アーキテクチャ図

ユーザーの本人性確認

  • Web3のアプリケーションでは、ユーザー名/パスワードやIDフェデレーションを使ったログインの機構を持たないことが多く、ウォレットのアドレスをキーにユーザーを識別しています。ユーザーのアドレスが本人のものであること、なりすましではないことを証明する必要があるるため、ただ単にRestAPIのBodyに自分のウォレットのアドレスを平文で入れるだけでは意味がなく、従来のログインの機構ではない別の手段で本人であることを証明することが必要となります。
  • パブリックブロックチェーンのEthereumでは秘密鍵を持つユーザーが任意のメッセージに対して電子署名を作成することができる機能が備わっています。検証者は任意のメッセージと電子署名を照合することで署名者のウォレットのアドレスを得ることができるようになっています。つまり、任意のメッセージと電子署名を独自のAPI認証ロジックに送付することで、本人からのリクエストであることを証明することができます。この仕組みを使うことで、従来のログインの機構がなくても、システム側でユーザーを識別することができるようになります。
  • チャットルームの入室に使用するNFTの情報を任意のメッセージに挿入することで、誰がどの部屋に入室希望しているのかを識別します。

実装例

  • チャットルームの入室に使用するNFTの情報(tokenId)に対して電子署名を行う実装例です。
import * as ethers from 'ethers';
const AWSHttpProvider = require('@aws/web3-http-provider');

const ambEndpoint = process.env.AMB_HTTP_ENDPOINT;
const privateKey = process.env.PRIVATE_KEY;
const baseProvider = new AWSHttpProvider(ambEndpoint);
const provider = new ethers.providers.Web3Provider(baseProvider);
const account = new ethers.Wallet(privateKey, provider);


const message = JSON.stringify({
    tokenId: tokenId, // tokenId 保有を証明したいトークンのIDを指定する
})

// ユーザーの秘密鍵でmessageに電子署名を付与する
const signedMessage = await account.signMessage(message);

// 電子署名のエンコードしたものをトークンとして使用する。
// Lambda Authorizerでデコードする。
const abiCoder = new ethers.utils.AbiCoder();

let encodedAuthToken = abiCoder.encode(["string", "string"], [message, signedMessage])
console.log("encodedAuthToken", encodedAuthToken)

return encodedAuthToken

独自のAPI認証ロジック

  • ユーザーが本当にNFTを保有しているかを検証する方法について説明します。AWS AppSyncの独自のAPI認証ロジックがAmazon Managed Blockchainを使ってNFTを管理するスマートコントラクトを参照することで、ユーザーがNFTを本当に保有していることを検証します。独自のAPI認証ロジックはLambdaを使用しているため、任意の認証のロジックを実装することができます。
  • 任意のメッセージと電子署名を受け取った独自のAPI認証ロジックでは、まず初めに電子署名をしたウォレットのアドレスを取得します。その後、任意のメッセージからチャットルームの入室に使用するNFTの情報を取得します。必要なNFTを保有しているかをスマートコントラクトを参照して、保有していればアクセスを許可し、保有していなければアクセスを許可しません。これによって、NFT保有者のみがアクセス可能なサービスを作ることができます。

実装例

  • process.env.contractAddressで指定したスマートコントラクトが管理するNFTを保有している場合にアクセスを許可する実装例です。NFTはERC-1155 (https://ethereum.org/ja/developers/docs/standards/tokens/erc-1155/) を使用しています。
  • encodedAuthTokenに含まれるmessageとsignedMessageから電子署名を付与したウォレットのアドレス(signerAddress)を取得します。その後、Amazon Managed Blockchainを使用してスマートコントラクトを参照し、signerAddressが入室に必要なNFTを保有していることを確認します。
  • ERC-1155の場合は、同じIDのNFTを複数所有することができるため、指定したIDのNFTを1個以上保有している場合は入室を許可することとします。
import * as path from 'path';
import * as ethers from 'ethers';

exports.handler = async (event) => {
    const {
      authorizationToken,
      requestContext: { apiId, accountId },
    } = event
    
    // 認証OKならtrueにする
    let isAuthorized = false
    let username =  ''
    let tokenId = ''
    
    try {
        // Blockchainに接続する部分を準備
        const ambEndpoint = process.env.AMB_HTTP_ENDPOINT;
        const contractAddress = process.env.CONTRACT_ADDRESS;
        const AWSHttpProvider = require('@aws/web3-http-provider');
        const ERC1155ContractInfo = require(path.join(__dirname, 'contracts', 'ERC1155.json'));
        const baseProvider = new AWSHttpProvider(ambEndpoint);
        const provider = new ethers.providers.Web3Provider(baseProvider);
        const contract = new ethers.Contract(contractAddress, ERC1155ContractInfo.abi, provider);
        
        // authorizationTokenから電子署名を解析
        const abiCoder = new ethers.utils.AbiCoder();
        let decoded = abiCoder.decode(["string", "string"], authorizationToken)
        let message = decoded[0]
        let signedMessage = decoded[1]
        let messageJson = JSON.parse(message);
        
        // 電子署名を付与したユーザーのアドレスを取得
        let signerAddress = ethers.utils.verifyMessage(message, signedMessage);
        username = signerAddress;
        tokenId = messageJson.tokenId

        // NFTの保有数を確認
        let balanceOf = await contract.balanceOf(signerAddress, messageJson.tokenId);
        console.log(balanceOf.toNumber())
        
        // NFTを保有していれば認証は承認する
        if(balanceOf.toNumber() >0) {
            isAuthorized=true
        };
    
      
    } catch (error) {
      console.log(error)      
    }

    // 認証結果を返す部分の組み立て
    const response = {
      
      // 認証結果。trueなら認証成功
      isAuthorized: isAuthorized,

      // 認証後のメッセージ
      // 後続処理に渡すことのできる情報
      resolverContext: {
        username: username,
        tokenId: tokenId
      },
      deniedFields: [
      ],
      ttlOverride: 10,
    }
    console.log(`response >`, JSON.stringify(response, null, 2))
    return response
  }

まとめ

このブログでは、AWS AppSyncの独自のAPI認証ロジックを使用して、NFT保有者のみがアクセスできる仕組みについて紹介しました。これにより、従来のログインの仕組みを使用せずにユーザーを識別し、NFTをアクセス権に見立ててることで、ユーザーがコンテンツに対してアクセスすることができるかを検証することができるようになりました。
Web3の領域ではNFTはさまざまな用途で使うことができます。今回はチャットの入室例でしたが、他にも特定のNFT保有者のみが視聴可能なアーティストの特典映像、会員証としてのNFTを保有した人のみがアクセス可能なコミュニティなど、さまざまなユースケースで応用が可能です。Web3のサービスでブロックチェーンの外側の世界と連携する場合には、このような方法をとることで実現可能なサービスの幅が広がると思います。