Intro to OAuth with Node.js: OAuth 1.0 (One-Legged)

Introduction to OAuth with Node.js: Twitter API OAuth 1.0, OAuth 2.0, OAuth Echo, Everyauth and OAuth 2.0 Server Examples

This text is part of Introduction to OAuth with Node.js mini-book which is available at gum.co/hRyc.

Introduction to OAuth with Node.js

Introduction to OAuth with Node.js: Twitter API OAuth 1.0, OAuth 2.0, OAuth Echo, Everyauth and OAuth 2.0 Server Examples

Let’s start with good old OAuth 1.0. The way it usually works is as follows:

  1. For the first time, when we authorize a user to use our app, we need to perform extra work and obtain access token and secret (three-legged).
  2. You store these values for each user in your application.
  3. Then, on subsequent requests, things become much simpler. We construct auth headers and make HTTP requests (one-legged).

In this chapter, will be dealing only with those later, one-legged requests, as shown in Figure 1–1, while the former three-legged approach will be covered in the Everyauth chapter, i.e., building from simple to more complex.

Figure 1–1: OAuth 1.0A (one-legged) schema

Figure 1–1: OAuth 1.0A (one-legged) schema

Twitter provides OAuth tools to generate tokens and secrets for your own apps. This is what we’ll be using for requests. You’ll need four values to make an OAuth 1.0 request to Twitter API v1.1, or any other service using OAuth 1.0:

  1. Application key, a.k.a. consumer key
  2. Application secret key
  3. User token for this application
  4. User secret for this application

All four of them can be obtained for your own apps at dev.twitter.com, as shown in Figure 1–2.

Figure 1–2: dev.twitter.com home page

Figure 1–2: dev.twitter.com home page

Just to reiterate, in case you are not the user (which is the most common use case), you’ll need to perform a three-legged OAuth / Sign in with Twitter, or something else. The result of Sign in with Twitter is the response with the user’s token and secret, which can be store in the app database for later usage. For more information, follow the examples in the Everyauth chapter.

Going back to obtaining the keys, click on “Sign in” located in the right upper corner and you’ll see a list of your Twitter applications, if you have any. Your own apps refers to the apps that you have created using your Twitter account. For example, there are three apps shown in Figure 1–3.

Figure 1–3: Twitter API apps

Figure 1–3: Twitter API apps

For the sake of this exercise, it’s easier to simply create a brand new app and get tokens and secrets for it. Click on Create New App from Figure 1–3’s screenshot and it will take you to the page Create an application, as shown in Figure 1–4.

