🔐 Creating a Crypto Token Gated Express application
Tags: nft, web3, express, ethereum
Published: 2021-11-03 13:34:48

---
title: 🔐 Creating a Crypto Token Gated Express application
published: true
description: In this post I will show you how to create a simple application with some token gated content: content that is only available to people who have a valid NFT membership!
tags: nft, web3, express, ethereum
//cover_image: https://direct_url_to_image.jpg
---

Web3 encompasses a plethora of elements that empower us to build a more accessible web for everyone -- including Non-Fungible-Tokens, Token Gating, and decentralization. One perk from the emergence of web3 is moving away from the monetization of our personal data to sell ads. Win-win for us all!

I'm here to show you how simple the process is for token gating content in an Express.js application using the [Ʉnlock Protocol](https://unlock-protocol.com/). Ʉnlock is a protocol for memberships that allows anyone to deploy a "lock" (a membership smart contract). The contract specifies the *terms* of the membership: including price, duration, number of members, and many more parameters necessary to run a thriving membership subscription. When a supporter wants to join your membership, they simply pay the specified amount to the contract and will receive an NFT that represents the purchased membership.


```javascript
app.get('/premium', membersOnly(), (req, res) => {
  // ... unchanged
})
``` 
TL;DR: that's all you need to monetize your application!
[Try it](http://locked-time.herokuapp.com/) | [Code](https://github.com/julien51/locked-time)

**🚀 Let's get started!**

_Requirements_: you need to use an ethereum/web3 wallet such as [MetaMask](https://metamask.io/). We will be using a the Rinkeby test network for this tutorial (no need to spend _real_ money, get some fake Ether [on this faucet](https://faucet.rinkeby.io/)), and when you decide to ship, you can use Ethereum's mainnet, or the [xDAI](https://unlock-protocol.com/blog/xdai) and [Polygon](https://unlock-protocol.com/blog/unlock-on-polygon) networks if you want to skip the high gas fees.

<img src="https://github.com/unlock-protocol/unlock/blob/master/design/brand/PNG/Unlock-LogoMark-Circle.png?raw=true" />


## Deploying a lock 🔒 

This is the first step. Unlock Inc provides a front-end for this to be a bit easier, but know that you could also do it directly from the smart contracts.

[Go to the Unlock Dashboard](https://app.unlock-protocol.com/dashboard). Connect your wallet and then click on the "Create Lock" button.

You can then complete the form to specify the name of your membership contract, the duration (in days) of each membership, the amount of members, as well as the price for each membership.

![Dashboard Example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8awt3uzh2vnpdkwflcr5.png)
 
Clicking on "Submit" will prompt you to submit a transaction that will deploy your lock! It usually does not take much more than a few seconds but you will be able to see the address of your lock right away. This is the address of the smart contract you just deployed! It is yours, and only yours.

The one I deployed is called "Another Lock", and its memberships are valid for 1 day, and they cost 0.001 Ether. It is at the address `0x32BeC8e1Fc72509fFa115bd6a0f064Ec77319E4A` and we can [inspect it on Etherscan](https://rinkeby.etherscan.io/address/0x32BeC8e1Fc72509fFa115bd6a0f064Ec77319E4A).

![Deployed Lock](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/liqijhj24miky8dpz7vk.png)

The side bar shows lets you enable credit cards 💳 (for users who do not have a crypto wallet, withdraw its funds, and more... feel free to tinker with it!)

## Express application for date and times

_If you're familiar with Express, you won't learn much so skip to next section!_

For this demo, I created a dummy [Express](https://expressjs.com/) application that gives the time ⏰ in all timezones. The "free" version just includes 4 cities around the world, but the premium version offers a list of 457 cities!

Here is how the code looks like:

```javascript
const express = require('express')
const cookieParser = require('cookie-parser')
const zones = require('./zones')

const app = express()
const port = process.env.PORT || 3000;

app.use(cookieParser())

// Helper function that returns date as a string in a city
const dateInZone = (zone, city) => {
  const d = new Date();
  return `${d.toLocaleString('en-US', { timeZone: zone })} in ${city}`
}

// Main root of the application that only shows 4 cities
app.get('/', (req, res) => {
  res.send(`<h1>World Clocks</h1>
  <p>It is now: </p>
  <ul>
    <li>${dateInZone("Asia/Tokyo", "Tokyo")}</li>
    <li>${dateInZone("Asia/Kolkata", "Kolkata")}</li>
    <li>${dateInZone("Europe/Paris", "Paris")}</li>
    <li>${dateInZone("America/New_York", "New York")}</li>
  </ul>
  <p>Premium version: <a href="/premium">select any time zone</a>!</p>`)
})

// Premium route where times in all timezones are displayed!
app.get('/premium', (req, res) => {
  res.send(`<h1>World Clocks</h1>
  <p>Thank you for your support!</p>
  ... (check github for code that renders all zones!)
  <p>Premium version: select any time zone!</p>`)
})

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})
```

As you can see it is a pretty standard Express application. The list of zones is stored in `zones.js` file that you can [find on the repo](https://github.com/julien51/locked-time/blob/master/zones.js).

## Adding the lock

There are multiple ways to [integrate Ʉnlock in application](https://docs.unlock-protocol.com/developers/unlock-platform), from the lowest levels by hooking into smart contracts directly, all the way to the front-end where some JavaScript can be used to hide/show content. 

Here we will use the `@unlock-protocol/unlock-express` npm module. This module provides a middleware that can be added to any Express route in order to ensure that members only can indeed access the content.

Add the plugin through `yarn add @unlock-protocol/unlock-express` or `npm i --save @unlock-protocol/unlock-express`, and then let's include it in our application:

```javascript
const configUnlock = require('@unlock-protocol/unlock-express')
```

After this, it's just a matter of configuring the express and using the middleware. The configuration has 3 important elements:

* `yieldPaywallConfig`
* `getUserEthereumAddress`
* `updateUserEthereumAddress`

Let's go through them one by one.

### `yieldPaywallConfig`

This one is used by the plugin to ask the application what configuration it should use. The main aspect of this is of course to list what "locks" should be used specifically. You can find more details [in the Unlock docs](https://docs.unlock-protocol.com/developers/paywall/configuring-checkout#the-paywallconfig-object), but here is what we'll use:

```javascript
    yieldPaywallConfig: (request) => {
      return {
        locks: {
          '0x32BeC8e1Fc72509fFa115bd6a0f064Ec77319E4A': {
            network: 4,
          },
        },
      }
    },
```

Quite simply, we say that we only have a single lock that we accept memberships from and it is on the network `4` (the Rinkeby test network).

### `getUserEthereumAddress`

This one is a way for the middleware to ask the application for the user's Ethereum address, if it knows about it. Here for example, the application could look up its user database and just yield the address of the user... etc. However, in this example, we do not have a user database/table, so we will only use a cookie. So here is what that looks like:

```javascript
    getUserEthereumAddress: async (request) => {
      return request.cookies.userAddress
    },
```

### `updateUserEthereumAddress`

Finally, the middleware has a way to inform the application of the user's wallet address, so that the application can store it. There again, the application could decide to store that information in a database or, like we do it here, just store the address in a cookie. 

```javascript
    updateUserEthereumAddress: async (
      request,
      response,
      address,
      message
    ) => {
      response.cookie('userAddress', address)
    },
```

And that's it, here is how the full configuration looks like:

```javascript

const { membersOnly } = configureUnlock(
  {
    yieldPaywallConfig: () => {
      return {
        locks: {
          '0xafa8fE6D93174D17D98E7A539A90a2EFBC0c0Fc1': {
            network: 4,
          },
        },
      }
    },
    getUserEthereumAddress: async (request) => {
      return request.cookies.userAddress
    },
    updateUserEthereumAddress: async (
      request,
      response,
      address,
    ) => {
      response.cookie('userAddress', address)
    },
  },
  app
)
```

💡 Please note that the configuration block yields the `membersOnly` middleware that we'll use in the last step!

### Adding the middleware to our premium route

Now that the middleware exists, we can very easily plug it into our premium route and here is how it looks like:

```javascript
app.get('/premium', membersOnly(), (req, res) => {
  // ... unchanged
})
```

✅ As you can see, it's quite trivial! 

## Final touches

A nice addition is to let the user "log out" by just deleting the cookie. For this, we're adding a route:

```javascript
app.get('/logout', (req, res) => {
  res.clearCookie('userAddress')
  res.redirect('/')
})
```

🧪 Want to try it out? We deployed the application [on Heroku](http://locked-time.herokuapp.com/).
🧐 Want to look at the code? It is all on [Github](https://github.com/julien51/locked-time)!

_A note on security_: one could note that the cookie itself could be spoofed. This is true, but also easily fixable using multiple approaches. One of them is to change how `updateUserEthereumAddress` is implemented by using the 4th and 5th arguments passed to this function that include a signature and the message signed by the user. By recovering the signature, the application can get the _certainty_ that the user's address actually matches the visitor (especially if the message signed includes a timestamp or some kind of unique nonce, configurable in the paywall config).

## 👋 Closing thoughts!

Using this [express plugin](https://www.npmjs.com/package/@unlock-protocol/unlock-express) means that a developer can start monetizing their application from anywhere in the world, in a matter of minutes. There is no need to agree to terms of service, apply for an API key and no approval or review of your application!

Additionally you as a developer can decide of the benefits you want to offer to your members, whether its access to unique content, special features. One of my favorite feature is that the locks themselves are not even tied to a specific platform. For example, if you decide to do support over Discord, you could use a bot like [SwordyBot](https://swordybot.com/) to easily identify paying members (or grant them access to a special channel!)

![Unlock Showcase](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1qjuxc3vwbcsowmxg8ex.png)
[Examples of Projects using Unlock](https://www.unlockshowcase.com/)!


If you enjoyed this and want to learn more, please [join the Unlock Discord](https://discord.com/invite/Ah6ZEJyTDp). I would also recommend that [you apply for an Unlock grant](https://docs.unlock-protocol.com/governance/grants-bounties-and-matchings)!

👩‍💻 Oh! And we're [hiring a Dev Evangelist](https://www.notion.so/unlockprotocol/Unlock-Jobs-907811d15c4d490091eb298f71b0954c). Is that you?