skip to Main Content

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


  1. Chosen as BEST ANSWER

    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.


  2. 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:

    // 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 })
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search