OAuth and API Authentication in Smart Contracts

OAuth is a popular form of API authentication and authorization that allows users to access different websites and applications without sharing their credentials. At first, it might seem difficult to get data from an external, Web2 service like OAuth into our blockchain smart contracts. However, Chainlink external adapters make it easy to perform difficult computation off-chain, and it’s no different with API authentication like OAuth. External adapters make it possible to use OAuth to access more secure external data sources and easily interact with them from our smart contracts on-chain. We can call these secure APIs from our Solidity or other smart contracts to access multiple services that have this high level of security. In this post, we’ll walk through an example of a Reddit external adapter.

What is OAuth?

When interacting with the internet, you often have to prove who you are in order to access data. The simplest way to prove who you are is with usernames and passwords. This is known as password or basic authentication methodology. However, we could think of another way to prove online that we are who we claim to be in the real world, like having someone else vouch for us. This is basically OAuth: it’s a form of delegated token authentication, meaning one party grants authentication to another.

OAuth works when two parties engage a trusted third party to prove who the identity of one of them via a provided digital token.

Let’s say Bob wants to get data from Alice, but doesn’t want to give Alice his password or tell her who he is. Bob and Alice are both friends with Margaret. Margaret tells Bob that she can give him a temporary token to give to Alice to get his data, and Alice doesn’t need Bob’s name or any of his information. All Alice knows is that someone they trust is getting the data. Margaret lends Bob a token to get access with Alice, like lending someone your hotel room key. This is a really simple explanation of how OAuth works.

When programming systems with OAuth, it therefore involves an extra step to get access to the data that you’re looking for, since you first have to wait for a response from the trusted third party to continue. With basic authentication, you can just show up with your password and be accepted or rejected at the door. With OAuth authentication, you have to wait for your token from the third party, and then you can use that to be rejected or accepted at the door.

As we know, blockchains like Ethereum (1.0) are synchronous in how they execute processes. This means they can only do one thing at a time, and it can be a little clunky to have to wait for tokens in order to make API calls back. It’s also not very gas-efficient for our Solidity code to have to wait for a token to be passed back to us before making another call. A great way to handle all this would be in one fell swoop off-chain with a Chainlink external adapter, making smart contract OAuth faster and less costly in terms of gas.

OAuth in Nodejs

When building our external adapter, which handles the OAuth authorization from our Solidity smart contract, we first need to decide if we want to build our own OAuth handler or if we want to use someone else’s. An OAuth handler is simply a piece of code that allows us to handle signing in and out easily. Most of the time, if there’s a working solution out there, we don’t want to reinvent the wheel. For platforms that use OAuth, you can usually find one out there already. For example, we found this fantastic Reddit handler, and we can even run through the code to see what it does.

We have two main functions:

async _getToken ()

and

_makeRequest (method, url, data, token)

The _getToken() as you can imagine gets the token, and _makeRequest is our function that actually sends the final authentication request to a Reddit URL along with the token. In this implementation, they both get wrapped up and called by _sendRequest.

We can see that the _getToken () function actually interacts with the third party by using the basic authentication.

return new Promise((resolve, reject) => {
      get.concat({
        url: TOKEN_BASE_URL,
        method: 'POST',
        form: {
          grant_type: 'password',
          username: this.username,
          password: this.password
        },
        headers: {
          authorization: `Basic ${Buffer.from(`${this.appId}:${this.appSecret}`).toString('base64')}`,
          'user-agent': this.userAgent
        },
        json: true,
        timeout: REQUEST_TIMEOUT
      }, (err, res, body) => {
        if (err) {
          err.message = `Error getting token: ${err.message}`
          return reject(err)
        }

And the _makeRequest () function uses the token instead of a password.

return new Promise((resolve, reject) => {
      const opts = {
        url: url,
        method: method,
        headers: {
          authorization: token,
          'user-agent': this.userAgent
        },
        timeout: REQUEST_TIMEOUT
      }

We can see why it’s often preferable to use an implementation that is already complete, but it’s important to know how it works so we can build it ourselves whenever we need.

Once we have our code all set for the OAuth, we can then fill in the boilerplate for the external adapter! You can always use whichever adapter you’d like, but we will use the Chainlink External Adapter Template. If you followed along with the building external adapters blog, the rest of this is easy! We could reinvent the wheel and copy paste all this code into our external adapter, but it’s much nicer being able to import it, so we can stay focused on our Solidity and smart contract code, as opposed to authentication.

The Reddit External Adapter

Now that we have this OAuth handler setup, we can add it to our Chainlink External Adapter. This works like any of the other Chainlink adapters. We can add this adapter in our list and perform whatever computation using the OAuth authorization that we need. Looking into the code for the Reddit External adapter, we can see that we follow the exact framework from the first external adapters tutorial. As with our previous blog post on external adapters, we only need to update the code in index.js. The main difference is we are adding a new package, in this case the Reddit package with:

const Reddit = require('reddit')

And we are adding all our credentials like so:

const reddit = new Reddit({
  username: process.env.REDDIT_USER,
  password: process.env.REDDIT_PASSWORD,
  appId: process.env.REDDIT_API_KEY,
  appSecret: process.env.REDDIT_API_SECRET,
  userAgent: 'tweether',
})

Once you create an app from the Reddit site, you’ll get your REDDIT_API_KEY and REDDIT_API_SECRET to use in the adapter. The external adapter has a number of inputs that we can use to customize what we want to send to Reddit from our smart contract.

const customParams = {
  sr: false,
  kind: false,
  resubmit: false,
  title: false,
  text: false,
  endpoint: false,
  url: false,
}

These custom parameters are all identified in the Reddit API Documentation.

We make one additional major difference from our template: instead of sending off our request with the Requester object, we use the Reddit object with:

 reddit
    .post(endpoint, {
      sr: sr,
      kind: kind,
      resubmit: resubmit,
      title: title,
      text: text,
      url: url,
    })
    .then((response) => {
      response.json.data.result = response.json.data.id
      response.json.status = 200
      callback(response.json.status, Requester.success(jobRunID, response.json))
    })
    .catch((error) => {
      callback(500, Requester.errored(jobRunID, error))
    })

You can test it out now yourself after you make an app!

After setting those four environment variables, simply run:

git clone https://github.com/tweether-protocol/reddit-cl-ea
cd reddit-cl-ea
yarn
yarn start

And from another shell you can test it out with:

curl -X POST -H "content-type:application/json" "http://localhost:8080/" --data '{ "id": 0, "data": {"title":"HELLO" }}'

And you can check the output to see what you’ve posted to Reddit!

If you get a chance, you can check out the Twitter Chainlink External Adapter, which allows you to post statuses based on your smart contract interactions.

This is just the first step to interacting with your smart contract via OAuth and Reddit. You’ll need to work with a node that hosts the external adapter and set the credentials in the node. Be sure to check out the Chainlink docs on how to take it to the next step, and if you build something amazing that uses OAuth with your smart contracts, be sure to upload it to market.link so you can help others interact with the real world from their smart contracts. It’s also a great way to show off your smart contract building expertise.

If you’re a developer and want to connect your smart contract to off-chain data and systems, visit the developer documentation and join the technical discussion on Discord. If you want to schedule a call to discuss the integration more in-depth, reach out here.

Website | Twitter | Reddit | YouTube | Telegram | Events | GitHub | Price Feeds | DeFi

Need Integration Support?
Talk to an expert
Faucets
Get testnet tokens
Read the Docs
Technical documentation