This module lets you authenticate using Twitter in your Node.js applications
By plugging into Passport, Twitter authentication can be integrated into any application or framework that supports
Connect-style middleware, including
Express.
When you want to interact with Twitter's API or just provide a “social login” through Twitter, until last year you had to use legacy OAuth 1.0a authentication. While it still has its uses, Twitter is transitioning to the new version of their API (v2) and OAuth 2.0 protocol, which is also used by other providers, like Facebook, Google, and LinkedIn.
Besides being more widespread, Twitter's OAuth 2.0 has other advantages compared to OAuth 1.0a:
It lets you specify exactly what your application needs from the API through scopes, so users have a clear idea what your app can and cannot do with their account; OAuth 1.0a has only three levels of access: “read”, “read + write”, and “read + write + access DMs”
It is future-proof, as the development effort is focused on v2 API, for which OAuth 2.0 is the default authentication protocol.
When we wanted to build social media integrations, we couldn't find any up-to-date Node.js library to handle the authentication flow. So, we built one as a strategy for the popular Passport.js authentication middleware.
The strategy is available in @superfaceai/passport-twitter-oauth2 package. Recently we released a new minor version 1.2, which conducts a full rewrite to TypeScript.
Getting OAuth 2.0 Client ID and Secret from Twitter
To start using Twitter API, you need to register for a developer account (including phone number verification). Once you register, you will be prompted to create the first application. You will immediately receive API Key and API Key Secret – but ignore them, since these are only for OAuth 1.0. Instead, go to your project's dashboard, there go to the App Settings of the only application you created.
In application settings, find User authentication settings and click Set up.
In User authentication settings make sure to select Type of App as Web App, Automated App or Bot.
In App info enter the following URL under Callback URI / Redirect URL:
http://localhost:3000/auth/twitter/callback
This is a URL where the user can be redirected after approving the application. The callback will be handled by the example server we will build in the next section.
Fill in also the Website URL, since it is a required value. Feel free to ignore other fields.
Once you save the form, you will get OAuth 2.0 Client ID and Client Secret. Make sure to write these values down, since you can't reveal the secret later, only regenerate it.
But in case you forget to save the secret, you can always regenerate it under Keys and Tokens section of your app.
Authenticating with Passport.js and Twitter in Express.js
Let's create a simple Express server with Passport.js to show the authentication flow in action. Passport requires some session handling mechanism, usually through express-session. I will also use the dotenv package to load the credentials from .env file to process.environment object.
First, let's create a project and install dependencies:
Now put your Client ID and Client Secret into .env file in the root of your project. Use the following template and make sure to paste in the correct values from the developer portal:
BASE_URL=http://localhost:3000
TWITTER_CLIENT_ID="OAuth 2.0 Client ID from Twitter Developer portal"
TWITTER_CLIENT_SECRET="OAuth 2.0 Client Secret from Twitter Developer portal"
I have also prepared the BASE_URL variable, since we will be passing the callback URL during the authentication, and keeping this value configurable makes it easier to take your app to production.
And now, let's create server.js file, and put in the following contents:
constexpress=require('express');constpassport=require('passport');const{Strategy}=require('@superfaceai/passport-twitter-oauth2');constsession=require('express-session');require('dotenv').config();// <1> Serialization and deserializationpassport.serializeUser(function (user,done){done(null,user);});passport.deserializeUser(function (obj,done){done(null,obj);});// Use the Twitter OAuth2 strategy within Passportpassport.use(// <2> Strategy initializationnewStrategy({clientID:process.env.TWITTER_CLIENT_ID,clientSecret:process.env.TWITTER_CLIENT_SECRET,clientType:'confidential',callbackURL:`${process.env.BASE_URL}/auth/twitter/callback`,},// <3> Verify callback(accessToken,refreshToken,profile,done)=>{console.log('Success!',{accessToken,refreshToken});returndone(null,profile);}));constapp=express();// <4> Passport and session middleware initializationapp.use(passport.initialize());app.use(session({secret:'keyboard cat',resave:false,saveUninitialized:true}));// <5> Start authentication flowapp.get('/auth/twitter',passport.authenticate('twitter',{// <6> Scopesscope:['tweet.read','users.read','offline.access'],}));// <7> Callback handlerapp.get('/auth/twitter/callback',passport.authenticate('twitter'),function (req,res){constuserData=JSON.stringify(req.user,undefined,2);res.end(`<h1>Authentication succeeded</h1> User data: <pre>${userData}</pre>`);});app.listen(3000,()=>{console.log(`Listening on ${process.env.BASE_URL}`);});
Now run your server with npm start and visit http://localhost:3000/auth/twitter. You will be redirected to Twitter authorization page:
And once you authorize the app, you should see the user data from your profile and, in addition, accessToken and refreshToken will be logged into console.
Breaking down the example
Let's break down the example code above a bit.
User serialization and deserialization
// <1> Serialization and deserializationpassport.serializeUser(function (user,done){done(null,user);});passport.deserializeUser(function (obj,done){done(null,obj);});
These functions serialize and deserialize user to and from session. In our example application, we keep all sessions in the memory with no permanent storage, so we just pass the whole user object.
Typically, you will persist data in a database. In that case, you will store the user ID in the session, and upon deserialization find the user in your database using the serialized ID, for example:
The deserialized user object is then accessible through req.user property in middleware functions.
Strategy initialization
// Use the Twitter OAuth2 strategy within Passportpassport.use(// <2> Strategy initializationnewStrategy({clientID:process.env.TWITTER_CLIENT_ID,clientSecret:process.env.TWITTER_CLIENT_SECRET,clientType:'confidential',callbackURL:`${process.env.BASE_URL}/auth/twitter/callback`,}// ...));
To use an authentication strategy, it must be registered with Passport through passport.use. Here, the Twitter OAuth 2.0 strategy is initialized with credentials from Twitter developer portal. The callback URL must be absolute and registered with Twitter – it's where the user is redirected after authorizing the application.
clientType can be either confidential or public, but in case of server-side applications you will usually use confidential. As explained in OAuth specification, confidential clients can keep the secret safe, while public clients, like mobile applications, can't.
The second argument to the strategy constructor is a verify function. In case of OAuth-based strategies, it is called at the end of successful authorization flow. The user has authorized your application, and you will receive their access token and (optionally) refresh token and user's profile (username, display name, profile image etc.).
Now it's your turn: typically you will want to update or create the user in your database and store the tokens, so you can call the API with them. The done callback should receive a user object, which is passed in req.user property.
Passport and Session middlewares initialization
// <4> Passport and session middleware initializationapp.use(passport.initialize());app.use(session({secret:'keyboard cat',resave:false,saveUninitialized:true}));
Passport needs to be initialized as middleware as well. And it requires a session middleware for storing state and user data. The most common session middleware is express-session.
By default, express-session stores all data in memory, which is good for testing, but not intended for production: if your server gets restarted, all users will be logged out. There is a wide selection of compatible session stores – pick one which fits with the rest of your stack.
Start the authentication flow
Now we get to the juicy part, routes where the authentication happens. The first route is /auth/twitter:
passport.authenticate creates a middleware for the given strategy. It redirects the user to Twitter with URL parameters, so Twitter knows what application the user is authorizing and where the user should be then redirected back. authenticate function accepts a second parameter with additional options, where the most important is scopes.
OAuth scopes define what the application is allowed to do on behalf of the user. The user can then review and approve these permissions.
In this case, the following scopes are requested:
tweet.read – allows reading of user's and others' tweets (including tweets from private accounts the user follows)
users.read – allows reading information about users profiles
offline.access – allows access even when the user isn't signed in; in practice, the application receives the refresh token
Both tweet.read and users.read are necessary for accessing information about the signed-in user. offline.access is useful when you want to do something when the user isn't directly interacting with your application, for example if you post scheduled tweets, build a bot, or monitor mentions on Twitter.
// <7> Callback handlerapp.get('/auth/twitter/callback',passport.authenticate('twitter'),function (req,res){constuserData=JSON.stringify(req.user,undefined,2);res.end(`<h1>Authentication succeeded</h1> User data: <pre>${userData}</pre>`);});
This is the final step in the authentication flow. After the user authorized your application, they are redirected to /auth/twitter/callback route. The passport.authenticate middleware is here again, but this time it checks query parameters Twitter provided on redirect, checks if everything is correct, and obtains access and refresh tokens.
If the authentication succeeds, the next middleware function is called – typically you will display some success message to the user or redirect them back to your application. Since the authentication passed, you can now find the user data in req.user property.
Next steps
Passport.js isn't limited to just Express, you can use our strategy with other frameworks with Passport compatibility like NestJS, Fastify, or Koa.
With the obtained access token, you can start integrating Twitter's API. Check out our twitter-demo repository with CLI scripts showing how to list followers, lookup posts by hashtag, or publish a tweet.
We also have a bit more complex social-media-demo which demonstrates authentication and integrations with Facebook, Instagram, LinkedIn, and Twitter.
I will be diving into more hands-on examples with Twitter, Instagram, and other social media in the future posts. Subscribe to our blog or sign up for our monthly newsletter, so you don't miss it.
OAuth 2.0 Authorization Code Flow with PKCE explains details of Twitter's OAuth 2.0 implementation, like lifetime of access and refresh tokens, glossary, and overview of all scopes.