├── Part1 ├── NFT-ERC721-Sample-Collection-SmartContract.sol ├── README.md ├── components │ ├── config.js │ ├── nftabi.json │ └── web3connect.js └── pages │ ├── _app.js │ ├── api │ └── hello.js │ ├── denied.js │ ├── index.js │ └── welcome.js └── README.md /Part1/NFT-ERC721-Sample-Collection-SmartContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT LICENSE 2 | 3 | /* 4 | N2D NFT ERC721 NFT Smart Contract. 5 | 6 | Follow/Subscribe Youtube, Github, IM, Tiktok 7 | for more amazing content!! 8 | @Net2Dev 9 | ███╗░░██╗███████╗████████╗██████╗░██████╗░███████╗██╗░░░██╗ 10 | ████╗░██║██╔════╝╚══██╔══╝╚════██╗██╔══██╗██╔════╝██║░░░██║ 11 | ██╔██╗██║█████╗░░░░░██║░░░░░███╔═╝██║░░██║█████╗░░╚██╗░██╔╝ 12 | ██║╚████║██╔══╝░░░░░██║░░░██╔══╝░░██║░░██║██╔══╝░░░╚████╔╝░ 13 | ██║░╚███║███████╗░░░██║░░░███████╗██████╔╝███████╗░░╚██╔╝░░ 14 | ╚═╝░░╚══╝╚══════╝░░░╚═╝░░░╚══════╝╚═════╝░╚══════╝░░░╚═╝░░░ 15 | 16 | THIS CONTRACT IS AVAILABLE FOR EDUCATIONAL 17 | PURPOSES ONLY. YOU ARE SOLELY REPONSIBLE 18 | FOR ITS USE. I AM NOT RESPONSIBLE FOR ANY 19 | OTHER USE. THIS IS TRAINING/EDUCATIONAL 20 | MATERIAL. ONLY USE IT IF YOU AGREE TO THE 21 | TERMS SPECIFIED ABOVE. 22 | */ 23 | 24 | 25 | import "@openzeppelin/contracts/access/Ownable.sol"; 26 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; 27 | 28 | pragma solidity ^0.8.4; 29 | 30 | contract Collection is ERC721Enumerable, Ownable { 31 | 32 | 33 | using Strings for uint256; 34 | string public baseURI; 35 | string public baseExtension = ".json"; 36 | uint256 public maxSupply = 1000; 37 | uint256 public maxMintAmount = 5; 38 | bool public paused = false; 39 | 40 | constructor() ERC721("Net2Dev NFT Collection", "N2D") {} 41 | 42 | 43 | function _baseURI() internal view virtual override returns (string memory) { 44 | return "ipfs://QmYB5uWZqfunBq7yWnamTqoXWBAHiQoirNLmuxMzDThHhi/"; 45 | 46 | } 47 | 48 | function mint(address _to, uint256 _mintAmount) public payable { 49 | uint256 supply = totalSupply(); 50 | require(!paused); 51 | require(_mintAmount > 0); 52 | require(_mintAmount <= maxMintAmount); 53 | require(supply + _mintAmount <= maxSupply); 54 | 55 | for (uint256 i = 1; i <= _mintAmount; i++) { 56 | _safeMint(_to, supply + i); 57 | } 58 | } 59 | 60 | 61 | function walletOfOwner(address _owner) 62 | public 63 | view 64 | returns (uint256[] memory) 65 | { 66 | uint256 ownerTokenCount = balanceOf(_owner); 67 | uint256[] memory tokenIds = new uint256[](ownerTokenCount); 68 | for (uint256 i; i < ownerTokenCount; i++) { 69 | tokenIds[i] = tokenOfOwnerByIndex(_owner, i); 70 | } 71 | return tokenIds; 72 | } 73 | 74 | 75 | function tokenURI(uint256 tokenId) 76 | public 77 | view 78 | virtual 79 | override 80 | returns (string memory) { 81 | require( 82 | _exists(tokenId), 83 | "ERC721Metadata: URI query for nonexistent token" 84 | ); 85 | 86 | string memory currentBaseURI = _baseURI(); 87 | return 88 | bytes(currentBaseURI).length > 0 89 | ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension)) 90 | : ""; 91 | } 92 | // only owner 93 | 94 | function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner() { 95 | maxMintAmount = _newmaxMintAmount; 96 | } 97 | 98 | function setBaseURI(string memory _newBaseURI) public onlyOwner() { 99 | baseURI = _newBaseURI; 100 | } 101 | 102 | function setBaseExtension(string memory _newBaseExtension) public onlyOwner() { 103 | baseExtension = _newBaseExtension; 104 | } 105 | 106 | function pause(bool _state) public onlyOwner() { 107 | paused = _state; 108 | } 109 | 110 | function withdraw() public payable onlyOwner() { 111 | require(payable(msg.sender).send(address(this).balance)); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Part1/README.md: -------------------------------------------------------------------------------- 1 | # Web3-NFT-User-Authentication 2 | ⚡ A very straightfoward guide to authenticate users in NextJS with NFTs. 3 | Block users that aren't holding a specific ERC721 NFT 4 | 5 |

