I am setting up Twitter oauth on my create-react-app
application by using a helper function (via axios
) on the front end to initiate the passport
oauth process on my backend. I am currently in development, so I am hosting my express server on port 3001
and my front end on port 3000
with a proxy on the front end to port 3001.
I have set up CORS permissions via the cors
npm package.
No matter what set of configurations I try, I am unable to complete the Twitter OAuth process. I have tried switching ports, keeping the same ports; I have tried proxying my backend with express-http-proxy
.
I have used http://127.0.0.1
instead of localhost
in both my callback function and my initial api call, trying both ports 3000
and 3001
.
I’m at the point where I’m not sure where I’m going wrong or if I need to abandon the passport-twitter
for other solutions.
In every case, I keep getting the following error:
Failed to load https://api.twitter.com/oauth/authenticate?
oauth_token=alphanumericcoderedactedherebyme: No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin 'http://localhost:3000' is
therefore not allowed access.
Depending on the configuration I have attempted, I get origin as null
or http://localhost:3001
or http://127.0.0.1
.
Note, I successfully call my backend api numerous times for other reasons, such as connecting to Yelp Fusion API
. Moreover, I am using middleware to log my session data and I can see that I am successfully getting oauth_token
and oauth_token_secret
from Twitter. The call is failing on the next leg of the oauth process:
[0] *************SESSION MIDDLEWARE***************
[0] Session {
[0] cookie:
[0] { path: '/',
[0] _expires: 2018-01-06T20:20:31.913Z,
[0] originalMaxAge: 2678400000,
[0] httpOnly: true },
[0] 'oauth:twitter':
[0] { oauth_token: 'alphanumericcoderedactedherebyme',
[0] oauth_token_secret: 'alphanumericcoderedactedherebyme' } }
[0]
[0] Logged In:
[0] __________ false
[0] **********************************************
Here is relevant portions of my code –
BACKEND CODE
SERVER.JS
// Dependencies
const express = require("express");
const cors = require("cors");
const passport = require('passport');
// Initialize Express Server
const app = express();
// Specify the port.
var port = process.env.PORT || 3001;
app.set('port', port);
app.use(passport.initialize());
app.use(passport.session());
//enable CORS
app.use(cors());
//set up passport for user authentication
const passportConfig = require('./config/passport');
require("./controllers/auth-controller.js")(app);
// Listen on port 3000 or assigned port
const server = app.listen(app.get('port'), function() {
console.log(`App running on ${app.get('port')}`);
});
PASSPORT.JS
const passport = require('passport');
const TwitterStrategy = require('passport-twitter').Strategy;
passport.use(new TwitterStrategy({
consumerKey: process.env.TWITTER_CONSUMER_KEY,
consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
callbackURL: process.env.NODE_ENV === 'production' ? process.env.TWITTER_CALLBACK_URL : 'http://localhost:3000/auth/twitter/callback'
},
function(accessToken, refreshToken, profile, done) {
...etc, etc, etc
AUTH-CONTROLLER.JS
const router = require('express').Router();
const passport = require('passport');
module.exports = function(app) {
router.get('/twitter', passport.authenticate('twitter'));
router.get('/twitter/callback',
passport.authenticate('twitter', {
successRedirect: '/auth/twittersuccess',
failureRedirect: '/auth/twitterfail'
})
);
router.get('/twittersuccess', function(req, res) {
// Successful authentication
res.json({ user: req.user, isAuth: true });
})
router.get('/twitterfail', function(req, res) {
res.statusCode = 503;
res.json({ err: 'Unable to Validate User Credentials' })
})
app.use('/auth', router);
}
FRONTEND CODE
HELPERS.JS
import axios from 'axios';
export function authUser() {
return new Promise((resolve, reject) => {
axios.get('/auth/twitter', {
proxy: {
host: '127.0.0.1',
port: 3001
}
}).then(response => {
resolve(response.data);
}).catch(err => {
console.error({ twitterAuthErr: err })
if (err) reject(err);
else reject({ title: 'Error', message: 'Service Unavailable - Please try again later.' });
});
});
}
UPDATE
I verified that Passport Authentication works on my backend port. I called the endpoint directly in the browser and was redirected to the Twitter auth which then returned to my callback the new user saved in my schema which was saved to the session data.
This means the problem lies with the use of Create-React-App on a different port than my backend.
http://127.0.0.1:3001/auth/twittersuccess
"user": {
"_id": "redactedbyme",
"name": "Wesley L Handy",
"__v": 0,
"twitterId": "redactedbyme",
"favorites": [],
"friends": []
},
"isAuth": true
2
Answers
I could not find a solution to the problem at hand, after consulting several developers and posting this question in other forums.
However, according to this blog,
passport-twitter
is not optimized for RESTful apis. This same blog provides a helpful tutorial for using thispassport-twitter-token
strategy together withreact-twitter-auth
found hereThe issue relates to the fact that with Create-React-App the application runs on two different servers, one for the front-end and another for the back. There is no way around the CORS issue without a series of communications between the front-end and back-end, which passport does not allow. Passport is a great tool for handling OAuth on a single-server, but there is quite a bit of back and forth needed in OAuth that requires more complexity.
The tutorial by Ivan Vasiljevic is a helpful starting place for both understanding and working through that complexity.
If anyone coming here got stuck with fetching users with Reactjs from
passport-twitter
strategy, Here is the solution I found.All you have to do is allowing credentials
Enabling credentials gives the ability to use cookies or
express-session
on the frontend. Read MDN for more details.Back-End:
Front-End: