我有一个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
})));
答案 0 :(得分:4)
我有以下工作。我遇到的一些问题是确保我的谷歌api已启用并启用了正确的范围。我也只使用auth端点上的护照中间件,并使用isAuthenticated中间件检查会话是否经过身份验证,如果没有重定向到auth端点。还将请求对象放入上下文中,以便解析器可以使用它来潜在地授权用户。您当然需要更新用户查找,因为我只是传递模拟数据。
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);
});
答案 1 :(得分:0)
vbranden的答案非常好,这是这个答案的基础。但是,他的答案还有很多其他代码可以使解决方案混淆不清。我不想搞砸它,因为它提供了更完整的事物视图,但希望这个答案通过更直接的方式以自己的方式有所帮助。但同样,这个解决方案的所有功劳都属于vbranden(请相应地提出他的回答)。
如果使用适当的签名(isAuthenticated
)创建request, response, next
函数,则可以在设置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
}))
);