MetAPP is an air-gapped service to securely authorize the use of NFTs without acting on a web3 wallet. MetAPP has a front-end to create ownership proofs and a public API that anyone can use to integrate into their projects and verify ownership proof.
MetAPP is a solution created with β€οΈ and:
The MetAPP Monorepo is a ready-to-code repository fully integrated to Gitpod. You can start coding now by visiting the Gitpod Workspace below:
Gitpod Workspace Ready-to-Code
Once you open the gitpod, it will launch the services listed below, it takes ~2 minutes if it is your first time running it:
- PostgreSQL Database at
Port 5432 - Adminer Database Visualizer at
Port 8080 - NestJS API Server at
Port 3001 - MetAPP Front-End at
Port 3000**
** The Front-End will open in a new tab once it is ready, however your browser may block it from opening.
Once all services have started, you can see the list of available ports going to the PORTS tab next to the terminal window. It is important to note that The Gitpod instance will close automatically after 30 minutes of inactivity.
If after you run Gitpod for the first time, the front-end of MetAPP doesn't open in a new tab, you can go ahead and click on the link available in the PORTS tab on Gitpod, as the figure above shows. You should be able to see the following dApp:
You can either LOGIN or SIGN UP in the Dapp:
After successfully logging in, the next step will ask the user to connect to a Metamask Wallet holding the NFTs that will be verified.
metapp_metamask_login.mp4
The DApp is currently compatible with:
- Ethereum Goerli Testnet
- Polygon Mumbai Testnet
The AuthBank Smart contracts that manage the validation of ownership are deployed at:
For that reason, in order to proceed with the next steps, you need to hold a ERC721 compatible token on either GOERLI TESTNET or MUMBAI TESTNET. By clicking in the activation button of any of the NFTs you currently hold. A Metamask confirmation window pops-up:
Once the transaction is confirmed on the blockchain. Two things will happen:
- The Validation is written to the Database;
- The Validation is written to the Blockchain;
Validations expire within 7 days and lose validity once the NFT change hands to prevent abuse. These validations will be made available through API and the user don't have to use his web3 wallet to access the validation proof, only his account name and password.
To better visualize and manage what is happening in the backend of our application, we also serve a lightweight database management tool called Adminer, adminer is available on port 8080 and you can find a link to the service on the PORTS tab in the Gitpod terminal window.
Once you click on the service, a the following window should show up:
To loging to the database, we can use the information described in the ./backend/docker-compose.yml for our PostgreSQL container:
System: PostgreSQL
Server: pgsql
Username: pguser
Password: pgpasswordWe want to see the info we are writting in the postgres_auth database:
Once we click in postgres_auth we have access to two tables: Users and Assets. Clicking in either select Asset or select User should show the user and the asset we created in the π° Frontend - Using MetAPP to validate ownership section:
An API is served at PORT 3001 so we can fetch the data from NFTs validated with the front-end served @ PORT 3000. This API is public and any user can use it given it has a valid account that can be created in the dapp or in the API itself. The following routes are available:
POST {API_URL}/login # Route used for logging in, it returns a JWT token that must be used to access the other routes
GET {API_URL}/info # Route used to get user info such as email, name, role and associated wallets
POST {API_URL}/users/register # Public route used to register a new user
GET {API_URL}/getall # Protected route (Admin only) to get all users data
POST {API_URL}/wallet # Link a new wallet to user
GET {API_URL}/wallet # Get all wallets from user
DEL {API_URL}/wallet # Delete all wallets from user
POST {API_URL}/assets # Create a new ownership proof on the database
GET {API_URL}/assets # Get all ownership proofs for a given JWT bearer
DEL {API_URL}/assets # Delete a ownership proof for a given NFTThese endpoints and the correct configuration to use them are available as a Postman Collection. To import it and start using it, take the following steps:
- With Postman Open, in your Workspace, click in Import:
- Select
Link, paste the link below, and click inContinue:
https://www.getpostman.com/collections/e59d40ab627af9bfad88- In the
Import Elementstab, go ahead and click inImport
If you are using Gitpod, remember to change the default http://localhost:3001/ URL for the URL provided in the PORTS tab.
- Creating a new User using NodeJs:
var axios = require('axios');
var data = JSON.stringify({
"name": "admin",
"email": "admin@mail.com",
"password": "admin"
});
var config = {
method: 'post',
url: 'http://localhost:3001/users/register', // Remember to change the URL if using Gitpod
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
maxRedirects: 0,
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});- Logging In using NodeJs:
var axios = require('axios');
var data = JSON.stringify({
"email": "admin@mail.com",
"password": "admin"
});
var config = {
method: 'post',
url: 'http://localhost:3001/login', // Remember to change the URL if using Gitpod
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
maxRedirects: 0,
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});- Accessing protected routes using NodeJs ( wallet, assets, info ):
var axios = require('axios');
var AuthToken = 'YOUR_JWT_TOKEN'; // A JWT token generated from Login API
var data = '';
var config = {
method: 'get',
url: 'http://localhost:3001/info',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${AuthToken}`,
},
maxRedirects: 0,
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});













