Not getting Context value in my resolvers!

I am using @apollo/server with express and authenticating with passport but i am not getting any req.user object in my context, i even hard code the user object in my context but not getting anything in my resolvers. Here is some code of my app.js

const apolloServer = new ApolloServer({
  schema,
  context: ({ req }) => {
   // const user = "John Doe" //hard-coded
    const user = req.user || null;
    return { user };
  },
  introspection: isDevelopment,
  plugins: [
    // Proper shutdown for the HTTP server.
    ApolloServerPluginDrainHttpServer({ httpServer }),
    // Proper shutdown for the WebSocket server.
    {
      async serverWillStart() {
        return {
          async drainServer() {
            await serverCleanup.dispose();
          },
        };
      },
    },
  ],
});
const start = async () => {
  await connectDB(`${process.env.VITE_MONGO_URI}`).then((conn) => {
    console.log(`MongoDB Connected: ${conn.connection.host}`.black.bgCyan)
    httpServer.listen(PORT, () => console.log(`Listening on port: ${process.env.VITE_PORT} (in ${process.env.VITE_NODE_ENV})`.black.bgGreen))
  }).catch((err) => {
    console.log(`MongoDB Error: ${err}`.black.bgRed)
  })

  await apolloServer.start()
  app.use('/api/v1/graphql', passport.authenticate('jwt', { session: false }), (req, res, next) => {
    console.log(req.user); // successfully consoling the user object
    next();
  },
  app.use('/api/v1/sandbox', expressMiddleware(apolloServer));

}

There is one more issue and that’s why i am using two different paths for graphql APIs and Sandbox, previously i was using the single path /api/v1/graphql but after applying the passport middleware the APIs are working fine but i cant access the sandbox in my browser saying “Unauthorised” how to set the Authorization header even though i am passing from react client.

Is the community alive ???

Hey there @S4nfs! Looking at your code in the server context initialization, I can see that it isn’t accessing the request headers correctly.

The authorization header can be accessed in req.headers.authorization. You can refer to this code snippet in the docs. All other headers sent with your request can be accessed in req.headers.

i even hard code the user object in my context but not getting anything in my resolvers

How are you accessing it in your resolvers? This docs page has an example on how to do so.

Hope that helps!

Thanks for the response @MichelleMabuyo but I already fixed that. Anyways, the issue with apollo sandbox is there as if i add passport middleware for authentication then i am unable to access it in browser, it says Unauthorised. Also note that my apollo sandbox path is different from api. How can i set headers in apollo sandbox if i can’t access the GUI.

this is my store.jsx (React)

import { configureStore } from '@reduxjs/toolkit'
import authReducer, { reset } from '../features/auth/authSlice'
import crowdReducer, { resetCrowdState } from '../features/user/crowdSlice'
import onboarded_customerReducer, { resetBillingState } from '../features/auth/billingSlice'
import { ApolloClient, InMemoryCache, ApolloProvider, gql, createHttpLink, split } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { setContext } from '@apollo/client/link/context';
import { Provider } from 'react-redux'
import { getMainDefinition } from '@apollo/client/utilities';
import { createClient } from 'graphql-ws';
export const store = configureStore({
  reducer: {
    auth: authReducer,
    crowd: crowdReducer,
    onboarded_customer: onboarded_customerReducer,
  },
  devTools: process.env.VITE_NODE_ENV !== 'production',
})

export const resetAllState = () => (dispatch) => {
  dispatch(reset());
  dispatch(resetBillingState());
  dispatch(resetCrowdState());
};

const httpLink = new createHttpLink({
  uri: `${process.env.VITE_LOCALHOST}:${process.env.VITE_PORT}/api/v1/graphql`,
});

const authLink = setContext((_, { headers }) => {
  const client = localStorage.getItem('client');
  const token = client ? JSON.parse(client).token : null;
  return {
    headers: {
      ...headers,
      Authorization: token ? `Bearer ${token}` : "",
    }
  }
});


// Create a WebSocket client
const wsClient = createClient({
  url: `ws://localhost:${process.env.VITE_PORT}/api/v1/graphql`,
  connectionParams: () => ({
    token: JSON.parse(localStorage.getItem('client'))?.token,
  }),
});


// Create a WebSocket link and use split for proper link selection
const wsLink = new GraphQLWsLink(wsClient);
const link = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  authLink.concat(httpLink),
);

const client = new ApolloClient({
  link,
  cache: new InMemoryCache(),
});

// Wrap your app with ApolloProvider and Redux Provider
export const AppProviders = ({ children }) => {
  return (
    <ApolloProvider client={client}>
      <Provider store={store}>{children}</Provider>
    </ApolloProvider>
  );
};

Hey @S4nfs - I just checked the docs and it looks like your context object should actually be the second argument of the expressMiddleware function. Here is the docs page: API Reference: expressMiddleware - Apollo GraphQL Docs.

And the code snippet should look something like this:

expressMiddleware(server, {
    context: async ({ req }) => { 
      const user = "John Doe"; //hard-coded
      return { user };
 },
}),

If you try that with the hard-coded value, you should now see it in your resolvers context argument. Can you confirm that the hard-coded approach is working?

1 Like

Next, let’s figure out the Authorization error. Can you post a screenshot of the error you’re seeing with Sandbox?

I am getting “Unauthorised” text in my browser which is from the passport js. i want to confirm as in some stackoverflow questions people are saying that the passport cannot be used as a middleware to authenticate apollo requests and that why i am getting this error.

This is my passport JWT strategy that will verify from the Authorization headers:

// JWT Authentication middleware using passport and continue to next middleware
passport.use(
  new JWTStrategy({
    jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
    secretOrKey: process.env.AUTH_KEY
  }, async (jwt_payload, done) => {
    try {
      const user = await User.findById(jwt_payload.user
        ._id)
      if (user && user.provider === jwt_payload.user.provider) {
        return done(null, user)
      } else {
        return done(null, false)
      }
    } catch (error) {
      done(error)
    }
  }
  )
)

and the path where i am using it as the middleware

app.use('/api/v1/graphql', passport.authenticate('jwt', { session: false }), expressMiddleware(apolloServer));

right now i am using the same path for the apollo endpoints and the apollo sandbox but somehow the passport is not authenticating the requests and i am seeing “Unauthorised” in my sandbox. API are working fine which is weird.

Are you saying the production works with your passport but not the sandbox?
In your sandbox are you manually entering in the headers the Authorization header/token when executing your queries/mutations?

I can’t help more than this since I am not familiar with the Passport.

Update: I saw this post, it could be useful: node.js - Apollo Server with Passport JWT - Stack Overflow

express - How to setup authentication with graphql, and passport but still use Playground - Stack Overflow

Not sure what the right approach is.