Securing Apps with NFTs

6 | 7 | 8 | ** THE FILES ATTACHED TO THIS REPO ARE FOR EDUCATIONAL PURPOSES ONLY ** 9 | 10 | ** NOT FINANCIAL ADVISE ** 11 | 12 | ** USE IT AT YOUR OWN RISK** **I'M NOT RESPONSIBLE FOR ANY USE, ISSUES ETC.. ** 13 | 14 | 15 | Click for video: 16 | 17 | 18 | 19 | 20 |

Steps to use this Repo

21 | 22 | 23 | 1-Create a new NextJS app: 24 | 25 | ```shell 26 | npx create-next-app web3auth 27 | ``` 28 | 29 | 2- Install Dependencies: 30 | 31 | ```shell 32 | cd web3auth 33 | npm i ethers web3modal 34 | ``` 35 | 36 | 3- Replace all files and folders in your project with the ones attached to this repo. 37 | 38 | Add all files and folders to the root project directory "web3auth", overwrite when prompted. 39 | 40 | 4- Deploy the attached NFT Smart Contract Attached to your favorite testnet 41 | 42 | NFT-ERC721-Sample-Collection-SmartContract.sol 43 | 44 | 5- Mint a couple of NFTs from the smart contract to one wallet. 45 | 46 | 6- Add the NFT Smart Contract address to: components/config.js 47 | 48 | ```shell 49 | export const nftcontract = "REPLACE WITH NFT SMART CONTRACT ADDRESS"; 50 | ``` 51 | ctrl + s to save 52 | 53 | 7- Run your NextJS Project 54 | 55 | ```shell 56 | npm run dev 57 | ``` 58 | 59 | 8- TEST 60 | 61 | - Navigate to your project page and proceed to test by clicking "Connect Wallet" 62 | 63 | with the wallet that holds the NFTs. You should be redirected to a "Welcome" page. 64 | 65 | - Attempt the same test with another wallet that doesn't have NFTs and you should 66 | 67 | be redirected to an "Access Denied" page. 68 | 69 | - Switch back to the wallet that has NFTs and get to the "Welcome" page. 70 | 71 | - Switch to the wallet that doesn't have NFTs and you should be auto redirected 72 | to the "Access Denied" page. 73 | 74 | 75 | Follow the video tutorial for explanations and guidance! 76 | -------------------------------------------------------------------------------- /Part1/components/config.js: -------------------------------------------------------------------------------- 1 | 2 | export const nftcontract = "REPLACE WITH NFT SMART CONTRACT ADDRESS"; 3 | -------------------------------------------------------------------------------- /Part1/components/nftabi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "owner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "approved", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": true, 19 | "internalType": "uint256", 20 | "name": "tokenId", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "owner", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "operator", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "bool", 45 | "name": "approved", 46 | "type": "bool" 47 | } 48 | ], 49 | "name": "ApprovalForAll", 50 | "type": "event" 51 | }, 52 | { 53 | "anonymous": false, 54 | "inputs": [ 55 | { 56 | "indexed": true, 57 | "internalType": "address", 58 | "name": "previousOwner", 59 | "type": "address" 60 | }, 61 | { 62 | "indexed": true, 63 | "internalType": "address", 64 | "name": "newOwner", 65 | "type": "address" 66 | } 67 | ], 68 | "name": "OwnershipTransferred", 69 | "type": "event" 70 | }, 71 | { 72 | "anonymous": false, 73 | "inputs": [ 74 | { 75 | "indexed": true, 76 | "internalType": "address", 77 | "name": "from", 78 | "type": "address" 79 | }, 80 | { 81 | "indexed": true, 82 | "internalType": "address", 83 | "name": "to", 84 | "type": "address" 85 | }, 86 | { 87 | "indexed": true, 88 | "internalType": "uint256", 89 | "name": "tokenId", 90 | "type": "uint256" 91 | } 92 | ], 93 | "name": "Transfer", 94 | "type": "event" 95 | }, 96 | { 97 | "inputs": [ 98 | { 99 | "internalType": "address", 100 | "name": "to", 101 | "type": "address" 102 | }, 103 | { 104 | "internalType": "uint256", 105 | "name": "tokenId", 106 | "type": "uint256" 107 | } 108 | ], 109 | "name": "approve", 110 | "outputs": [], 111 | "stateMutability": "nonpayable", 112 | "type": "function" 113 | }, 114 | { 115 | "inputs": [ 116 | { 117 | "internalType": "address", 118 | "name": "_to", 119 | "type": "address" 120 | }, 121 | { 122 | "internalType": "uint256", 123 | "name": "_mintAmount", 124 | "type": "uint256" 125 | } 126 | ], 127 | "name": "mint", 128 | "outputs": [], 129 | "stateMutability": "payable", 130 | "type": "function" 131 | }, 132 | { 133 | "inputs": [ 134 | { 135 | "internalType": "bool", 136 | "name": "_state", 137 | "type": "bool" 138 | } 139 | ], 140 | "name": "pause", 141 | "outputs": [], 142 | "stateMutability": "nonpayable", 143 | "type": "function" 144 | }, 145 | { 146 | "inputs": [], 147 | "name": "renounceOwnership", 148 | "outputs": [], 149 | "stateMutability": "nonpayable", 150 | "type": "function" 151 | }, 152 | { 153 | "inputs": [ 154 | { 155 | "internalType": "address", 156 | "name": "from", 157 | "type": "address" 158 | }, 159 | { 160 | "internalType": "address", 161 | "name": "to", 162 | "type": "address" 163 | }, 164 | { 165 | "internalType": "uint256", 166 | "name": "tokenId", 167 | "type": "uint256" 168 | } 169 | ], 170 | "name": "safeTransferFrom", 171 | "outputs": [], 172 | "stateMutability": "nonpayable", 173 | "type": "function" 174 | }, 175 | { 176 | "inputs": [ 177 | { 178 | "internalType": "address", 179 | "name": "from", 180 | "type": "address" 181 | }, 182 | { 183 | "internalType": "address", 184 | "name": "to", 185 | "type": "address" 186 | }, 187 | { 188 | "internalType": "uint256", 189 | "name": "tokenId", 190 | "type": "uint256" 191 | }, 192 | { 193 | "internalType": "bytes", 194 | "name": "data", 195 | "type": "bytes" 196 | } 197 | ], 198 | "name": "safeTransferFrom", 199 | "outputs": [], 200 | "stateMutability": "nonpayable", 201 | "type": "function" 202 | }, 203 | { 204 | "inputs": [ 205 | { 206 | "internalType": "address", 207 | "name": "operator", 208 | "type": "address" 209 | }, 210 | { 211 | "internalType": "bool", 212 | "name": "approved", 213 | "type": "bool" 214 | } 215 | ], 216 | "name": "setApprovalForAll", 217 | "outputs": [], 218 | "stateMutability": "nonpayable", 219 | "type": "function" 220 | }, 221 | { 222 | "inputs": [ 223 | { 224 | "internalType": "string", 225 | "name": "_newBaseExtension", 226 | "type": "string" 227 | } 228 | ], 229 | "name": "setBaseExtension", 230 | "outputs": [], 231 | "stateMutability": "nonpayable", 232 | "type": "function" 233 | }, 234 | { 235 | "inputs": [ 236 | { 237 | "internalType": "string", 238 | "name": "_newBaseURI", 239 | "type": "string" 240 | } 241 | ], 242 | "name": "setBaseURI", 243 | "outputs": [], 244 | "stateMutability": "nonpayable", 245 | "type": "function" 246 | }, 247 | { 248 | "inputs": [ 249 | { 250 | "internalType": "uint256", 251 | "name": "_newmaxMintAmount", 252 | "type": "uint256" 253 | } 254 | ], 255 | "name": "setmaxMintAmount", 256 | "outputs": [], 257 | "stateMutability": "nonpayable", 258 | "type": "function" 259 | }, 260 | { 261 | "inputs": [ 262 | { 263 | "internalType": "address", 264 | "name": "from", 265 | "type": "address" 266 | }, 267 | { 268 | "internalType": "address", 269 | "name": "to", 270 | "type": "address" 271 | }, 272 | { 273 | "internalType": "uint256", 274 | "name": "tokenId", 275 | "type": "uint256" 276 | } 277 | ], 278 | "name": "transferFrom", 279 | "outputs": [], 280 | "stateMutability": "nonpayable", 281 | "type": "function" 282 | }, 283 | { 284 | "inputs": [ 285 | { 286 | "internalType": "address", 287 | "name": "newOwner", 288 | "type": "address" 289 | } 290 | ], 291 | "name": "transferOwnership", 292 | "outputs": [], 293 | "stateMutability": "nonpayable", 294 | "type": "function" 295 | }, 296 | { 297 | "inputs": [], 298 | "name": "withdraw", 299 | "outputs": [], 300 | "stateMutability": "payable", 301 | "type": "function" 302 | }, 303 | { 304 | "inputs": [], 305 | "stateMutability": "nonpayable", 306 | "type": "constructor" 307 | }, 308 | { 309 | "inputs": [ 310 | { 311 | "internalType": "address", 312 | "name": "owner", 313 | "type": "address" 314 | } 315 | ], 316 | "name": "balanceOf", 317 | "outputs": [ 318 | { 319 | "internalType": "uint256", 320 | "name": "", 321 | "type": "uint256" 322 | } 323 | ], 324 | "stateMutability": "view", 325 | "type": "function" 326 | }, 327 | { 328 | "inputs": [], 329 | "name": "baseExtension", 330 | "outputs": [ 331 | { 332 | "internalType": "string", 333 | "name": "", 334 | "type": "string" 335 | } 336 | ], 337 | "stateMutability": "view", 338 | "type": "function" 339 | }, 340 | { 341 | "inputs": [], 342 | "name": "baseURI", 343 | "outputs": [ 344 | { 345 | "internalType": "string", 346 | "name": "", 347 | "type": "string" 348 | } 349 | ], 350 | "stateMutability": "view", 351 | "type": "function" 352 | }, 353 | { 354 | "inputs": [ 355 | { 356 | "internalType": "uint256", 357 | "name": "tokenId", 358 | "type": "uint256" 359 | } 360 | ], 361 | "name": "getApproved", 362 | "outputs": [ 363 | { 364 | "internalType": "address", 365 | "name": "", 366 | "type": "address" 367 | } 368 | ], 369 | "stateMutability": "view", 370 | "type": "function" 371 | }, 372 | { 373 | "inputs": [ 374 | { 375 | "internalType": "address", 376 | "name": "owner", 377 | "type": "address" 378 | }, 379 | { 380 | "internalType": "address", 381 | "name": "operator", 382 | "type": "address" 383 | } 384 | ], 385 | "name": "isApprovedForAll", 386 | "outputs": [ 387 | { 388 | "internalType": "bool", 389 | "name": "", 390 | "type": "bool" 391 | } 392 | ], 393 | "stateMutability": "view", 394 | "type": "function" 395 | }, 396 | { 397 | "inputs": [], 398 | "name": "maxMintAmount", 399 | "outputs": [ 400 | { 401 | "internalType": "uint256", 402 | "name": "", 403 | "type": "uint256" 404 | } 405 | ], 406 | "stateMutability": "view", 407 | "type": "function" 408 | }, 409 | { 410 | "inputs": [], 411 | "name": "maxSupply", 412 | "outputs": [ 413 | { 414 | "internalType": "uint256", 415 | "name": "", 416 | "type": "uint256" 417 | } 418 | ], 419 | "stateMutability": "view", 420 | "type": "function" 421 | }, 422 | { 423 | "inputs": [], 424 | "name": "name", 425 | "outputs": [ 426 | { 427 | "internalType": "string", 428 | "name": "", 429 | "type": "string" 430 | } 431 | ], 432 | "stateMutability": "view", 433 | "type": "function" 434 | }, 435 | { 436 | "inputs": [], 437 | "name": "owner", 438 | "outputs": [ 439 | { 440 | "internalType": "address", 441 | "name": "", 442 | "type": "address" 443 | } 444 | ], 445 | "stateMutability": "view", 446 | "type": "function" 447 | }, 448 | { 449 | "inputs": [ 450 | { 451 | "internalType": "uint256", 452 | "name": "tokenId", 453 | "type": "uint256" 454 | } 455 | ], 456 | "name": "ownerOf", 457 | "outputs": [ 458 | { 459 | "internalType": "address", 460 | "name": "", 461 | "type": "address" 462 | } 463 | ], 464 | "stateMutability": "view", 465 | "type": "function" 466 | }, 467 | { 468 | "inputs": [], 469 | "name": "paused", 470 | "outputs": [ 471 | { 472 | "internalType": "bool", 473 | "name": "", 474 | "type": "bool" 475 | } 476 | ], 477 | "stateMutability": "view", 478 | "type": "function" 479 | }, 480 | { 481 | "inputs": [ 482 | { 483 | "internalType": "bytes4", 484 | "name": "interfaceId", 485 | "type": "bytes4" 486 | } 487 | ], 488 | "name": "supportsInterface", 489 | "outputs": [ 490 | { 491 | "internalType": "bool", 492 | "name": "", 493 | "type": "bool" 494 | } 495 | ], 496 | "stateMutability": "view", 497 | "type": "function" 498 | }, 499 | { 500 | "inputs": [], 501 | "name": "symbol", 502 | "outputs": [ 503 | { 504 | "internalType": "string", 505 | "name": "", 506 | "type": "string" 507 | } 508 | ], 509 | "stateMutability": "view", 510 | "type": "function" 511 | }, 512 | { 513 | "inputs": [ 514 | { 515 | "internalType": "uint256", 516 | "name": "index", 517 | "type": "uint256" 518 | } 519 | ], 520 | "name": "tokenByIndex", 521 | "outputs": [ 522 | { 523 | "internalType": "uint256", 524 | "name": "", 525 | "type": "uint256" 526 | } 527 | ], 528 | "stateMutability": "view", 529 | "type": "function" 530 | }, 531 | { 532 | "inputs": [ 533 | { 534 | "internalType": "address", 535 | "name": "owner", 536 | "type": "address" 537 | }, 538 | { 539 | "internalType": "uint256", 540 | "name": "index", 541 | "type": "uint256" 542 | } 543 | ], 544 | "name": "tokenOfOwnerByIndex", 545 | "outputs": [ 546 | { 547 | "internalType": "uint256", 548 | "name": "", 549 | "type": "uint256" 550 | } 551 | ], 552 | "stateMutability": "view", 553 | "type": "function" 554 | }, 555 | { 556 | "inputs": [ 557 | { 558 | "internalType": "uint256", 559 | "name": "tokenId", 560 | "type": "uint256" 561 | } 562 | ], 563 | "name": "tokenURI", 564 | "outputs": [ 565 | { 566 | "internalType": "string", 567 | "name": "", 568 | "type": "string" 569 | } 570 | ], 571 | "stateMutability": "view", 572 | "type": "function" 573 | }, 574 | { 575 | "inputs": [], 576 | "name": "totalSupply", 577 | "outputs": [ 578 | { 579 | "internalType": "uint256", 580 | "name": "", 581 | "type": "uint256" 582 | } 583 | ], 584 | "stateMutability": "view", 585 | "type": "function" 586 | }, 587 | { 588 | "inputs": [ 589 | { 590 | "internalType": "address", 591 | "name": "_owner", 592 | "type": "address" 593 | } 594 | ], 595 | "name": "walletOfOwner", 596 | "outputs": [ 597 | { 598 | "internalType": "uint256[]", 599 | "name": "", 600 | "type": "uint256[]" 601 | } 602 | ], 603 | "stateMutability": "view", 604 | "type": "function" 605 | } 606 | ] -------------------------------------------------------------------------------- /Part1/components/web3connect.js: -------------------------------------------------------------------------------- 1 | import { nftcontract } from "./config"; 2 | import NFTABI from './nftabi.json'; 3 | import { ethers } from "ethers"; 4 | import Web3Modal from 'web3modal'; 5 | 6 | export async function connectWallet() { 7 | const web3Modal = new Web3Modal(); 8 | const connection = await web3Modal.connect() 9 | const provider = new ethers.providers.Web3Provider(connection) 10 | const signer = provider.getSigner() 11 | const addressraw = signer.getAddress() 12 | const addressstr = (await addressraw).valueOf() 13 | let contract = new ethers.Contract(nftcontract, NFTABI, signer); 14 | let getids = await contract.walletOfOwner(addressstr); 15 | if (getids[0] === undefined){ 16 | return 0; 17 | } 18 | else { 19 | return 1; 20 | } 21 | } -------------------------------------------------------------------------------- /Part1/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /Part1/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function handler(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /Part1/pages/denied.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Image from 'next/image' 3 | import styles from '../styles/Home.module.css' 4 | import { connectWallet } from '../components/web3connect'; 5 | import { useRouter } from 'next/router'; 6 | import { useEffect } from 'react'; 7 | 8 | export default function Home() { 9 | const router = useRouter(); 10 | 11 | async function verifyUser() { 12 | const output = await connectWallet(); 13 | // console.log(output); 14 | if (output === 0) { 15 | router.push("/denied"); 16 | } else { 17 | router.push("/welcome"); 18 | } 19 | } 20 | 21 | useEffect(() => { 22 | const checkAuth = setInterval(() => { 23 | verifyUser(); 24 | }, 2000); 25 | return () => clearInterval(checkAuth); 26 | }, []); 27 | 28 | return ( 29 |
30 | 31 | Create Next App 32 | 33 | 34 | 35 | 36 |
37 |

38 | ACCESS DENIED 39 |

40 |
41 |
42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /Part1/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Image from 'next/image' 3 | import styles from '../styles/Home.module.css' 4 | import { connectWallet } from '../components/web3connect' 5 | import { useRouter } from 'next/router'; 6 | 7 | 8 | export default function Home() { 9 | 10 | const router = useRouter(); 11 | 12 | async function verifyUser() { 13 | const output = await connectWallet() 14 | if (output === 0) { 15 | router.push('/denied') 16 | } 17 | else { 18 | router.push('/welcome') 19 | } 20 | } 21 | 22 | return ( 23 |
24 | 25 | Create Next App 26 | 27 | 28 | 29 | 30 |
31 |

32 | Connect to App 33 |

34 | 35 |

36 | Get started by editing{' '} 37 | pages/index.js 38 |

39 | 40 |
41 | 42 |

Documentation →

43 |

Find in-depth information about Next.js features and API.

44 |
45 | 46 | 47 |

Learn →

48 |

Learn about Next.js in an interactive course with quizzes!

49 |
50 | 51 | 55 |

Examples →

56 |

Discover and deploy boilerplate example Next.js projects.

57 |
58 | 59 | 63 |

Deploy →

64 |

65 | Instantly deploy your Next.js site to a public URL with Vercel. 66 |

67 |
68 |
69 |
70 | 71 | 83 |
84 | ) 85 | } 86 | -------------------------------------------------------------------------------- /Part1/pages/welcome.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Image from 'next/image' 3 | import styles from '../styles/Home.module.css'; 4 | import { connectWallet } from '../components/web3connect'; 5 | import { useRouter } from 'next/router'; 6 | import { useEffect } from 'react'; 7 | 8 | export default function Home() { 9 | 10 | const router = useRouter() 11 | 12 | useEffect(() => { 13 | const checkauth = setInterval(() => { 14 | verifyUser() 15 | }, 2000) 16 | return () => clearInterval(checkauth) 17 | }) 18 | 19 | async function verifyUser() { 20 | const output = await connectWallet() 21 | if (output === 0) { 22 | router.push('/denied') 23 | } 24 | else { 25 | router.push('/welcome') 26 | } 27 | } 28 | 29 | 30 | return ( 31 |
32 | 33 | Create Next App 34 | 35 | 36 | 37 | 38 |
39 |

40 | Welcome! 41 |

42 |
43 |
44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web3-NFT-User-Authentication 2 | ⚡ A very straightfoward guide to authenticate users in NextJS with NFTs. Block users that aren't holding a specific ERC721 NFT 3 | 4 | 5 |

Part 1: Securing Apps with NFTs

6 | Refer to "Part 1" folder attached. 7 | --------------------------------------------------------------------------------