RisingStack / graffiti-mongoose

⚠️ DEVELOPMENT DISCONTINUED - Mongoose (MongoDB) adapter for graffiti (Node.js GraphQL ORM)
https://risingstack-graffiti.signup.team/
MIT License
382 stars 52 forks source link

How to use Express Middleware for Authentication/Authorization. #186

Closed cellis closed 7 years ago

cellis commented 7 years ago

Hi, I'm wondering how to do authorization with Express Middleware. I know the docs suggest using the hooks provided, but I'm unsure how to proceed with e.g. a passport-js enabled api. How, for example, do I pass the req.user to the context, as suggested In this article. TLDR; i'm looking for how to do something like this:

app.post('/graphql', (req, res) => {
  graphql(schema, req.body, { user: req.user })
  .then((data) => {
    res.send(JSON.stringify(data));
  });
});

Because graffiti-express / graffiti-mongoose abstracts over the graphql library and provides itself as middleware, I'm not sure how to insert the req.user.

I've read the README, but understanding how the hooks work is also somewhat difficult to an uninitiated user. What are the meanings of singular, viewer, plural, and mutation and why do we need to provide each of them? How could I use the request argument in conjuction with passport.authenticate to authenticate the user?

Any help appreciated!

cellis commented 7 years ago

Upon closer inspection, i have figured this out. However the README should be updated as the request parameter is undefined, while the root parameter actually contains the request.

const schema = getSchema([User], { hooks: {
    singular: {
        pre: (next, root, args, request)=> {
            console.log(root.request.user);  // => { email: 'user@example.com' }
            console.log(request);            // => undefined
            next();
        },
        post: (next, value)=> {
            //...
            next();
        }
    }
}});

Perhaps the shifted signature of graphql@0.6.2 has something to do with it. Here's the signature at 0.6.2:

graphql(schema, requestString, rootValue, contextValue, variableValues, operationName) {

And here's what graffiti passes:

return graphql(schema, query, { request }, context, parsedVariables)...

at L41

katopz commented 7 years ago

@cellis for mutation is even more weird doc

  mutation: {
    pre: (next, args, context) => next(),
    post: (next, value, args, context) => next()
  }

actually

  mutation: {
    pre: (next, args, context, what) => {
      console.log(what.rootValue.request.user);
      next();
    },
    post: (next, value, args, context) => {
      next();
    }
  }

And context is empty {}

tothandras commented 7 years ago

Thanks for finding this! PR's are welcomed if you have time to fix it @cellis, @katopz!

katopz commented 7 years ago

@tothandras I'm not sure PR for read me is enough, something really wrong there Here's workaround for me atm

const hooks = {
  viewer: {
    pre: (next, args, foo, context, options) => {
      console.log(options.rootValue.request.user);
      next()
    },
    post: (next, value) => {
      next()
    }
  },
  singular: {
    pre: (next, root, args, request, options) => {
      console.log(options.rootValue.request.user);
      next();
    },
    post: (next, value, args, context) => {
      next();
    }
  },
  plural: {
    pre: (next, root, args, request, options) => {
      console.log(options.rootValue.request.user);
      next();
    },
    post: (next, value, args, context) => {
      next();
    }
  },
  mutation: {
    pre: (next, args, context, options) => {
      console.log(options.rootValue.request.user);
      next();
    },
    post: (next, value, args, context) => {
      next();
    }
  }
}

module.exports = hooks

Somehow nested viewer change an order of arguments, and here's a problem

  plural: {
    pre: (next, root, args, request, options) => {

      // Normal plural query will return `root.request.user` properly
      // But mutation with viewer plural will return `{_type: "Viewer", id: "viewer"}` 
      // and `root.request` will be `undefined`
      console.log(root.request.user);

      // Working both query, mutation
      console.log(options.rootValue.request.user);

      next();
    }

Took me a day to figure this out 🤕