Solo Solo - 6 months ago 49
Node.js Question

How to check permissions and other conditions in GraphQL query?

How could I check if user has permission to see or query something? I have no idea how to do this.


  • In
    args
    ? How would that even work?

  • In
    resolve()
    ? See if user has permission and somehow
    eliminate/change some of the args?



Example:

If user is "visitor", he can only see public posts, "admin" can see everything.

const userRole = 'admin'; // Let's say this could be "admin" or "visitor"

const Query = new GraphQLObjectType({
name: 'Query',
fields: () => {
return {
posts: {
type: new GraphQLList(Post),
args: {
id: {
type: GraphQLString
},
title: {
type: GraphQLString
},
content: {
type: GraphQLString
},
status: {
type: GraphQLInt // 0 means "private", 1 means "public"
},
},

// MongoDB / Mongoose magic happens here
resolve(root, args) {
return PostModel.find(args).exec()
}
}
}
}
})





Update - Mongoose model looks something like this:

import mongoose from 'mongoose'

const postSchema = new mongoose.Schema({
title: {
type: String
},
content: {
type: String
},
author: {
type: mongoose.Schema.Types.ObjectId, // From user model/collection
ref: 'User'
},
date: {
type: Date,
default: Date.now
},
status: {
type: Number,
default: 0 // 0 -> "private", 1 -> "public"
},
})

export default mongoose.model('Post', postSchema)

Answer

You can check a user's permission in the resolve function or in the model layer. Here are the steps you have to take:

  1. Authenticate the user before executing the query. This is up to your server and usually happens outside of graphql, for example by looking at the cookie that was sent along with the request. See this Medium post for more details on how to do this using Passport.js.
  2. Add the authenticated user object or user id to the context. In express-graphql you can do it via the context argument:

    app.use('/graphql', (req, res) => {
      graphqlHTTP({ schema: Schema, context: { user: req.user } })(req, res);
    }
    
  3. Use the context inside the resolve function like this:

    resolve(parent, args, context){
      if(!context.user.isAdmin){
        args.isPublic = true;
      }
      return PostModel.find(args).exec();
    }
    

You can do authorization checks directly in resolve functions, but if you have a model layer, I strongly recommend implementing it there by passing the user object to the model layer. That way your code will be more modular, easier to reuse and you don't have to worry about forgetting some checks in a resolver somewhere.

For more background on authorization, check out this post (also written by myself): Auth in GraphQL - part 2