0

I have a GraphQL API with mixed public and private queries and mutation. I'm looking for a way to check whether an operation requires the user to be authenticated and also a permission checker, so that the user can only modify his own data.

I noticed that the resolver function has a fourth argument, info that includes path.key which returns the name of the operation (documented here).

My solution was to add a checker function inside every resolver, like so:

// modify user details
resolve: async (parent, args, { mongo: { User }, loggedUser }, info) => {
    // auth check
    authChecker(info.path.key, loggedUser, args.id);

  // continue resolving
},

And in another file:

function authChecker(operationName, loggedUser, userId) {
  if (PUBLIC_OPERATIONS.includes(operationName) {
    // public operation
    return true;
  } else {
    // private operation
    if (args.id) {
      // private operation that requires a permission check
      ...
    } else {
      // private operation that only requires user to be logged in
      ...
    }
  }
}

The function either returns true or throws an error if conditions are not met.

I was wondering if this was an ok solution or if there was a way that this could be done with a middleware somehow so that I wouldn't have to repeat the code in every resolver. The problem is that I wouldn't have access to the operation name if I'd use a middleware. Any suggestions?

Norbert
  • 2,741
  • 8
  • 56
  • 111

1 Answers1

1

Utilizing middleware should be possible, but would be painful because you would have to parse the query yourself. I think the cleanest way is to utilize a schema-level resolver, which is available with graphql-tools.

const {makeExecutableSchema, addSchemaLevelResolveFunction} = require('graphql-tools')

const schema = makeExecutableSchema({typeDefs, resolvers})
addSchemaLevelResolveFunction(schema, (root, args, context, info) => {
  // check info to see if query is private and throw if user isn't authenticated
})
// use the schema like normal
app.use('/graphql', graphqlHTTP({schema}))

The resolver doesn't need to return anything; it just needs to throw or return a rejected Promise when authentication fails. For more info about generating a schema with graphql-tools, check the docs here.

Daniel Rearden
  • 80,636
  • 11
  • 185
  • 183
  • Awesome! I ended up using this to check if auth is needed for an operation. And I kept the permission checker inside each individual resolver. Thanks! – Norbert Dec 11 '17 at 06:30