This week I’ve had a bit of a play with creating some apps that run in the terminal using Node.js. Partly inspired by my previous blog where I created a console tool to output a days commit messages in their respected branches.
The goal of this blog is to create a console application from which I can send tweets. But to do that, I’m going to need some way to auth.
Pin-based OAuth
For a console application, we need something a little different to the traditional 3-legged OAuth as we don’t have a callback URL. So we will set the value for oauth_callback
to oob
, which stands for “out of band” as to say, non-traditional. Once the user grants permissions to your application through a browser, Twitter will provide them with a 7-digit PIN, which they will copy and paste into your application. Your application will then plug that number into the final POST oauth/access_token
request.
This should all make a lot more sense as we go through it.
Testing with postman
I’m assuming you have a Twitter application, if not just go to the Twitter app dashboard and create one.
Next, go to Keys and Tokens and generate some consumer keys, and store them somewhere.
1. Obtain request token
Create POST https://api.twitter.com/oauth/request_token
request in Postman with the Authorization type OAuth 1.0:
Make sure to fill in your Consumer Key
and your Consumer Secret
with the keys/secrets, we just generated in Twitter. And importantly, add oob
as the Callback URL

Running that should give you something like:
oauth_token=iZ90iwAAAAABTB3vABBBABe4xmP2U&oauth_token_secret=L1ASPRVlTNBfAipyphRegZIDQrY7Xlwi&oauth_callback_confirmed=true
We’re gonna need the oauth_token
and oauth_token_secret
.
2. Redirect the user
Next, open up the page https://api.twitter.com/oauth/authorize?oauth_token=YOUR_OAUTH_TOKEN_HERE
Which should look something like this:

Hit Authorize App and you’ll be provided with a code:

We’re gonna need that number for the next postman request.
3. Get the user access token
We need the user access token so we can post tweets on their behalf. Create a new POST https://api.twitter.com/oauth/access_token?oauth_token=OAUTH_TOKEN_HERE&oauth_verifier=PIN_HERE
The response should look something like this:
oauth_token=1024229588069765120-VsTP0ELWFAY0H5LN2nbTVEwkXcpWkp&oauth_token_secret=U3uhlwQrDSLR9SLZtDKRofNcUcwXMtY6zAqJWvpm9PC7&user_id=1124229588069765120&screen_name=luke_garrigan
Now we have the auth token and secret we can send tweets on behalf of the user!
4. Sending a tweet
Create a POST https://api.twitter.com/1.1/statuses/update.json?status=TWEET_HERE

Plug in your Consumer Key
and Consumer Secret
(The ones generated in your Twitter app) and set the Access token
and Token Secret
for the user (The ones we just go from the previous request).
Et voila:

Pin OAuth with Node.js – To send a tweet
It’s all well and good doing it in Postman but how do we transfer that to code? Quite easily actually. I’ve created a GitHub repo just for the Pin OAuth.
Installing the dependencies
First of all, let’s install OAuth to make our lives a hell of a lot easier:
npm install oauth
open so that we can open the browser for the user to auth:
npm install open
readline so we can read in the user’s 7-digit pin:
npm install readline
dotenv so we don’t commit our keys to version control!
Obtain request token
Let’s import all of the modules we’re gonna need:
const oauth = require('oauth');
const readline = require('readline');
const open = require('open');
require('dotenv').config();
Next, let’s use the OAuth library to create the OAuth object which takes our keys generated in Twitter:
const consumer = new oauth.OAuth(
"https://twitter.com/oauth/request_token",
"https://twitter.com/oauth/access_token",
process.env.TWITTER_API_KEY,
process.env.TWITTER_API_SECRET,
"1.0A", "oob", "HMAC-SHA1");
Note: You’ll need to create a .env
file that’ll contain your keys, something like:
TWITTER_API_KEY=NzDLSiItgS0aBhlb9AmF2Mddp
TWITTER_API_SECRET=QTRosftjdkjN5wNufJioZyfUIbQlL4KhgWbF1k81Py5vrPmD6H
Let’s make the request to get the request token, and once we’ve got it let’s open the url for the user to auth:
async function performAuth() {
const authRequest = await getAuthRequestToken();
console.log(authRequest);
}
async function getAuthRequestToken() {
return new Promise((resolve, reject) => {
consumer.getOAuthRequestToken(async (error, oauthToken, oauthTokenSecret, results) => {
if (error) {
reject(error);
} else {
await open(`https://twitter.com/oauth/authorize?oauth_token=${oauthToken}`);
resolve({
oauthToken,
oauthTokenSecret
});
}
});
});
}
Running this should open up Twitter:

Accept the user’s pin
Next, we need to wait for the user to enter their pin, add the following to the global scope, we’ll probably want this later on.
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
And let’s add some code to wait for the users to input:
async function getUserPin() {
return new Promise((resolve) => {
rl.question('Enter your code...\n', (pin) => {
console.log(`you entered ${pin}`);
resolve(pin);
});
})
}
And of course, call it:
async function performAuth() {
const authRequest = await getAuthRequestToken();
const pin = await getUserPin();
}
So now when you run you should have something like this:

