How to set equivalent of fetch's { credentials: "include" } in Redux Toolkit's createApi?
Asked Answered
G

1

9

I have an express-react-typescript-redux-passport project where I am using createApi from redux toolkit to call a /getuser api on my backend.

I'm using passport-google-oauth20 strategy to authenticate users, and the user is successfully authenticated.

My issue is that passport js deserializeUser() function is not called (even though the serializeUser() is called, and the user is authenticated using google strategy), so the req.user parameter is not set automatically when the front end sends requests to the back end.

I suspect that deserializeUser isn't being called because the I haven't set axio's { withCredentials: true } (or fetch's {credentials: "include"}) parameter in my createApi endpoint. How can I sent this parameter in RTK's createApi?

How do I specify credentials: include here?

Here is my createApi function

export const userApiSlice = createApi({
  reducerPath: "api",
  baseQuery: fetchBaseQuery({
    baseUrl: "http://localhost:4000",
    prepareHeaders(headers) {
      return headers;
    },
  }),
  endpoints(builder) {
    // debugger;
    return {
      fetchUser: builder.query<IUser, number | void>({
        query: () => {
          debugger;
          return `/getuser`;
        },
      }),
    };
  },
});

Here is my server index.js

import express from "express";
import mongoose from "mongoose";
import cors from "cors";
import session from "express-session";
import passport from "passport";

var GoogleStrategy = require("passport-google-oauth20").Strategy;
import { IGoogleAuthUser } from "./types/authTypes";
const PORT = process.env.PORT || 4000;

require("dotenv").config();

const app = express();

mongoose.connect(process.env.LOCAL_DB_ADDRESS, () => {
  console.log("connected to mongoose db");
});

app.use(express.json());
app.use(cors({ origin: "http://localhost:3000", credentials: true }));

app.use(
  session({
    secret: process.env.SESSION_SECRET,
    resave: true,
    saveUninitialized: true,
  })
);
app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser((user: IGoogleAuthUser, done: any) => {
  //send a cookie to browser to store user id in session
  const { id} = user;
  console.log("serializeUser called");
  return done(null, id);
});

// why isn't this called???
passport.deserializeUser((userId: string, done: any) => {
  //attaches the cookie id from session to req.user
  console.log("deserializeUser userId : ", userId);
  return done(null, userId);
});

//google strategy
passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: "/auth/google/callback",
    },
    function (accessToken: any, refreshToken: any, profile: any, done: any) {
      //this is called on succesful authentication with the above Google Stratety     
      console.log("successful authorization");
      done(null, profile);
    }
  )
);

//when user clicks on 'login with google' /auth/google is hit
app.get(
  "/auth/google",
  passport.authenticate("google", { scope: ["profile", "email"] }),
  (req, res) => {
    console.log("/auth/google called"); //this console.log does not get called, not sure why
  }
);

app.get(
  "/auth/google/callback",
  passport.authenticate("google", {
    successRedirect: "http://localhost:3000/profile",
    failureRedirect: "http://localhost:3000/login",
  }),
  function (req, res) {
    // console.dir(req);
    // Successful authentication, redirect home.
    console.log("redirect to profile"); //this does get called
    res.redirect("http://localhost:3000/profile"); 
  }
);

app.get("/", (req, res) => {
  res.send("Hello world.");
});

app.get("/getuser", (req: any, res: any) => {
  //req should have user thanks to serializer/deserializer
  console.log(req.user); // => returns undefined even after successful authentication
  res.send(req.user);
});

app.listen(PORT, () => {
  console.log(`Server Started on ${PORT}`);
});

Why isn't deserializeUser() being called??

Geoff answered 4/10, 2021 at 1:27 Comment(0)
F
20

fetchBaseQuery is just a wrapper around fetch with some extra options.

So it's either

  baseQuery: fetchBaseQuery({
    baseUrl: "http://localhost:4000",
    prepareHeaders(headers) {
      return headers;
    },
    credentials: "include"
  }),

or

        query: () => {
          return { url: `/getuser`, credentials: "include" };
        },
Freer answered 4/10, 2021 at 9:57 Comment(1)
I was encountering a similar issue where my NestJS API was not delivering a session cookie to the browser hosting my React app frontend. This answer unblocked me, thanks!Bongbongo

© 2022 - 2024 — McMap. All rights reserved.