Javascript 具有中继的身份验证和访问控制

Javascript 具有中继的身份验证和访问控制,javascript,graphql,relayjs,graphql-js,Javascript,Graphql,Relayjs,Graphql Js,Facebook的官方说法是Relay是“.”在Relay存储库中的所有示例中,身份验证和访问控制是一个独立的问题。实际上,我还没有找到一种简单的方法来实现这种分离 中继存储库中提供的示例都具有根模式,其中有一个viewer字段,该字段假定有一个用户。用户可以访问所有内容 然而,在现实中,一个应用程序有许多用户,每个用户对每个节点都有不同程度的访问 假设我在JavaScript中有这个模式: export const Schema=new GraphQLSchema({ 查询:新的GraphQL

Facebook的官方说法是Relay是“.”在Relay存储库中的所有示例中,身份验证和访问控制是一个独立的问题。实际上,我还没有找到一种简单的方法来实现这种分离

中继存储库中提供的示例都具有根模式,其中有一个
viewer
字段,该字段假定有一个用户。用户可以访问所有内容

然而,在现实中,一个应用程序有许多用户,每个用户对每个节点都有不同程度的访问

假设我在JavaScript中有这个模式:

export const Schema=new GraphQLSchema({
查询:新的GraphQLObjectType({
名称:“查询”,
字段:()=>({
节点:nodeField,
用户:{
类型:新GraphQLObjectType({
名称:“用户”,
args:{
//要查询的用户的“id”
id:{type:new GraphQLNonNull(GraphQLID)},
//标识正在查询的用户
会话:{type:new GraphQLInputObjectType({…})},
},
解析:({id,session})=>{
//给定`session,获取具有`id的用户`
返回data.getUser({id,session});
}
字段:()=>({
姓名:{
类型:GraphQLString,
解析:用户=>{
//“会话”是否有权访问此用户的
//名字?
用户名
}
}
})
})
}
})
})
});
从查询用户的角度来看,有些用户是完全私有的。其他用户可能只向查询用户公开某些字段。因此,要获得用户,客户端不仅必须提供他们要查询的用户ID,还必须标识自己,以便进行访问控制

这似乎很快变得复杂起来,因为控制访问的需求从图表中逐渐显现出来

此外,我需要控制每个根查询的访问,比如
nodeField
。我需要确保实现
nodeInterface
的每个节点


所有这些似乎都是重复性的工作。是否有任何已知的模式可以简化这一点?我想得不对吗?

不同的应用程序对访问控制的形式有着非常不同的要求,因此在基本中继框架或GraphQL参考实现中烘焙一些东西可能没有意义

我看到的一种非常成功的方法是将隐私/访问控制烘焙到数据模型/数据加载程序框架中。每次加载对象时,不仅要按id加载,还要提供查看器的上下文。如果查看器看不到对象,它将无法加载,就像它不存在一样,以防止甚至泄漏对象的存在。该对象还保留查看器上下文,某些字段可能具有受限访问权限,在从该对象返回之前会检查这些字段。在较低级别的数据加载机制中对其进行烘焙有助于确保较高级别的product/GraphQL代码中的bug不会泄漏私有数据

在一个具体的例子中,我可能不允许看到某个用户,因为他阻止了我。一般来说,你可能会被允许见他,但不会看到他的电子邮件,因为你和他不是朋友

在类似这样的代码中:

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.")
)
试图在GraphQL级别实现可见性有很大的可能泄漏信息。考虑在Facebook的GraphQL实现中访问用户的多种方式:

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

所有这些查询都可能加载用户名,如果用户阻止了您,则所有查询都不应返回用户名或用户名。对所有这些字段进行访问控制,并且在任何地方都不忘记检查,这会导致在某个地方漏掉检查。

我发现,如果您使用GraphQL,则处理身份验证很容易,在对架构执行查询时,GraphQL会传递给执行引擎。该值在所有执行级别都可用,并可用于存储访问令牌或标识当前用户的任何内容

如果使用的是中间件,则可以在GraphQL中间件之前的中间件中加载会话,然后配置GraphQL中间件以将该会话放入根值:

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 }
})));
然后,此会话可在架构中的任何深度上使用:

new GraphQLObjectType({
  name: 'MyType',
  fields: {
    myField: {
      type: GraphQLString,
      resolve(parentValue, _, { rootValue: { session } }) {
        // use `session` here
      }
    }
  }
});
您可以将其与“面向查看器”的数据加载相结合,以实现访问控制。查看有助于创建此类数据加载对象并提供批处理和缓存的

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)),
  };
}

如果有人对此主题有问题:我根据dimadima的答案为Relay/GraphQL/express身份验证做了一个示例。它使用express中间件和GraphQL变体将会话数据(用户ID和角色)保存在cookie中

我认为如果中继中有一些中间件位于执行引擎之上,并根据会话信息重写查询AST,那将非常酷。你有没有得到一个好的示例/答案?我正在寻找有关中继令牌身份验证(无会话)的信息,但很难找到anything@GreenRails这里没有,但我知道怎么做了。非常好!基本上,对我来说,关键是要弄清楚,您可以将内容放入GraphQL“rootValue”中,它可以在所有级别的分辨率下使用。如果您使用的是express中间件,它的操作方式如下:。对于任何实现都可以这样做。然后,根据下面的答案,您还可以采用“面向查看器”的方法来加载数据,以协助ACL。是一个很好的帮助工具。@GreenRails刚刚添加了一个答案应该有一个github回购来演示这个问题