Search code examples
node.jsazureexpressoauth-2.0passport.js

Passport: why is 'req.user' undefined when deployed on Azure Web App?


I have a React frontend running on one Azure Web app service, and a node backend running on another Azure Web app service. I have setup OAuth to authenticate a user via Google, utilising the Passport middleware, and sets a cookie using the cookie.session middleware. This all works fine on my local machine.

On Azure, however, when I try and retrieve the value of req.user from my Frontend (I use this data to control access to routes) it returns undefined. I've console logged the value of user in the passport.serializeUser function in the backend, and at that point in the flow, it has retrieved the user profile data from Google. The cookie is created in my browser, but then, for some reason, this is not then being passed to req.user in my 'success' route.

Running locally, everything runs over http. On Azure, both front and backend web services are set to run over https in the Azure settings config.

This is my Frontend function which retrieves the req.user data:

  useEffect(() => {
    const getUser = () => {
      fetch(process.env.REACT_APP_BE_URL+"/auth/login/success", {
        method: "GET",
        credentials: "include",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
          "Access-Control-Allow-Credentials": true,
        },
      })
        .then((response) => {
          if (response.status === 200) return response.json();
          throw new Error("Authentication failed");
        })
        .then((resObject) => {
          setUser(resObject.user.id);
        })
        .catch((err) => {
          console.log(err);
        });
    };
    getUser();

Backend index.js:

const express = require("express");
const app = express();
const cors = require("cors");
const cookieSession = require("cookie-session");
const passport = require("passport");
require("./config/passport");
require("dotenv").config();

app.use(express.json());
app.set("trust proxy", 2);

app.use(
  cookieSession({
    name: "session",
    keys: [process.env.COOKIE_SESSION_KEY],
    maxAge: 24 * 60 * 60 * 1000,
  })
);

app.use(passport.initialize());
app.use(passport.session());

app.use(
  cors({
    origin: process.env.HOST,
    methods: "GET,POST,PUT,DELETE",
    credentials: true,
  })
);

const db = require("./models");

// Routers
const authRouter = require("./routes/auth");
app.use("/auth", authRouter);

const successRouter = require("./routes/auth");
app.use("/login/success", successRouter);

const failedRouter = require("./routes/auth");
app.use("/login/failed", failedRouter);

db.sequelize.sync().then(() => {
  app.listen(process.env.PORT || 3001, () => {
    console.log("Server running");
  });
});

Passport configuration (Passport.js):

require("dotenv").config();
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const passport = require("passport");

passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: "/auth/google/callback",
      scope: ["profile"],
    },
    function (accessToken, refreshToken, profile, done) {
      done(null, profile);
    }
  )
);

passport.serializeUser((user, done) => {
  done(null, user);
  console.log("passport.serialization user = " + JSON.stringify(user));
});

passport.deserializeUser((user, done) => {
  done(null, user);
});

Authentication related routers (from Auth.js):


router.get("/login/failed", (req, res) => {
  res.status(401).json({
    success: false,
    message: "Logon failure",
  });
});

router.get("/login/success", (req, res) => {
  // if (req.user) {
  res.status(200).json({
    success: true,
    message: "Logon successful",
    user: req.user,
  });
  console.log("req.user in router.get =" + JSON.stringify(req.user));
  // }
});

router.get(
  "/google/callback",
  passport.authenticate("google", {
    successRedirect: host + "/login/success",
    failureRedirect: host + "/login",
  })
);

router.get("/logout", (req, res) => {
  req.logout();
  res.redirect(host);
});

router.get("/google", passport.authenticate("google", { scope: ["profile"] }));

I read in other Stack Overflow posts that the issue could be due to Azure acting as a reverse proxy and blocking https traffic, so added the recommended line: app.set("trust proxy", 2); in my index.js file, but this did not resolve the issue.


Solution

  • In case anyone else is experiencing the same issue, I eventually resolved this (worked around might be a better description) by setting both my frontend and backend systems to run on sub-domains of my main domain. So front end is running on app.mydomain.com and backend is running backend.mydomain.com.

    I still don't know what the specific root-cause issue was, but just that it was something to do with having different domains for front and back ends. Perhaps cors, perhaps something to do with Azure acting as a reverse proxy, perhaps something else?!

    Anyway, I'll leave this here in case it helps someone else. If anyone eventually knows exactly what the specific problem was, then please do add an answer or comment.