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: pgpassword
We 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 NFT
These 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 Elements
tab, 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": "[email protected]",
"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": "[email protected]",
"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);
});