CORS issue making AJAX request from React app > Node server > redirect to Google OAuth2 auth

匿名 (未验证) 提交于 2019-12-03 01:18:02

问题:

Hi all I have been banging my head researching this for past 2 days with no luck.

This is the error I'm getting when trying to auth with Google Oauth2 Passport strategy from my React app @localhost:3000. I am running a separate app with a node/express server on localhost:3001.

XMLHttpRequest cannot load http:localhost:3001/api/auth/google/login. Redirect from 'http:localhost:3001/api/auth/google/login' to 'https:accounts.google.com/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3001%2Fapi%2Fauth%2Fgoogle%2Fcallback&scope=https%3A%2F%2Fmail.google.com&client_id=***.apps.googleusercontent.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http:localhost:3000' is therefore not allowed access.

createError.js:16  

Uncaught (in promise) Error: Network Error at createError (createError.js:16) at XMLHttpRequest.handleError (xhr.js:87)

This is the code Im using in my client to try and login from one of my components:

// BUTTON

<div>    <button className="btn btn-danger" onClick={props.googleAuth}>       Login With Google    </button> </div> 

// STATEFUL COMPONENT METHOD

googleAuth() {     axios.get("http:localhost:3001/api/auth/google/login").then(res => {       console.log("GOOGLE OAUTH 2 RES", res);     });   } 

// CONTROLLER

passport.use(   new GoogleStrategy(     {       clientID: process.env.GOOGLE_CLIENT_ID,       clientSecret: process.env.GOOGLE_CLIENT_SECRET,       callbackURL: "/api/auth/google/callback",       accessType: "offline"     },     (accessToken, refreshToken, profile, done) => {       const info = {         googleUsername: profile.displayName,         googleAccessToken: accessToken,         googleRefreshToken: refreshToken,         googleEmail: profile.emails[0].value       };        User.findOrCreate({         where: {           googleId: profile.id         },         defaults: info       })         .spread(user => {           if (user) return done(null, user.toAuthJSON);            // this is method that returns a signed JWT off user DB instance.            return done(null, false);         })         .catch(done);     }   ) );  // GOOGLE LOGIN  router.get(   "/login",   passport.authenticate("google", {     scope: [       "https://mail.google.com/",       "https://www.google.com/m8/feeds/",       "email",       "profile"     ]   }) );  // GOOGLE CALLBACK router.get(   "/callback",   passport.authenticate("google", {     session: false,   }), (req, res) => {     res.json(req.user.token)   } ); 

Steps I've already taken to try and solve:

  • I disabled CORS on my browser
  • I tried the cors npm module on my routes / api on server side Nothing seems to work.
  • And a lot of other tinkering and desperation...

  • Based on the error, Google is preventing me from making a downstream request from my server and throwing the error (I think)...

What my goals are:

  • I want Google to return a user object, which I then store on in my DB (already coded logic for this)
  • Instead of having the server res.redirect() I want to res.json() a signed JWT (which I have properly wired up already).
  • I do not want to use session based auth on my server and keep things clean and stateless.

Is this even possible?It should also be noted that I have a dev environment setup:

Server startup with Concurrently (starts client and nodemon server concurrently - client@localhost:3000 makes proxy requests to server@localhost:3001) - not sure if this might be causing any problems?

Any help would be greatly appreciated!

回答1:

So I was able to solve the issue. The issue is you are using passport.js for social auth, when you could have simple used something like

https://www.npmjs.com/package/react-social-login

But I would anyways tell how to get your current project working.

https://github.com/j-mcfarlane/Reactjs-SocialAuth-RESTAPI

You need to have the redirect to the UI and not api. So your config will change like below

googleCallbackURL: 'http://localhost:3000/api/auth/google/callback' 

Next in your callback, instead of returning data, you will return a redirect like below

jwt.sign(payload, secret, { expiresIn: 220000 }, (err, token) => {     if (err) return console.log(err)      res.redirect("http://localhost:3000/login?token=" + token); }) 

I have hardcoded to localhost:3000 but you should get the host from request and then use it.

Next you will update your App.js to add another route for /login, which will call a component SocialLogin

import React, { Component, Fragment } from 'react' import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'  import PrivateRoute from '../util/PrivateRoute'  // Components import Navbar from './layout/Navbar'  import Landing from './Landing' import SocialLogin from './SocialLogin' import Dashboard from './Dashboard'  class App extends Component {     render() {         return (             <Router>                 <Fragment>                     <Navbar /><br />                      <Route exact path="/" component={Landing} />                     <Route exact path="/login" component={SocialLogin} />                      <Switch>                         <PrivateRoute path="/dashboard" component={Dashboard} />                     </Switch>                 </Fragment>             </Router>          )     } }  export default App 

The simplest SocialLogin I wrote is below

import React from 'react' import qs from 'query-string'  const SocialLogin = (props) => {     console.log(props.location.search)     let params = qs.parse(props.location.search)     window.localStorage.setItem('token', params['token'])     return (<div>Landing Page - {params.token} </div>) }  export default SocialLogin 

Here you have the token and you can continue the flow the way you like it. Also in Google.js I changed below

<button onClick={this.googleAuth}>Signin With Google</button> 

back to your commented

<a href="/api/auth/google">Log in with google</a> 

You need redirects to work and you shouldn't AJAX your google auth call, else how will use select an account?

Now after all these fixes, I am able to get the token as shown below



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!