Apollo 服务器 - 仅使用 Passport-JWT 将身份验证应用于某些解析器

Posted

技术标签:

【中文标题】Apollo 服务器 - 仅使用 Passport-JWT 将身份验证应用于某些解析器【英文标题】:Apollo Server - Apply Authentication to Certain Resolvers Only with Passport-JWT 【发布时间】:2019-06-25 14:05:21 【问题描述】:

我目前有一个 Node.js 后端,运行 Express 和 Passport.js 以进行身份​​验证,并且正在尝试使用 Apollo Server 切换到 GraphQL。我的目标是实现我目前使用的相同身份验证,但无法弄清楚如何在为其他人启用授权的同时公开某些解析器。 (我已经尝试广泛研究这个问题,但到目前为止还没有找到合适的解决方案。)

这是我目前的代码:

我的智威汤逊策略:

const opts = ;
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = JWT_SECRET;

module.exports = passport => 
  passport.use(
    new JwtStrategy(opts, async (payload, done) => 
      try 
        const user = await UserModel.findById(payload.sub);
        if (!user) 
          return done(null, false,  message: "User does not exist!" );
        
        done(null, user);
       catch (error) 
        done(err, false);
      
    )
  );

我的 server.js 和 Apollo 配置: (我目前正在从 HTTP 标头中提取不记名令牌并使用上下文对象将其传递给我的解析器):

const apollo = new ApolloServer(
  typeDefs,
  resolvers,
  context: async ( req ) => 
    let authToken = "";

    try 
      if (req.headers.authorization) 
        authToken = req.headers.authorization.split(" ")[1];
      
     catch (e) 
      console.error("Could not fetch user info", e);
    

    return 
      authToken
    ;
  
);

apollo.applyMiddleware( app );

最后,我的解析器:

exports.resolvers = 
  Query: 
    hello() 
      return "Hello world!";
    ,
    async getUserInfo(root, args, context) 
      try 
        const  id  = args;
        let user = await UserModel.findById(id);
        return user;
       catch (error) 
        return "null";
      
    ,
    async events() 
      try 
        const eventsList = await EventModel.find();
        return eventsList;
       catch (e) 
        return [];
      
    
  
;

我的目标是将某些查询(例如第一个(“hello”))公开,同时将其他查询限制为仅使用有效承载令牌的请求。但是,我我不确定如何在使用 Passport.js 和 Passport-JWT 的解析器中实现此授权(通常是通过将中间件添加到某些端点来完成,但是因为我只有一个端点 (/graphql) 在此示例中,该选项将所有查询限制为仅经过身份验证的用户,这不是我想要的。我必须以某种方式在解析器中执行授权,但不确定如何使用 Passport.js 中可用的工具执行此操作.)

非常感谢任何建议!

【问题讨论】:

【参考方案1】:

我将创建一个模式指令来授权查询字段定义,然后在我想要应用授权的任何地方使用该指令。示例代码:

class authDirective extends SchemaDirectiveVisitor 
    visitObject(type) 
        this.ensureFieldsWrapped(type);
        type._requiredAuthRole = this.args.requires;
    

    visitFieldDefinition(field, details) 
        this.ensureFieldsWrapped(details.objectType);
        field._requiredAuthRole = this.args.requires;
    

    ensureFieldsWrapped(objectType) 
        // Mark the GraphQLObjectType object to avoid re-wrapping:
        if (objectType._authFieldsWrapped) return;
        objectType._authFieldsWrapped = true;

        const fields = objectType.getFields();

        Object.keys(fields).forEach(fieldName => 
            const field = fields[fieldName];
            const 
                resolve = defaultFieldResolver
             = field;
            field.resolve = async function (...args) 
                // your authorization code 
                return resolve.apply(this, args);
            ;
        );
    


并在类型定义中声明这个

directive @authorization(requires: String) on OBJECT | FIELD_DEFINITION

在您的架构中映射架构指令

....
resolvers,
schemaDirectives: 
authorization: authDirective

然后在你的 api 端点或任何对象上使用它

Query: 
    hello  ... 
    getuserInfo():Result @authorization(requires:authToken) ... 
    events():EventResult @authorization(requires:authToken) ... 
  ;

【讨论】:

太棒了,谢谢!今天下午我会试试看。 另外,我是否将我的 JWT 策略代码粘贴到 authDirective 类中(您在其中编写了“//您的授权代码”)?这不是问题吗,因为该代码被设计为作为 Express 中间件运行? @GaborSzekely,不确定 JWT 策略,但我相信您可以从那个地方调用任何其他方法?如果您遇到任何问题,请发布。 您能帮我了解我将在哪里声明 authToken 或模式指令将如何接收它吗?我需要从 HTTP 标头中对其进行解码,但是如何将其传递给 GraphQL? 它存在于header中,通过args方法可以直接访问visitfielddefinition()。参考此hackernoon.com/…。它正是你想要的。

以上是关于Apollo 服务器 - 仅使用 Passport-JWT 将身份验证应用于某些解析器的主要内容,如果未能解决你的问题,请参考以下文章

Apollo GraphQL:MQTT 订阅代理以仅提供已发布的数据

我可以有条件地使用passport-jwt吗?

GraphQL 中的社交身份验证

vue-apollo:GraphQL 查询仅在使用 <ApolloQuery> 标签时运行。永远不会在脚本代码中工作。 this.$apollo.queries 为空

我如何通过 nodejs 通过 apollo-server 从服务器获取所有数据

如何使用 Apollo Server 清除设置的 cookie