如何使用 Passport 对 GraphQL 端点进行身份验证?
Posted
技术标签:
【中文标题】如何使用 Passport 对 GraphQL 端点进行身份验证?【英文标题】:How can I authenticate a GraphQL endpoint with Passport? 【发布时间】:2018-06-02 15:28:36 【问题描述】:我有一个 GraphQL 端点:
app.use('/graphql', graphqlHTTP(request => (
graphiql: true,
schema
)));
我还有一个用于登录的 Passport 路由(并处理回调,因为我使用的是 Google OAuth2):
this.app.get('/login', passport.authenticate('google'));
this.app.get('/auth/callback/google', ....
Passport 将用户添加到请求中,我可以在网上找到的所有文章都建议使用该方法在我的每个 GraphQL 解析器中进行身份验证:
resolve: (root, args, user ) =>
if (!user) throw new NotLoggedInError();
但是,当它应用于所有解析器时,必须将该逻辑添加到每个解析器是没有意义的,所以我希望以某种方式对整个端点进行身份验证。
问题是我不确定如何组合中间件。我尝试了以下方法,但它只是破坏了端点:
app.use('/graphql', passport.authenticate('google'), graphqlHTTP(request => (
graphiql: true,
schema
)));
【问题讨论】:
【参考方案1】:我有以下工作。我遇到的一些问题是确保启用了我的 google API 并启用了正确的范围。我也只在身份验证端点上使用护照中间件,并使用isAuthenticated
中间件来检查会话是否经过身份验证,如果没有重定向到身份验证端点。还将请求对象放入上下文中,以便解析器可以使用它来潜在地授权用户。您当然需要更新用户查找,因为我只是在传递模拟数据。
import express from "express";
import graphqlHTTP from "express-graphql";
import passport from "passport";
import cookieParser from "cookie-parser";
import session from "express-session";
import Strategy as GoogleStrategy from "passport-google-oauth20";
import buildSchema from "graphql";
const PORT = 5000;
const data = [
id: "1", name: "foo1" ,
id: "2", name: "foo2" ,
id: "3", name: "foo3" ,
];
const def = `
type Foo
id: String!
name: String
type Query
readFoo(id: String!): Foo
schema
query: Query
`;
const schema = buildSchema(def);
const fieldMap = schema.getType("Query").getFields();
fieldMap.readFoo.resolve = (source, args) =>
return data.filter(( id ) => id === args.id)[0] || null;
;
passport.serializeUser((user, done) =>
done(null, user);
);
passport.deserializeUser((obj, done) =>
done(null, obj);
);
passport.use(
new GoogleStrategy(
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: `http://localhost:$PORT/auth/google/callback`,
,
(accessToken, refreshToken, profile, cb) =>
return cb(null,
id: "1",
username: "foo@bar.baz",
googleId: profile.id,
);
)
);
function isAuthenticated(req, res, next)
return req.isAuthenticated() ? next() : res.redirect("/auth/google");
const app = express();
app.use(cookieParser());
app.use(
session(
secret: "sauce",
resave: false,
saveUninitialized: false,
)
);
app.use(passport.initialize());
app.use(passport.session());
app.get("/auth/fail", (req, res) =>
res.json( loginFailed: true );
);
app.get(
"/auth/google",
passport.authenticate("google", scope: ["profile"] )
);
app.get(
"/auth/google/callback",
passport.authenticate("google", failureRedirect: "/auth/fail" ),
(req, res) =>
res.redirect("/graphql");
);
app.use(
"/graphql",
isAuthenticated,
graphqlHTTP((req) => (
schema,
graphiql: true,
context: req,
))
);
app.listen(PORT, () =>
console.log("Started local graphql server on port ", PORT);
);
【讨论】:
对于未来的读者,那里有很多代码,但答案的关键是isAuthenticated
函数(以及它在app.use('/graphql', ...
调用中的使用方式)。【参考方案2】:
vbranden 的回答非常好,这是此回答的基础。但是,他的答案有很多其他代码使解决方案有些模糊。我不想弄乱它,因为它提供了更完整的事物视图,但希望这个答案会更直接地以自己的方式有所帮助。但同样,这个解决方案的所有功劳都属于 vbranden(请相应地支持他的答案)。
如果您使用适当的签名 (request, response, next
) 创建了 isAuthenticated
函数,那么您可以在设置 GraphQL 端点时“链接”该函数:
function isAuthenticated(req, res, next)
return req.isAuthenticated() ?
next() :
res.redirect('/auth/google');
app.use(
'/graphql',
isAuthenticated,
graphqlHTTP(req => (
schema,
graphiql: true,
context: req
))
);
【讨论】:
这不会使所有 graphql 端点都受到保护吗?如果我们只想保护选定的路由怎么办? 取决于您要完成的工作。我希望我的所有 api 都只能被登录用户访问,所以这种方法对我来说很有意义。 我希望在 graphql 端点内保护访客路由(登录、忘记密码、注册)。所以我可以将单个 api 端点与 apollo 客户端一起使用。 你为什么要保护那些?您想让您的用户登录以进入登录页面吗? 我的意思是我希望 graphql 同时拥有受保护和不受保护的路由。我可以将一个 api 端点附加到 apollo 客户端。如果我将登录路线与 graphql 分开,我需要使用 axios 或 fetch 之类的东西来登录……然后使用 gql 来查询其余数据。我不想在前端使用除了 graphql 之外的任何东西。以上是关于如何使用 Passport 对 GraphQL 端点进行身份验证?的主要内容,如果未能解决你的问题,请参考以下文章
Passport (oAuth2) 如何与 GraphQL (TypeGraphQL) 一起使用?
使用 express js、passport s 保护 GraphQL 查询