Apollo 服务器 - 仅使用 Passport-JWT 将身份验证应用于某些解析器
Apollo Server - Apply Authentication to Certain Resolvers Only with Passport-JWT
我目前有一个 Node.js back-end 运行 Express Passport.js 用于身份验证,我正在尝试使用 Apollo Server 切换到 GraphQL。我的目标是实现我当前使用的相同身份验证,但无法弄清楚如何在为其他解析器启用授权时保留某些解析器 public。 (我已经尝试广泛研究这个问题,但到目前为止还没有找到合适的解决方案。)
这是我目前的代码:
我的 JWT 策略:
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 headers 中提取承载令牌并使用上下文 object 将其传递给我的解析器):
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") public,同时将其他查询限制为仅具有有效承载令牌的请求. 但是,我不确定如何在解析器中具体使用 Passport.js 和 Passport-JWT 实现此授权(通常是通过向某些端点添加中间件来完成,但是,由于在此示例中我只有一个端点 (/graphql),因此该选项会将所有查询限制为仅对经过身份验证的用户进行查询,这不是我想要的。我必须执行以某种方式在解析器中授权,但不确定如何使用 Passport.js 中可用的工具来执行此操作。)
非常感谢任何建议!
我会创建一个模式指令来授权对字段定义的查询,然后在我想应用授权的任何地方使用该指令。示例代码:
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) {...}
};
我目前有一个 Node.js back-end 运行 Express Passport.js 用于身份验证,我正在尝试使用 Apollo Server 切换到 GraphQL。我的目标是实现我当前使用的相同身份验证,但无法弄清楚如何在为其他解析器启用授权时保留某些解析器 public。 (我已经尝试广泛研究这个问题,但到目前为止还没有找到合适的解决方案。)
这是我目前的代码:
我的 JWT 策略:
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 headers 中提取承载令牌并使用上下文 object 将其传递给我的解析器):
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") public,同时将其他查询限制为仅具有有效承载令牌的请求. 但是,我不确定如何在解析器中具体使用 Passport.js 和 Passport-JWT 实现此授权(通常是通过向某些端点添加中间件来完成,但是,由于在此示例中我只有一个端点 (/graphql),因此该选项会将所有查询限制为仅对经过身份验证的用户进行查询,这不是我想要的。我必须执行以某种方式在解析器中授权,但不确定如何使用 Passport.js 中可用的工具来执行此操作。)
非常感谢任何建议!
我会创建一个模式指令来授权对字段定义的查询,然后在我想应用授权的任何地方使用该指令。示例代码:
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) {...}
};