Reputation: 1
I have implemented local as well as google login using passport.js in a mern web application. The local authentication is working fine with the frontend but I am getting errors when using the Google strategy.
Error:
Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?.........' (redirected from 'http://localhost:5000/auth/google') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:84)
Issue shown by the console:
Indicate whether a cookie is intended to be set in a cross-site context by specifying its SameSite attribute.
Indicate whether to send a cookie in a cross-site request by specifying its SameSite attribute
I tested the google strategy using POSTMAN and it was working fine but when requesting from my frontend there seems to be some issue.
server.js
require("dotenv").config();
const express=require("express");
const cors=require("cors");
const mongoose=require("mongoose");
const session=require("express-session");
const passport=require("passport");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const errorController=require("./controllers/errorController")
const app=express();
const port = process.env.PORT || 5000;
app.use(
cors({
origin: "http://localhost:3000", // <-- location of the react app were connecting to
credentials: true,
})
);
app.use(express.static("public"));
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(session({
secret:process.env.SECRET,
resave:false,
saveUninitialized:false,
}));
app.use(passport.initialize());
app.use(passport.session());
const User=require("./models/user.model");
passport.use(User.createStrategy());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: "http://localhost:5000/auth/google/keeper"
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
mongoose.connect(process.env.MONGO_URI, {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify:false, useCreateIndex:true});
const connection = mongoose.connection;
connection.once('open', () => {
console.log("MongoDB database connection established successfully");
});
const loginRouter=require("./routes/login");
const registerRouter=require("./routes/register");
const logoutRouter=require("./routes/logout");
const authRouter=require("./routes/auth");
app.use("/login", loginRouter);
app.use("/register", registerRouter);
app.use("/logout", logoutRouter);
app.use("/auth/google", authRouter);
app.use(errorController);
app.listen(port, function(){
console.log("server started on port 5000");
});
auth.js (handles the google login related routes)
const router=require("express").Router();
const passport=require("passport");
router.get("/", passport.authenticate("google", { scope: ["profile"] }));
router.get("/keeper",
passport.authenticate("google", { failureRedirect: "/login" }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect("/");
});
module.exports=router;
Axios request from the frontend
function googleLogin(event){
Axios({
method: "GET",
withCredentials: true,
url: "http://localhost:5000/auth/google",
})
.then(function(res){
console.log(res);
})
.catch(function(err){
console.log(err);
})
event.preventDefault();
}
button that triggers the request
<button className="btn btn-danger" onClick={googleLogin}>Sign in with Google</button>
Upvotes: 0
Views: 1868
Reputation: 244
You can't make an axios call to the /auth/google URL!!
Here's my solution...
// step 1:
// onClick handler function of the button should use window.open instead
// of axios or fetch
const googleLogin = () => window.open("http://[server:port]/auth/google", "_self")
//step 2:
// on the server's redirect route add this successRedirect object with correct url.
// Remember! it's your clients root url!!!
router.get(
'/google/redirect',
passport.authenticate('google',{
successRedirect: "[your CLIENT root url/ example: http://localhost:3000]"
})
)
// step 3:
// create a new server route that will send back the user info when called after the authentication
// is completed. you can use a custom authenticate middleware to make sure that user has indeed
// been authenticated
router.get('/getUser',authenticated, (req, res)=> res.send(req.user))
// here is an example of a custom authenticate express middleware
const authenticated = (req,res,next)=>{
const customError = new Error('you are not logged in');
customError.statusCode = 401;
(!req.user) ? next(customError) : next()
}
// step 4:
// on your client's app.js component make the axios or fetch call to get the user from the
// route that you have just created. This bit could be done many different ways... your call.
const [user, setUser] = useState()
useEffect(() => {
axios.get('http://[server:port]/getUser',{withCredentials : true})
.then(response => response.data && setUser(response.data) )
},[])
Explanation....
step 1 will load your servers auth url on your browser and make the auth request.
step 2 then reload the client url on the browser when the authentication is
complete.
step 3 makes an api endpoint available to collect user info to update the react state
step 4 makes a call to the endpoint, fetches data and updates the users state.
Upvotes: 2