Authentication and Access Control with Relay

前端 未结 3 1300
被撕碎了的回忆
被撕碎了的回忆 2021-02-01 07:40

The official line from Facebook is that Relay is \"intentionally agnostic about authentication mechanisms.\" In all the examples in the Relay repository, authentication and acce

相关标签:
3条回答
  • 2021-02-01 08:16

    I found that handling authentication is easy if you make use of the GraphQL rootValue, which is passed to the execution engine when the query is executed against the schema. This value is available at all levels of execution and is useful for storing an access token or whatever identifies the current user.

    If you're using the express-graphql middleware, you can load the session in a middleware preceding the GraphQL middleware and then configure the GraphQL middleware to place that session into the root value:

    function getSession(req, res, next) {
      loadSession(req).then(session => {
        req.session = session;
        next();
      }).catch(
        res.sendStatus(400);
      );
    }
    
    app.use('/graphql', getSession, graphqlHTTP(({ session }) => ({
      schema: schema,
      rootValue: { session }
    })));
    

    This session is then available at any depth in the schema:

    new GraphQLObjectType({
      name: 'MyType',
      fields: {
        myField: {
          type: GraphQLString,
          resolve(parentValue, _, { rootValue: { session } }) {
            // use `session` here
          }
        }
      }
    });
    

    You can pair this with "viewer-oriented" data loading to achieve access control. Check out https://github.com/facebook/dataloader which helps create this kind of data loading object and provides batching and caching.

    function createLoaders(authToken) {
      return {
        users: new DataLoader(ids => genUsers(authToken, ids)),
        cdnUrls: new DataLoader(rawUrls => genCdnUrls(authToken, rawUrls)),
        stories: new DataLoader(keys => genStories(authToken, keys)),
      };
    }
    
    0 讨论(0)
  • 2021-02-01 08:23

    Different applications have very different requirements for the form of access control, so baking something into the basic Relay framework or GraphQL reference implementation probably doesn't make sense.

    An approach that I have seen pretty successful is to bake the privacy/access control into the data model/data loader framework. Every time you load an object, you wouldn't just load it by id, but also provide the context of the viewer. If the viewer cannot see the object, it would fail to load as if it doesn't exist to prevent even leaking the existence of the object. The object also retains the viewer context and certain fields might have restricted access that are checked before being returned from the object. Baking this in the lower level data loading mechanism helps to ensure that bugs in higher level product / GraphQL code doesn't leak private data.

    In a concrete example, I might not be allowed to see some User, because he has blocked me. You might be allowed to see him in general, but no his email, since you're not friends with him.

    In code something like this:

    var viewer = new Viewer(getLoggedInUser());
    User.load(id, viewer).then(
      (user) => console.log("User name:", user.name),
      (error) => console.log("User does not exist or you don't have access.")
    )
    

    Trying to implement the visibility on GraphQL level has lots of potential to leak information. Think of the many way to access a user in GraphQL implementation for Facebook:

    node($userID) { name }
    node($postID) { author { name } }
    node($postID) { likers { name } }
    node($otherUserID) { friends { name } }
    

    All of these queries could load a user's name and if the user has blocked you, none of them should return the user or it's name. Having the access control on all these fields and not forgetting the check anywhere is a recipe for missing the check somewhere.

    0 讨论(0)
  • 2021-02-01 08:28

    If anyone has problems with this topic: I made an example repo for Relay/GraphQL/express authentication based on dimadima's answer. It saves session data (userId and role) in a cookie using express middleware and a GraphQL Mutation

    0 讨论(0)
提交回复
热议问题