cors - passport.js + Google: automatically redirect to login page if user is not authenticated - Stack Overflow

I use passport.js to log in with Google account to my web app.There is a login button if the user clic

I use passport.js to log in with Google account to my web app.
There is a login button if the user clicks on it then they can choose a Google account to log in, it works fine.

What I want is if no user is authenticated then an automatic redirect should be performed to login.
I have a React js front end which sends GET/POST/etc requests to node.js backend, so if nobody is logged in and a fetch is initiated then Google login page should be displayed automatically before executing anything else.

The error message in Chrome is (no user is logged in so the node.js backend tried to redirect to auth/google endpoint where passport.js tries to authenticate with Google):

Access to XMLHttpRequest at ';redirect_uri=http%3A%2F%2Flocalhost%3A3500%2Fauth%2Fgoogle%2Fcallback&scope=profile&client_id=734494171389-f8kk3r9n7e3crt61ilpvmal20uku9qq4.apps.googleusercontent' (redirected from 'http://localhost:3500/repositories') from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

What interesting is that the same Google URL is called when I click on the button (it works) and when trying to redirect when user is not authenticated (it doesn't work -> CORS error):
;redirect_uri=http%3A%2F%2Flocalhost%3A3500%2Fauth%2Fgoogle%2Fcallback&scope=profile&client_id=734494171389-f8kk3r9n7e3crt61ilpvmal20uku9qq4.apps.googleusercontent

Any thought please why I am having this CORS error only at redirect?
Thank you very much!

server.ts

...
const app: Express = express()

app.use(
    session({
        secret: [process.env.COOKIE_SECRET!],
        cookie: {
            secure: process.env.NODE_ENV === "production" ? true : false,
            sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
            maxAge: 30 * 24 * 60 * 60 * 1000,
        },
        resave: false,
        saveUninitialized: false,
    })
)

app.use(cookieParser());

app.use(passport.initialize());

app.use(passport.session());

app.use(cors({
    methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
    credentials: true,
    origin: process.env.CLIENT_URL!
}))

app.use("/auth", authRouter);

const ensureAuthenticated = (req: any, res: any, next: any) => {
    if (req.isAuthenticated()) {
        logger.info(`It's authenticated.`)
        return next()
    }
    else {
        logger.info(`It is not authenticated, redirecting to '/auth/google'.`)
        res.redirect('/auth/google')
    }
}
app.all('*', function(req,res,next) {
    if (req.path === '/' || req.path.startsWith('/auth'))
      next();
    else
      ensureAuthenticated(req,res,next);  
  });

// routes
app.use('/extractions', extractionRouter)
app.use('/prog-langs', progLangRouter)
app.use('/repositories', repositoryRouter)
app.use('/skills', skillRouter)
app.use('/developers', developerRouter)

app.listen(PORT, async () => {
    await initDB()
    logger.info(`Server running on port ${PORT}`)
})

auth.ts

...
passport.use(new GoogleStrategy(
    {
        clientID: process.env.GOOGLE_CLIENT_ID!,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        callbackURL: 'http://localhost:3500/auth/google/callback',
    },
    async(accessToken, refreshToken, profile, done) => {
        const userData = profile._json;
        let user = {};
        try {
            const currentUserQuery = await AuthUserModel.findByPk(userData.sub)
            if (currentUserQuery) {
                user = {
                    user_id: currentUserQuery.id,
                    username: currentUserQuery.name,
                }
            } else {
                const newAuthUser: AuthUserModel = await AuthUserModel.create({
                    id: userData.sub,
                    name: userData.name
                })
                user = {
                    user_id: newAuthUser.id,
                    username: newAuthUser.name
                }
            }
            done(null, user);
        } catch (error: any) {
            done(error, false, error.message)
        }
    }
))

passport.serializeUser((user, done) => {
    done(null, user);
});
    
passport.deserializeUser((user, done) => {
    logger.info(`deserializeUser: ${JSON.stringify(user)}`)
    done(null, user!)
})

const authRouter = express.Router()
authRouter.get("/google",
    passport.authenticate("google", { scope: "profile", })
)

authRouter.get(
    "/google/callback", 
    passport.authenticate(
        "google", 
        {
            successRedirect: process.env.CLIENT_URL!,
            failureRedirect: "/auth/login/failed"
          }
    )
)

authRouter.get("/login/success", (req, res) => {
    if (req.user) {
      res.json({
        success: true,
        message: "user has successfully authenticated",
        user: req.user,
        cookies: req.cookies
      });
    }
  });
  
// when login failed, send failed msg
authRouter.get("/login/failed", (req, res) => {
res.status(401).json({
    success: false,
    message: "user failed to authenticate."
});
});

  // When logout, redirect to client
authRouter.post("/logout", (req, res, next) => {
    req.logout(function(err) {
        if (err) { return next(err); }
        res.redirect(process.env.CLIENT_URL!);
    })
  });

export default authRouter

Button on front end

export default function Login() {

    const handleOAuth = () => {
        window.open(`${endpointBackEnd}/auth/google`, "_self");
    };

    return (
        <span>
            <button onClick={handleOAuth}>
                Login with Google
            </button>
        </span>
    )
}

I use passport.js to log in with Google account to my web app.
There is a login button if the user clicks on it then they can choose a Google account to log in, it works fine.

What I want is if no user is authenticated then an automatic redirect should be performed to login.
I have a React js front end which sends GET/POST/etc requests to node.js backend, so if nobody is logged in and a fetch is initiated then Google login page should be displayed automatically before executing anything else.

The error message in Chrome is (no user is logged in so the node.js backend tried to redirect to auth/google endpoint where passport.js tries to authenticate with Google):

Access to XMLHttpRequest at 'https://accounts.google/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3500%2Fauth%2Fgoogle%2Fcallback&scope=profile&client_id=734494171389-f8kk3r9n7e3crt61ilpvmal20uku9qq4.apps.googleusercontent' (redirected from 'http://localhost:3500/repositories') from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

What interesting is that the same Google URL is called when I click on the button (it works) and when trying to redirect when user is not authenticated (it doesn't work -> CORS error):
https://accounts.google/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3500%2Fauth%2Fgoogle%2Fcallback&scope=profile&client_id=734494171389-f8kk3r9n7e3crt61ilpvmal20uku9qq4.apps.googleusercontent

Any thought please why I am having this CORS error only at redirect?
Thank you very much!

server.ts

...
const app: Express = express()

app.use(
    session({
        secret: [process.env.COOKIE_SECRET!],
        cookie: {
            secure: process.env.NODE_ENV === "production" ? true : false,
            sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
            maxAge: 30 * 24 * 60 * 60 * 1000,
        },
        resave: false,
        saveUninitialized: false,
    })
)

app.use(cookieParser());

app.use(passport.initialize());

app.use(passport.session());

app.use(cors({
    methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
    credentials: true,
    origin: process.env.CLIENT_URL!
}))

app.use("/auth", authRouter);

const ensureAuthenticated = (req: any, res: any, next: any) => {
    if (req.isAuthenticated()) {
        logger.info(`It's authenticated.`)
        return next()
    }
    else {
        logger.info(`It is not authenticated, redirecting to '/auth/google'.`)
        res.redirect('/auth/google')
    }
}
app.all('*', function(req,res,next) {
    if (req.path === '/' || req.path.startsWith('/auth'))
      next();
    else
      ensureAuthenticated(req,res,next);  
  });

// routes
app.use('/extractions', extractionRouter)
app.use('/prog-langs', progLangRouter)
app.use('/repositories', repositoryRouter)
app.use('/skills', skillRouter)
app.use('/developers', developerRouter)

app.listen(PORT, async () => {
    await initDB()
    logger.info(`Server running on port ${PORT}`)
})

auth.ts

...
passport.use(new GoogleStrategy(
    {
        clientID: process.env.GOOGLE_CLIENT_ID!,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        callbackURL: 'http://localhost:3500/auth/google/callback',
    },
    async(accessToken, refreshToken, profile, done) => {
        const userData = profile._json;
        let user = {};
        try {
            const currentUserQuery = await AuthUserModel.findByPk(userData.sub)
            if (currentUserQuery) {
                user = {
                    user_id: currentUserQuery.id,
                    username: currentUserQuery.name,
                }
            } else {
                const newAuthUser: AuthUserModel = await AuthUserModel.create({
                    id: userData.sub,
                    name: userData.name
                })
                user = {
                    user_id: newAuthUser.id,
                    username: newAuthUser.name
                }
            }
            done(null, user);
        } catch (error: any) {
            done(error, false, error.message)
        }
    }
))

passport.serializeUser((user, done) => {
    done(null, user);
});
    
passport.deserializeUser((user, done) => {
    logger.info(`deserializeUser: ${JSON.stringify(user)}`)
    done(null, user!)
})

const authRouter = express.Router()
authRouter.get("/google",
    passport.authenticate("google", { scope: "profile", })
)

authRouter.get(
    "/google/callback", 
    passport.authenticate(
        "google", 
        {
            successRedirect: process.env.CLIENT_URL!,
            failureRedirect: "/auth/login/failed"
          }
    )
)

authRouter.get("/login/success", (req, res) => {
    if (req.user) {
      res.json({
        success: true,
        message: "user has successfully authenticated",
        user: req.user,
        cookies: req.cookies
      });
    }
  });
  
// when login failed, send failed msg
authRouter.get("/login/failed", (req, res) => {
res.status(401).json({
    success: false,
    message: "user failed to authenticate."
});
});

  // When logout, redirect to client
authRouter.post("/logout", (req, res, next) => {
    req.logout(function(err) {
        if (err) { return next(err); }
        res.redirect(process.env.CLIENT_URL!);
    })
  });

export default authRouter

Button on front end

export default function Login() {

    const handleOAuth = () => {
        window.open(`${endpointBackEnd}/auth/google`, "_self");
    };

    return (
        <span>
            <button onClick={handleOAuth}>
                Login with Google
            </button>
        </span>
    )
}
Share asked Mar 3 at 23:02 ViktorViktor 1,4972 gold badges26 silver badges52 bronze badges 1
  • I've just noticed when I have the CORS issue (redirecting when no user is logged in) the Origin request header value is null but when the authentication works (clicking on the Login button) then no Origin is set. Can that be the problem? I don't know how I could control that, is is managed by passport.js – Viktor Commented Mar 4 at 22:08
Add a comment  | 

1 Answer 1

Reset to default 0

My solution was to return 401 in ensureAuthenticated function and handles the 401 in React.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745068476a4609399.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信