🐙

NFTゲートウェブサイト(NFTの所有に基づいてアクセスを制御するサイト)の作成について

2023/01/12に公開

※当記事はこちらの記事の翻訳版です。
https://blog.thirdweb.com/guides/nft-gated-website/

このガイドでは、React を使用して、NFT の所有に基づいてコンテンツを制限する Web サイトを作成する方法を説明します。

このガイドでは、会員証としての機能に最適な、一つのアセットから多くのNFTを作成できるEdition Dropを使用します!

サンプルリポジトリの使用

はじめに、CLI を使用してスターターキットをクローンします。

npx thirdweb create --next --js

ThirdwebProvider のセットアップ

pages/_app.js ページ内で、アプリケーションを ThirdwebProvider コンポーネントでラップし、React SDK のすべてのフックにアプリケーション内のどこからでもアクセスできるようにします。

このファイルでは、プロジェクトが動作するdesiredChainId(希望するチェーンのId)を設定する必要があります。

このプロジェクトでは、Edition DropのコントラクトがMumbai testnet上にあるため、Mumbaiを使用します。

import { ChainId, ThirdwebProvider } from "@thirdweb-dev/react";

// This is the chainId your dApp will work on.
const activeChainId = ChainId.Mumbai;

function MyApp({ Component, pageProps }) {
  return (
    <ThirdwebProvider
      desiredChainId={activeChainId}
      authConfig={{
        domain: "example.org",
        authUrl: "/api/auth",
        loginRedirect: "/",
      }}
    >
      <Component {...pageProps} />
    </ThirdwebProvider>
  );
}

export default MyApp;

Auth(認証)設定ファイルの作成

プロジェクトのルートに auth.config.js というファイルを新規に作成します。

ここで、認証エンドポイントで使用する ThirdwebAuth オブジェクトを設定します。

ThirdwebAuthオブジェクトには、2つの設定オプションを渡す必要があります。

  1. ウォレットのprivateKey(秘密鍵):これはサーバ側で、ユーザがアプリケーションにサインインするための署名を生成するために使用されます。
  2. domain(ドメイン名):これは任意の値を指定できますが、通常は thirdweb.com のようなウェブサイトの名前になります。
import { ThirdwebAuth } from "@thirdweb-dev/auth/next";

export const { ThirdwebAuthHandler, getUser } = ThirdwebAuth({
  privateKey: "<your-private-key-here>",
  domain: "your-domain-name-here",
});

ウォレットから秘密鍵をエクスポートする方法はこのリンクを参考にしてください。
(日本語訳はまだ行っておりません。)

秘密鍵は安全に保管し、アクセスできるようにしてください。

秘密鍵に安全にアクセスする方法についてはこちらをご覧ください。
(日本語訳はまだ行っておりません。)

Auth(認証) APIエンドポイント

最後に、catch-all APIルートを用意します。

pages/api/auth/[...thirdweb].js が呼ばれ、ThirdwebAuthHandler をエクスポートして、ログインやログアウトなどの必要な認証エンドポイントすべてを管理します。

pages/api/authフォルダ(作成する必要があります)内に[...thirdweb].jsページを作成します。

このファイルから auth.config.js ファイルで設定した ThirdwebAuthHandler をエクスポートします。

import { ThirdwebAuthHandler } from "../../../auth.config";

export default ThirdwebAuthHandler();

ホームページのアクセス制限

ホームページでは、getServersideProps内でサーバーサイドのコードを実行し、チェックしています。

  1. ユーザーが認証されていること
  2. ユーザーがthirdwebのコレクションから NFT を取得していること

ユーザーが認証されていない場合、またはNFTを持っていない場合、ホームページのコンテンツにアクセスする前に、/loginページにリダイレクトされます。

それでは、このホームページのロジックを設定しましょう。

まず、これから使用する機能をインポートする必要があります。

import React from "react";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { useLogout } from "@thirdweb-dev/react";
import { getUser } from "../auth.config";
import checkBalance from "../util/checkBalance";

ホームページは認証されたNFT所有者しかアクセスできないので、ここにお礼のメッセージなど、任意のデータを表示することができます。

