node.jsexpressgraphqlapolloapollo-server

Implementing Middleware to Retrieve JWT Tokens from Header in a GraphQL Server with Node.js and Apollo


I'm trying to create a middleware to receive a token from the header, but it never seems to trigger, and I never see the 'hello' log message I inserted at the top of the middleware.

const jwt = require('jsonwebtoken');
const { GraphQLError } = require('graphql');

module.exports = async ({ req }) => {
  console.log('hello');
  const authHeader = req.headers.authorization || '';
  if (!authHeader.startsWith('Bearer ')) {
    throw new GraphQLError('Authorization header must be provided and must be Bearer token');
  }

  const token = authHeader.split('Bearer ')[1];
  if (!token) {
    throw new GraphQLError('Authentication token must be provided');
  }

  try {
    const user = jwt.verify(token, process.env.JWT_SECRET_KEY);
    return { user };
  } catch (err) {
    console.log(err);
    throw new GraphQLError('Invalid/Expired token');
  }
};

this is how i setted up the server; jwtmiddleware is the name of my middleware

const express = require('express')
const app = express()
const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone')
const { ApolloServerPluginDrainHttpServer } = require('@apollo/server/plugin/drainHttpServer');
const { schema } = require('./schema.js');
const { resolvers } = require('./resolvers.js');
const http = require('http');
const cors = require('cors');
const { json } = require('body-parser');
const { expressMiddleware } = require('@apollo/server/express4');
const mongoose = require('mongoose')
const jwtmiddleware = require('./middleware/auth.js')
require('dotenv').config(); 

app.use(express.json())
app.use(express.urlencoded({ extended: false }))

const start = async() => {
  await mongoose.connect('mongodb://localhost:27017/SamichayUsers',{
      useNewUrlParser: true,
      useUnifiedTopology: true
  });
}

const httpServer = http.createServer(app);
const server = new ApolloServer({
  schema,
  resolvers,
  plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});

const startServer = async () => {
  await startStandaloneServer(server, {
    listen: { port: 5552 },
    context: jwtmiddleware
  });

  app.use(
      '/graphql',
      cors(),
      json(), 
      expressMiddleware(server, {
        context: async ({ req }) => ({ token: req.headers.token }),
      })
    );

  app.listen(5551, () => {
    console.log('Connesso alla porta: 5551');
  });
}


start()
startServer();

module.exports = {app}

and this is the query i am trying to send from sandbox

    UpdateUserName: async (parent, { _id, newName }, context) => {
      console.log(context);
      const user = await User.findByIdAndUpdate(_id, { userName: newName },);
      return user;
    }

sandbox

I created a plugin that works as a middleware but i would love to know why my middleware are not triggered anyways

const jwt = require('jsonwebtoken');
require('dotenv').config(); 

const jwtmiddleware = {
  async requestDidStart(requestContext) {
    try {
      const operationType = requestContext.request.operationName;
      // Avoid login, register, and GET query from ApolloSandbox
      if (['IntrospectionQuery', 'LogUser', 'CreateUser', 'CheckUserIsRegistered'].includes(operationType)) {
        return;
      }
      const headers = requestContext.request.http.headers;
      const authHeader = headers.get('authorization');
      if (authHeader) {
        const token = authHeader.split(' ')[1];
        jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => {
          if (err) {
            throw new AuthenticationError('Invalid token');
          }
          console.log('Token verified: ', decoded);
        });
      } else {
        throw new AuthenticationError('Authorization header not present');
      }
    } catch (error) {
      console.error('Error during token verification: ', error.message);
      throw error;
    }
  },
};

module.exports = jwtmiddleware;

const server = new ApolloServer({
  schema,
  resolvers,
  plugins: [ApolloServerPluginDrainHttpServer({ httpServer }), jwtmiddleware],
});

Solution

  • In Apollo-server 4.x the context is no longer an option to ApolloServer, instead it's an option to startStandAloneServer.

    You can use your jwtmiddleware function as context instead of as middleware.

    Try:

    const startServer = async () => {
      await startStandaloneServer(server, {
        listen: { port: 5552 },
        context: jwtmiddleware
      });