Reputation: 3525
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.' });
});
});
}
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
Upvotes: 0
Views: 1525
Reputation: 1276
If anyone coming here got stuck with fetching users with Reactjs from passport-twitter
strategy, Here is the solution I found.
Enabling credentials gives the ability to use cookies or express-session
on the frontend. Read MDN for more details.
Back-End:
// cors policy setup
app.use(
cors({
origin: "http://localhost:3000", // front end url
optionsSuccessStatus: 200,
credentials: true,
})
);
Front-End:
axios.get(`${apiURL}/profile`, { withCredentials: true })
Upvotes: 0
Reputation: 3525
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 this passport-twitter-token
strategy together with react-twitter-auth
found here
The 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.
Upvotes: 5