Fill in the values for Name, Description, Website and Callback URL (it doesn’t matter what it is, as long as it starts with http://). The callback value can be anything, but if it’s empty, the Twitter app will be locked into the OOB mode, which will prevent us from testing the OAuth Sign-in later (you’ll see Desktop applications only support the OAuth_callback value 'oob' error). For this reason, put any URL in the callback field, e.g., http://webapplog.com.

Figure 1–4: Create a Twitter application

Figure 1–4: Create a Twitter application

Once the app is created, go to the Permission tab, and change the permission to Read, Write and Access direct messages. This will result in a new value for the Access level field of the API Keys tab, as shown in Figure 1–5. From the same tab, click the button Create my access token. After generation is finished, the results will be in the Your access token section, as show in Figure 1–5. Note that your keys will differ from the illustration.

Figure 1–5: Access token and secret for your own app

Figure 1–5: Access token and secret for your own app

After the values are generated (it may take some time), copy and store these values:

  1. API key: twitterKey in the Node.js script
  2. API secret: twitterSecret in the Node.js script
  3. Access token: token in the Node.js script
  4. Access token secret: secret in the Node.js script

There are many Node.js OAuth modules and even Node.js Twitter SDKs (wrappers for the Twitter API). However, to understand the basics of making OAuth requests, we’ll be using the most minimalistic module, oauth. This module will help us to construct auth headers for HTTP requests without the necessity of having to understand all the complexities required to build the headers.

To authorize a request, it must have certain parameters in its Authorization header (docs), for example:

OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog", oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1318622958", oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", oauth_version="1.0"

Let’s take a brief look at each of these parameters:

[Sidenote]

Reading blog posts is good, but watching video courses is even better because they are more engaging.

A lot of developers complained that there is a lack of affordable quality video material on Node. It's distracting to watch to YouTube videos and insane to pay $500 for a Node video course!

Go check out Node University which has FREE video courses on Node: node.university.

[End of sidenote]

  • oauth_consumer_key: this is the same as the application key or Twitter consumer key, i.e., the unique identifier of your app
  • oauth_nonce: a unique identifier of your request, usually constructed with some random param and hashing algorithm
  • oauth_timestamp: current time in seconds, since the Unix epoch
  • oauth_version: should always be 1.0 for OAuth 1.0
  • oauth_signature_method: should be HMAC-SHA1
  • oauth_signature: a hashed combination of all the other parameters

The exact generation of these parameters is outside the scope of this introductory book. Luckily, there are many modules that can do this legwork for us, i.e., oauth.

To install the oauth module, you can use:

$ npm install oauth@0.9.12

In the oauth1/index.js file, we define the variables for the key, token, and secrets. Replace the string placeholders with your values:

var twitterKey = 'your Twitter application consumer key',
  twitterSecret = 'your Twitter application secret',
  token = 'your user token for this app',
  secret = 'your user secret for this app';

The following is simply an example of how key formats look. Please use your own, because these values will be invalidated:

twitterKey = 'A3UP583JRnLWuAdeMUBJjzOn2';
twitterSecret = 'tQEu1d0cMU43hk2hk5J77UhIweJ7z4oEPVl6OhxbMRONtLMLMG';
token = '575632191-r98rIwyKnAxTHFt86bKX5ZaFeYRzmfOF5JbaPyPI';
secret = 'eryuMTstgYrcerrL43XwVX1EAOJMTdQxvGKHu5UUSiBY7';

Going back to our file (oauth1/index.js), we import the OAuth class and create an object for OAuth 1.0 where the parameters are Twitter API endpoints, key, and secret; version (1.0A); and encryption method (HMAC-SHA1):

var OAuth = require('OAuth');
var oauth = new OAuth.OAuth(
  'https://api.twitter.com/oauth/request_token',
  'https://api.twitter.com/oauth/access_token',
  twitterKey,
  twitterSecret,
  '1.0A',
  null,
  'HMAC-SHA1'
);

After we created the oauth object using the parameters, we can call aouth.get() function to fetch a secured resource. The function accepts the URL, user token, user secret (don’t confuse this secret with the Twitter secret), and the callback:

oauth.get(
  'https://api.twitter.com/1.1/trends/place.json?id=23424977',
  token,
  secret,
  function (error, data, response){
    if (error) console.error(error);
    data = JSON.parse(data);
    console.log(JSON.stringify(data, 0, 2));
});

Behind the scenes, the get() function constructs unique values for the request header — Authorization header. The method encrypts the URL, timestamp, application, and other information in a signature, so the same header won’t work for another URL or after a specific time window.

In the callback function, we parse the data string into a JavaScript object and then print it with indentation using JSON.stringify parameters. The last argument of the callback, response, will have the response information with the headers.

The full source code of the oauth1/index.js:

var twitterKey = 'your Twitter application consumer key',
  twitterSecret = 'your Twitter application secret',
  token = 'your user token for this app',
  //you can get it at dev.twitter.com for your own apps
  secret = 'your user secret for this app';
  //you can get it at dev.twitter.com for your own apps

var OAuth = require('OAuth');

var oauth = new OAuth.OAuth(
  'https://api.twitter.com/oauth/request_token',
  'https://api.twitter.com/oauth/access_token',
  twitterKey,
  twitterSecret,
  '1.0A',
  null,
  'HMAC-SHA1'
);

oauth.get(
  'https://api.twitter.com/1.1/trends/place.json?id=23424977',
  token,
  secret,
  function (error, data, response){
    if (error) console.error(error);
    // data = JSON.parse(data);
    // console.log(JSON.stringify(data, 0, 2));
    console.log(response);
});

When you run the file with $ node index.js, you should see the data from the protected Twitter API endpoint, e.g., the top ten trending topics for a given location (WOEID lookup) via the trends/place endpoint:

https://api.twitter.com/1.1/trends/place.json?id=23424977

The trends docs are available at https://dev.twitter.com/docs/api/1.1/get/trends/place. The result might look like this:

[
  {
    "trends": [
      {
        "name": "#WorldCup",
        "query": "%23WorldCup",
        "url": "http://twitter.com/search?q=%23WorldCup",
        "promoted_content": null
      },
      {
        "name": "Kershaw",
        "query": "Kershaw",
        "url": "http://twitter.com/search?q=Kershaw",
        "promoted_content": null
      },
      {
        "name": "#RE2PECT",
        "query": "%23RE2PECT",
        "url": "http://twitter.com/search?q=%23RE2PECT",
        "promoted_content": null
      },
      ...
            {
        "name": "Starbucks",
        "query": "Starbucks",
        "url": "http://twitter.com/search?q=Starbucks",
        "promoted_content": null
      }
    ],
    "as_of": "2014-07-14T17:45:52Z",
    "created_at": "2014-07-14T17:41:24Z",
    "locations": [
      {
        "name": "United States",
        "woeid": 23424977
      }
    ]
  }
]

--
Best Regards,
Azat Mardan
Microsoft MVP | Book and Course Author | Software Engineering Leader
Azat Mardan avatar
https://www.linkedin.com/in/azatm
To contact Azat, the main author of this blog, submit the contact form or schedule a call at clarity.fm/azat and we can go over your bugs, questions and career.

3 thoughts on “Intro to OAuth with Node.js: OAuth 1.0 (One-Legged)

  1. Azat Post author

    Good point. Yes, technically it’s a three-legged OAuth 1.0, but we manually side-step the first two steps (for the ease of presenting). There’s nothing wrong with it. Token and secret are always temporary and can be invalidated. With Twitter API this approach will work after 10 seconds— I tested many times. ;-)

    If you read the book (or book’s Table of Contents) the normal three-legged OAuth 1.0 is also covered there.

  2. Pooyan

    Searching the web suggests that there is no 1-legged oAuth flow among all versions of oAuth.
    Can you link to specification for oAuth 1.0 1-legged flow?

    BTW what you’re describing is 3-legged and you’re doing authentication part manually. This is WRONG. Both user token and secret are temporarily data, they’re subject to expiration, invalidation etc and it is NOT guaranteed that your app will be able to use this data ten seconds from now.

  3. Cristian

    I think the concept of three legged vs one legged is not accurately described in the post.

    The three legged token is the one that includes information about: User, Developer and Application.
    On the other hand a 2-Legged token is one that includes information about Developer and Application, usually those tokens are issued by an API Management Service such as APIGee or Mashery.

    The process you are describing is a common developer enrolment process to start using an API.

    I hope my comment helps.

    Cristian

Leave a Reply

Your email address will not be published. Required fields are marked *