Get User Access Token
Add the following method which takes the oauthToken
, oauthTokenSecret
and the pin
we just received:
async function getAuthAccessToken(oauthToken, oauthTokenSecret, pin) {
return new Promise((resolve, reject) => {
consumer.getOAuthAccessToken(oauthToken, oauthTokenSecret, pin, (error, oauthAccessToken, oauthAccessTokenSecret, results) => {
if (error) {
reject(error);
} else {
console.log('Successfully authorised with twitter');
resolve({
oauthAccessToken,
oauthAccessTokenSecret
})
}
})
});
}
And call it:
async function performAuth() {
const authRequest = await getAuthRequestToken();
const pin = await getUserPin();
const authAccess = await getAuthAccessToken(authRequest.oauthToken, authRequest.oauthTokenSecret, pin);
}
So running this should output:

Sending a tweet
Let’s add two new functions, one to gather the user input so they can choose what to tweet and another to send the tweet:
async function getTweetToSend() {
return new Promise((resolve) => {
rl.question('Enter tweet to send\n', (tweet) => {
resolve(tweet);
});
})
}
async function sendTweet(oauthAccessToken, oauthAccessTokenSecret, tweet) {
return new Promise((resolve, reject) => {
consumer.post(`https://api.twitter.com/1.1/statuses/update.json?status=${tweet}`, oauthAccessToken, oauthAccessTokenSecret, {} ,function (error, data, response) {
if (error) {
console.log(error)
reject(error);
} else {
console.log(`You sent tweet ${tweet}`)
resolve(data);
}
})
})
}
And call them:
async function performAuth() {
const authRequest = await getAuthRequestToken();
const pin = await getUserPin();
const authAccess = await getAuthAccessToken(authRequest.oauthToken, authRequest.oauthTokenSecret, pin);
const tweet = await getTweetToSend();
await sendTweet(authAccess.oauthAccessToken, authAccess.oauthAccessTokenSecret, tweet);
}
Jobs a good’un.

All the code should look something like the following:
const oauth = require('oauth');
const readline = require('readline');
const open = require('open');
require('dotenv').config();
const consumer = new oauth.OAuth(
"https://twitter.com/oauth/request_token",
"https://twitter.com/oauth/access_token",
process.env.TWITTER_API_KEY,
process.env.TWITTER_API_SECRET,
"1.0A", "oob", "HMAC-SHA1");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
performAuth();
async function performAuth() {
const authRequest = await getAuthRequestToken();
const pin = await getUserPin();
const authAccess = await getAuthAccessToken(authRequest.oauthToken, authRequest.oauthTokenSecret, pin);
const tweet = await getTweetToSend();
await sendTweet(authAccess.oauthAccessToken, authAccess.oauthAccessTokenSecret, tweet);
}
async function getAuthRequestToken() {
return new Promise((resolve, reject) => {
consumer.getOAuthRequestToken(async (error, oauthToken, oauthTokenSecret, results) => {
if (error) {
reject(error);
} else {
await open(`https://twitter.com/oauth/authorize?oauth_token=${oauthToken}`);
resolve({
oauthToken,
oauthTokenSecret
});
}
});
});
}
async function getUserPin() {
return new Promise((resolve) => {
rl.question('Enter your code...\n', (pin) => {
console.log(`you entered ${pin}`);
resolve(pin);
});
})
}
async function getAuthAccessToken(oauthToken, oauthTokenSecret, pin) {
return new Promise((resolve, reject) => {
consumer.getOAuthAccessToken(oauthToken, oauthTokenSecret, pin, (error, oauthAccessToken, oauthAccessTokenSecret, results) => {
if (error) {
reject(error);
} else {
console.log('Successfully authorised with twitter');
resolve({
oauthAccessToken,
oauthAccessTokenSecret
})
}
})
});
}
async function getTweetToSend() {
return new Promise((resolve) => {
rl.question('Enter tweet to send\n', (tweet) => {
resolve(tweet);
});
})
}
async function sendTweet(oauthAccessToken, oauthAccessTokenSecret, tweet) {
return new Promise((resolve, reject) => {
consumer.post(`https://api.twitter.com/1.1/statuses/update.json?status=${tweet}`, oauthAccessToken, oauthAccessTokenSecret, {} ,function (error, data, response) {
if (error) {
console.log(error)
reject(error);
} else {
console.log(`You sent tweet ${tweet}`)
resolve(data);
}
})
})
}
Conclusion
PIN-Based OAuth is a great way to authorise with command-line applications is it is relatively simple. If you know how to use OAuth in the traditional 3-legged approach, you’ll find the transition to PIN-Based OAuth very simple.
Remember, all the code can be found on my GitHub.
If you liked this blog then please sign up for my newsletter and join an awesome community!!
[…] If you’re interested in OAuth, then you’d like PIN-based Oauth! […]