Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication with Passport + Facebook + Express + create-react-app + React-Router + proxy

TLDR: How do I initiate the Facebook Auth process from a page in my React application?

I've found quite a few posts that touch on most, but not all, of the items I've listed in the title of this post. And I'm pretty close to getting this working, but there must be something I'm missing.

My app consists of a create-react-app front-end using react-router for routing. I have this running on port 51000 in my development environment. In package.json, I'm using "proxy" to redirect other routes to my express server, running on port 51001. This works in general for making requests within my React components that hit the server. For example:

axios.get('/api/some-stuff')

When calling within my React component, this will successfully hit my Express server.

I want to ensure that my back-end routes are authenticated, and I've chosen to use Facebook auth via passport-facebook. I've set this up just like the many online tutorials I've seen. For example, I have a route on my express server named '/auth/facebook'. If I go to localhost:51001/auth/facebook, it redirects me to facebook to login, and then redirects me back to some the route I gave it. So that flow works as expected.

The specific problem I'm having is getting React to successfully initiate and complete the api call that triggers auth. I have tried two approach.

Approach 1: Calling the route via ajax

I called the following in my componentDidMount:

axios.get('/auth/facebook')

This successfully hits my express server, but then immediate generates the following error in the browser console:

XMLHttpRequest cannot load https://www.facebook.com/dialog/oauth?response_type=code&redirect_uri=http%…2Flocalhost%3A51000%2Fauth%2Ffacebook%2Fcallback&client_id=XYZ. Redirect from 'https://www.facebook.com/dialog/oauth?response_type=code&redirect_uri=http%…2Flocalhost%3A51000%2Fauth%2Ffacebook%2Fcallback&client_id=XYZ’ to 'https://www.facebook.com/login.php?skip_api_login=1&api_key=XYZ…_&display=page&locale=en_US&logger_id=7f30b5a1-2ad1-dc31-f08b-70d3cfdd55fa' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:51000' is therefore not allowed access.

**Approach 2: Going to the route in the browser **

The other thing I've tried, which I've seen in all the tutorials, is to add a link to one of my pages that simply navigates to the authentication route. For example:

<a href="/auth/facebook">Login with Facebook</a>

However, when I click this, I just end up at http://localhost:51000/auth/facebook, or just a blank page in my React app. The proxy I have doesn't kick in, and I don't see any activity on my Express server when I go to this route in the browser, even though it's not a Route I've defined in react-router.

So I'm a bit stuck here. Should clicking that tag have triggered that route to be fired against my Express server? Is there a way to tell react-router to ignore certain routes so that they're redirected to my express server? This seems to work fine when making AJAX calls, but if I go to localhost:51000/not/a/route, this doesn't cause the request to be redirected to Express. It just loads a react page with no content.

Any help is appreciated.

Thanks,

-Dan

EDIT

So I kind of have this working now, though I'm not at all confident it' the "right" way to do things.

In my react app (which recall is running on port 51000), I have the following link:

<a href="http://localhost:51001/auth/facebook">Facebook Login</a>

Note that this is pointing directly at my API server, not at a react route. Navigating to this link hits the express server, and immediately redirects to Facebook. Logging in redirects back to my Express facebook callback, which itself redirects to http://localhost:51000/somePage, which is now back to my react app.

Ideally that tag would be going to a /api/auth/facebook, instead of hard-coded to go to a different URL/port. I'll update this issue if/when I figure out how to get react-router to pass that through instead of treating it like a page it needs to load.

EDIT 2

I'm open to someone telling me otherwise, but I think my current approach is actually pretty much fine. I considered how things will behave in production. I will have my react site at www.example.com, with my express server accessible via api.example.com. My link to the Facebook login will be to "api.example.com/auth/facebook". The callback, assuming successful auth, will redirect to "www.example.com/some/route". I think it's reasonable to bounce over to the "api" URL to perform the login. Ultimately there's no point in trying to get react-router to ignore certain routes, since all of my api calls will be to an explicit route.

I realize I'm not providing a complete helpful solution to others. Once I have it all put together, I'll include a link to my repo that contains this login flow, in case anyone else finds it helpful.

** EDIT 3 **

Here are links to my project. It won't be trivial to get the whole project running, but there are only a handful of files associated with the auth flow.

You can view the repo here: https://github.com/dan-goyette/Portfolio

The Express server router for this is here:

https://github.com/dan-goyette/Portfolio/blob/master/portfolio-server/src/Routing/auth.js

In the UI, I'm triggering calls to the express server using this component:

https://github.com/dan-goyette/Portfolio/blob/75ca9326e6227d0601cdfa4c0cece672bd347302/portfolio-ui/src/Components/SocialMenuButton/SocialMenuButton.js

You can see it working at https://www.dan-goyette.com/home, the button at the top right.

like image 211
Dan Avatar asked Jun 29 '17 00:06

Dan


2 Answers

I was also running into this issue - changing the CRA proxy config seemed to do the trick:

"proxy": {
   "/api": {
      "target": "http://localhost:3001/"
   }
}

Where the OAuth link is located at /api/auth/foo, CRA app is at localhost:3000, and the express app is at localhost:3001

like image 104
romeboards Avatar answered Nov 07 '22 01:11

romeboards


I ran into the same problem today and solved it by not using create-react-app's proxy, but instead setting up a proxy in my backend to the create-react-app.

At the end of my express app, I have

 // all routes above

 if (env.isDevelopment()) {
   const proxy = require('express-http-proxy')
   app.use('/*', proxy('http://localhost:3000'))
 } else {
   // probably serve up build version in production
 }

Just remember to turn off the proxy in the package json of your client, or else you could end up with an infinite proxy loop or something.

like image 1
Zeke Nierenberg Avatar answered Nov 07 '22 03:11

Zeke Nierenberg