如何使用 graphql 和护照设置身份验证但仍使用 Playground

Posted

技术标签:

【中文标题】如何使用 graphql 和护照设置身份验证但仍使用 Playground【英文标题】:How to setup authentication with graphql, and passport but still use Playground 【发布时间】:2020-12-08 13:21:20 【问题描述】:

向我们的后端 Graphql 服务器添加身份验证后,“Schema”和“Docs”在 Graphql Playground 中不再可见。向 Playground 中的“HTTP HEADERS”添加令牌时执行查询在经过身份验证而不是在用户未经过身份验证时可以正常工作,所以没关系。

我们禁用了 Apollo-server 的内置 Playground,并使用中间件 graphql-playground-middleware-express 来使用不同的 URL 并绕过身份验证。我们现在可以浏览 Playground 并使用它,但我们无法读取那里的“Schema”或“Docs”。

尝试启用 introspection 并没有解决这个问题。在apollo-serverContext 中调用passport.authenticate() 会更好吗?还有一个名为passport-graphql 的工具,但它适用于本地策略,可能无法解决问题。在调用 Playground 路由之前,我也曾tried setting the token in the header,但这没有用。

我们对此有点迷茫。感谢您为我们提供的任何见解。

相关代码:

// index/ts
import passport from 'passport'
import expressPlayground from 'graphql-playground-middleware-express'

const app = express()
app.use(cors( origin: true ))
app.get('/playground', expressPlayground( endpoint: '/graphql' ))
app.use(passport.initialize())
passport.use(bearerStrategy)

app.use(
  passport.authenticate('oauth-bearer',  session: false ),
  (req, _res, next) =>  next() 
)
;(async () => 
    await createConnections()
    const server = await new ApolloServer(
      schema: await getSchema(),
      context: ( req ) => ( getUser: () => req.user, ),
      introspection: false,
      playground: false,
    )
    server.applyMiddleware( app, cors: false )

    app.listen( port: ENVIRONMENT.port , () =>  console.log(`Server ready`) )      
)()
// passport.ts
import  IBearerStrategyOptionWithRequest, BearerStrategy, ITokenPayload  from passport-azure-ad'
import  Account  from '@it-portal/entity/Account'

export const bearerStrategy = new BearerStrategy( config,
  async (token: ITokenPayload, done: CallableFunction) => 
    try 
      if (!token.oid) throw 'token oid missing'

      const knownAccount = await Account.findOne( accountIdentifier: token.oid )
      if (knownAccount) return done(null, knownAccount, token)

      const account = new Account()
      account.accountIdentifier = token.oid
      account.name = token.name
      account.userName = (token as any).preferred_username
      const newAccount = await account.save()
      return done(null, newAccount, token)
     catch (error) 
      console.error(`Failed adding the user to the request object: $error`)
    
  
)

【问题讨论】:

【参考方案1】:

感谢this SO answer,我想通了。关键是不要在 Express 上使用 passport 作为中间件,而是在 Graphql Context 中使用它。

在下面的示例代码中,您可以看到 Promise getUser 用于进行护照身份验证,用于 ApolloServer 的 Context。这样,在dev 模式下运行时,仍然可以到达 Playground,并且仍然可以访问“Schema”端“Docs”。

根据 Apollo docs 部分“将用户信息放在上下文中”,这也是首选方式。

// apollo.ts
passport.use(bearerStrategy)

const getUser = (req: Express.Request, res: Express.Response) =>
  new Promise((resolve, reject) => 
    passport.authenticate('oauth-bearer',  session: false , (err, user) => 
      if (err) reject(err)
      resolve(user)
    )(req, res)
  )

const playgroundEnabled = ENVIRONMENT.mode !== 'production'

export const getApolloServer = async () => 
  return new ApolloServer(
    schema,
    context: async ( req, res ) => 
      const user = await getUser(req, res)
      if (!user) throw new AuthenticationError('No user logged in')
      console.log('User found', user)

      return  user 
    ,
    introspection: playgroundEnabled,
    playground: playgroundEnabled,
  )

最好的是,您只需要两个函数即可:passport.use(BearerStrategy)passport.authenticate()。这是因为没有使用会话,所以我们不需要将其添加为 Express 中间件。

// index/ts
const app = express()
app.use(cors( origin: true ))

;(async () => 
    await createConnections()
    const server = await getApolloServer()
    server.applyMiddleware( app, cors: false )

    app.listen( port: ENVIRONMENT.port , () =>  console.log(`Server ready`) )      
)()

我希望这可以帮助其他有同样问题的人。

【讨论】:

以上是关于如何使用 graphql 和护照设置身份验证但仍使用 Playground的主要内容,如果未能解决你的问题,请参考以下文章

如何运行 github-actions 步骤,即使上一步失败,但仍使作业失败

隐藏嵌套div的滚动条,但仍使其可滚动[重复]

Facebook iOS SDK 和护照-facebook 身份验证

使用护照 + JWT 或会话保护 GraphQL API? (举例)

Apollo express 没有快速护照会话

未知的身份验证策略护照