【问题标题】:Firestore cloud functions apollo graphql authenticationFirestore 云功能 apollo graphql 身份验证
【发布时间】:2020-08-17 13:33:34
【问题描述】:

我需要帮助让我的 Firebase Apollo/GraphQL Cloud Function 进行身份验证和接收查询请求。

当权限设置为 allUsers 时,Apollo/GraphQL 函数可以正常工作(在 Playground 中测试)。在将权限设置为 allAuthenticatedUsers 并尝试发送经过身份验证的查询后,我收到以下错误响应:

Bearer error="invalid_token" error_description="The access token could not be verified"

我相信我在客户端发送的请求,或者对 ApolloServer 的验证和“上下文”的处理方面犯了错误。我已确认初始用户令牌是正确的。我目前的理论是我发送了错误的标头,或者在客户端或服务器级别以某种方式弄乱了语法。

解释我认为适当的请求流程应该是:

  1. 在客户端生成令牌
  2. 从客户端发送的以令牌为标头的查询
  3. ApolloServer 云函数接收请求
  4. 令牌已通过 Firebase 验证,提供新的已验证标头令牌
  5. 服务器接受带有新验证标头令牌的查询并返回数据

如果有人能解释如何将经过身份验证的有效客户端查询发送到 Firebase Apollo/GraphQL 云函数,我们将不胜感激。下面的服务器和客户端代码。

Server.js (ApolloServer)

/* Assume proper imports */
/* Initialize Firebase Admin SDK */
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "[db-url]",
});

/* Async verification with user token */
const verify = async (idToken) => {
  var newToken = idToken.replace("Bearer ", "");
  let header = await admin.auth().verifyIdToken(newToken)
    .then(function(decodedToken) {
      let uid = decodedToken.uid;
      // Not sure if I should be using the .uid from above as the token?
      // Also, not sure if returning the below object is acceptable, or
      // if this is even the correct header to send to firebase from Apollo
      return {
        "Authorization": `Bearer ${decodedToken}`
      }
    }).catch(function(error) {
      // Handle error
      return null
    });
  return header
}

/* Server */
function gqlServer() {
  const app = express();

  const apolloServer = new ApolloServer({
    typeDefs: schema,
    resolvers,
    context: async ({ req, res }) => {
      const verified = await verify(req.headers.Authorization)
      console.log('log verified', verified)
      return {
        headers: verified ? verified: '',
        req, 
        res,
      }
    },
    // Enable graphiql gui
    introspection: true,
    playground: true
  });
  
  apolloServer.applyMiddleware({app, path: '/', cors: true});

  return app;
}

export default gqlServer;

Client.js (ApolloClient)

使用these instructions构造的客户端查询。

/* Assume appropriate imports */
/* React Native firebase auth */
firebase.auth().onAuthStateChanged(async (user) => {
    const userToken = await user.getIdToken();
    
    /* Client creation */
    const client = new ApolloClient({
      uri: '[Firebase Cloud Function URL]',
      headers: {
        Authorization: userToken ? `Bearer ${userToken}` : ''
      },
      cache: new InMemoryCache(),
    });
    /* Query test */
    client.query({
      query: gql`
        {
          hello
        }
      `
    }).then(
      (result) => console.log('log query result', result)
    ).catch(
      (error) => console.log('query error', error)
    )
})

更新 05/03/20

我可能已经找到了错误的根源。在我确认之前我不会发布答案,但这是更新。看起来allAuthenticatedUsers 是特定于 Google 帐户而不是 Firebase 的角色。请参阅this part of the google docsthis stackoverflow answer

我会做一些测试,但解决方案可能是将权限更改为allUsers,这可能仍需要身份验证。如果我可以让它工作,我会更新一个答案。

【问题讨论】:

  • " 授权:Bearer ${userToken} ? userToken : '' " 这行正确吗?
  • @gstvg,根据关于客户端的说明中的第 2 步,这是应该发送的标头Authorization: Bearer ID_TOKEN。所以我认为是的。参考文档 -cloud.google.com/functions/docs/securing/…,
  • 是的,你的文档是正确的,我的评论的格式也很难识别,但我认为这一行有一个小错字:你正在做一个 ternay 操作,它总是返回userToken 单独,没有“Bearer”部分,不是吗?
  • @gstvg 很好,我确实不小心把它们翻了。不幸的是,在修复操作并再次尝试查询后,结果仍然是相同的错误。我已经更新了上述问题以反映正确的操作。
  • 解决方案有更新吗?

标签: react-native firebase-authentication graphql google-cloud-functions apollo-server


【解决方案1】:

我能够让事情正常进行。工作请求需要进行以下更改:

  1. 更改云函数“调用者”角色以包含 allUsers 而不是 allAuthenticatedUsers。这是因为allUsers 角色使该功能可用于 http 请求(您仍然可以要求通过 sdk 验证进行身份验证)
  2. 调整服务器和客户端的代码,如下所示。对标头字符串构造进行了细微更改。

Server.js (ApolloServer)

/* Assume proper imports */
/* Initialize Firebase Admin SDK */
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "[db-url]",
});

/* Async verification with user token */
const verify = async (idToken) => {
  if (idToken) {
    var newToken = idToken.replace("Bearer ", "");
    // var newToken = idToken
    let header = await admin.auth().verifyIdToken(newToken)
      .then(function(decodedToken) {
        // ...
        return {
          "Authorization": 'Bearer ' + decodedToken
        }
      }).catch(function(error) {
        // Handle error
        return null
      });
    return header
  } else {
    throw 'No Access' 
  }
}

/* Server */
function gqlServer() {
  const app = express();

  const apolloServer = new ApolloServer({
    typeDefs: schema,
    resolvers,
    context: async ({ req, res }) => {
      // headers: req.headers,
      const verified = await verify(req.headers.authorization)
      console.log('log verified', verified)
      return {
        headers: verified ? verified: '',
        req, 
        res,
      }
    },
    // Enable graphiql gui
    introspection: true,
    playground: true
  });
  
  apolloServer.applyMiddleware({app, path: '/', cors: true});

  return app;
}

export default gqlServer;

Client.js (ApolloClient)

/* Assume appropriate imports */
/* React Native firebase auth */
firebase.auth().onAuthStateChanged(async (user) => {
    const userToken = await user.getIdToken();
    
    /* Client creation */
    const userToken = await user.getIdToken();
    const client = new ApolloClient({
      uri: '[Firebase Cloud Function URL]',
      headers: {
        "Authorization": userToken ? 'Bearer ' + userToken : ''
      }, 
      cache: new InMemoryCache(),
    });
    client.query({
      query: gql`
        {
          hello
        }
      `
    }).then(
      (result) => console.log('log query result', result)
    ).catch(
      (error) => console.log('query error', error)
    )
})

【讨论】:

    猜你喜欢
    • 2019-02-25
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 2018-04-17
    • 2018-06-22
    • 2018-08-18
    • 2021-03-31
    • 1970-01-01
    相关资源
    最近更新 更多