export default function Home() {
  const logout = useLogout();

  return (
    <div>
      <h1>Restricted Access Page</h1>
      <p>Thanks for being a member of our NFT community!</p>

      <button onClick={logout}>Logout</button>
    </div>
  );
}

上では、シンプルなお礼のメッセージとlogoutのボタンを表示し、ユーザーをログアウトさせて /login ページにリダイレクトしています。

ここに到達する前に、getServersideProps関数の内部でロジックを実行します。

この関数では、ユーザーの認証状態とNFTの残高をチェックします。

ユーザーが認証されていない場合、または NFT を所有していない場合は、/login ページにリダイレクトされます。

// This gets called on every request
export async function getServerSideProps(context) {
  const user = await getUser(context.req);

  if (!user) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }

  // Instantiate our SDK
  const sdk = ThirdwebSDK.fromPrivateKey(
    // Learn more about securely storing your private key: https://portal.thirdweb.com/web3-sdk/set-up-the-sdk/securing-your-private-key
    "your-admin-private-key-here"
    "mumbai",
  );

  // Check to see if the user has an NFT
  const hasNft = await checkBalance(sdk, user.address);

  // If they don't have an NFT, redirect them to the login page
  console.log("User", user.address, "doesn't have an NFT! Redirecting...");
  if (!hasNft) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }

  // Finally, return the props
  return {
    props: {},
  };
}

NFTの残高を確認する

上記のスニペット(再利用可能なコードの断片)では、checkBalanceというユーティリティ関数(汎用的な処理を行う関数)を呼び出して、ユーザーがコレクションのNFTを持っているかどうかをチェックしています。

import { contractAddress } from "../const/yourDetails";

export default async function checkBalance(sdk, address) {
  const editionDrop = sdk.getEditionDrop(
    contractAddress, // replace this with your contract address
  );

  const balance = await editionDrop.balanceOf(address, 0);

  // gt = greater than
  return balance.gt(0);
}

素晴らしい これで、ユーザーが / ページにアクセスしようとするたびに、アクセスを許可する前に NFT を持っているかどうかを確認することができます。

では、ユーザーがウォレットを使ってアプリケーションにサインインするための /login ページを作成しましょう。

ログインページ

pages フォルダに login.jsx という名前の新しいページを作成します。

このページには、login関数を呼び出すボタンが必要です。

まず、このファイルに必要なロジックをインポートしましょう。

import {
  useAddress,
  useMetamask,
  useEditionDrop,
  useClaimNFT,
  useNetwork,
  useNetworkMismatch,
  useUser,
  useLogin,
} from "@thirdweb-dev/react";

import { ChainId } from "@thirdweb-dev/sdk";
import { contractAddress } from "../const/yourDetails";

インポートの下に、address変数(当サイトに接続されているウォレットのアドレスを検出)に基づくいくつかの条件付きロジックをレンダリングしています。

接続されたウォレットがある場合、Sign Inを表示し、useLoginフックからインポートしたlogin関数を呼び出します。

これはユーザーに、身元を証明するために自分のウォレットでメッセージに署名するよう求めます。

そして、ThirdwebProviderloginRedirectフィールドに設定したURLにリダイレクトさせます。(ここでは/を設定)

これは、ユーザーがメッセージを承認した後、/ ページにリダイレクトされ、そこで再びNFTを持っているかどうかがチェックされることを意味します。

export default function Login() {
  // Wallet & Network Information
  const address = useAddress();
  const connectWithMetamask = useMetamask();
  // Hooks to sign in with ethereum (auth SDK)
  const login = useLogin(); // Sign in

  return (
    <div>
      {address ? (
        <>
          <p>Welcome, {address.slice(0, 6)}...</p>

          <button onClick={login}>Sign In</button>
        </>
      ) : (
        <>
          <button onClick={() => connectWithMetamask()}>Connect Wallet</button>
        </>
      )}
    </div>
  );
}

このプロジェクトについて

このプロジェクトのコピーは、私たちのサンプルリポジトリから作成することができます。

このガイドにあるすべてのコードを含むプロジェクトを作成するには、以下を実行します。

npx thirdweb create --template nft-gated-website

Discordに参加して、チームからの最新情報を入手しましょう!

Discussion