Облачные функции Firestore apollo graphql аутентификация - PullRequest
0 голосов
/ 03 мая 2020

Мне нужна помощь, чтобы получить облачную функцию Firebase Apollo / GraphQL для аутентификации и получения запросов.

  • Я реализовал сервер Apollo / GraphQL как облачную функцию в Firebase / Firestore, используя this репозиторий из этого поста .
  • Я установил разрешения для облачной функции на allAuthenticatedUsers и использую Firebase Phone Authentication для аутентификации.
  • Я использовал код из этого ответа stackoverflow , чтобы помочь структурировать часть аутентификации, не включенную в исходный репозиторий.

Функция Apollo / GraphQL отлично работает (протестировано с детской площадкой) , когда права доступа установлены на allUsers. После установки разрешений на 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, то помощь будет принята с благодарностью. Код для сервера и клиента ниже.

Сервер. 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;

Клиент. js (ApolloClient)

Клиентский запрос, построенный с использованием этих инструкций .

/* 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 - это роль, определяемая c для учетных записей Google, а не Firebase. См. эту часть google docs и этот ответ stackoverflow .

Я проведу некоторое тестирование, но решение может заключается в изменении разрешений на allUsers, что может потребовать аутентификации. Если я смогу заставить это работать, я обновлю с ответом.

1 Ответ

0 голосов
/ 11 мая 2020

Я смог заставить вещи работать. Рабочие запросы требовали следующих изменений:

  1. Изменить роль облачной функции "invoker", включив в нее allUsers вместо allAuthenticatedUsers. Это связано с тем, что роль allUsers делает функцию доступной для http-запросов (вам все равно может потребоваться аутентификация через проверку SDK)
  2. Настройка кода для сервера и клиента, как показано ниже. Незначительное изменение в конструкции строки заголовка.

Сервер. 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;

Клиент. 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)
    )
})
...