Используя библиотеку узлов google oauth2, невозможно получить новый токен доступа с токеном refre sh - PullRequest
0 голосов
/ 18 июня 2020

С помощью библиотеки google oauth2 я могу успешно аутентифицировать пользователя при их первом проходе, получить их токен refre sh и первый токен доступа. Пока токен не истечет, все работает как положено.

Однако, когда срок действия токена доступа истекает, мне нужно получить новый токен доступа и сохранить эти токены в моем хранилище данных, используя существующий токен refre sh. Я знаю, что в документации указано, что токены должны повторно загружаться, когда срок их действия истекает, но поскольку я создаю нового клиента для каждого вызова (чтобы гарантировать, что токены не используются повторно между пользователями), я думаю, что клиент разрушается до того, как токен получает возможность обновить сам sh.

Проверяя, что делает библиотека, вызывая фактический API Google, я должен иметь возможность получить новые токены доступа, вызвав метод client.refreshAccessToken(), ответ на этот вызов дает мне ошибка invalid_grant Bad Request. Я сравнил фактический запрос api, который этот метод делает, с запросом на google oauth2 Playground , и два вызова идентичны, хотя их призыв к обновлению их токенов работает, а мой - нет.

Прикреплено это мой код в его нынешнем виде. Пожалуйста, пришлите помощь - у меня не осталось волос, которые нужно выдергивать!

const { google } = require('googleapis')
const scopes = [
  'https://www.googleapis.com/auth/spreadsheets.readonly',
  'https://www.googleapis.com/auth/userinfo.email',
  'https://www.googleapis.com/auth/drive.readonly'
]

module.exports = (env, mongo) => {
  const getBaseClient = () => {
    const { OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CALLBACK_URL } = env.credentials
    return new google.auth.OAuth2(
      OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CALLBACK_URL
    )
  }

  const getNewAccessTokens = async (authId, refreshToken) => {
    const { tokens } = await getBaseClient().getToken(refreshToken)
    await mongo.setAccessTokensForAuthUser(authId, { ...tokens, refresh_token: refreshToken })
    return tokens
  }

  const getAuthedClient = async (authId) => {
    let tokens = await mongo.getAccessTokensForAuthUser(authId)

    if (!tokens.access_token) {
      tokens = await getNewAccessTokens(authId, tokens.refresh_token)
    }

    const client = getBaseClient()
    client.setCredentials(tokens)

    if (client.isTokenExpiring()) {
      const { credentials } = await client.refreshAccessToken()
      tokens = { ...credentials, refresh_token: tokens.refreshToken }
      await mongo.setAccessTokensForAuthUser(authId, tokens)
      client.setCredentials(tokens)
    }

    return client
  }

  const generateAuthUrl = (userId) => {
    return getBaseClient().generateAuthUrl({
      access_type: 'offline',
      scope: scopes,
      state: `userId=${userId}`
    })
  }

  const getUserInfo = async (authId) => {
    const auth = await getAuthedClient(authId)
    return google.oauth2({ version: 'v2', auth }).userinfo.get({})
  }

  const listSheets = async (authId) => {
    const auth = await getAuthedClient(authId)
    let nextPageToken = null
    let results = []
    do {
      const { data } = await google
        .drive({ version: 'v3', auth })
        .files.list({
          q: 'mimeType = \'application/vnd.google-apps.spreadsheet\'',
          includeItemsFromAllDrives: true,
          supportsAllDrives: true,
          corpora: 'user',
          orderBy: 'name',
          pageToken: nextPageToken
        })
      nextPageToken = data.nextPageToken
      results = results.concat(data.files)
    } while (nextPageToken)
    return results
  }

  return {
    generateAuthUrl,
    getUserInfo,
    listSheets
  }
}

1 Ответ

0 голосов
/ 19 июня 2020

Я решил свою проблему.

Я объединял access_codes с refresh_tokens и считал, что код, который вы получаете из URL-адреса авторизации, был refresh_token, сохраняя его и пытаясь повторно использовать для получения подробнее access_tokens. Это не верно. Не делайте этого.

Вы получаете access_code из URL-адреса аутентификации, и в первый раз, когда вы используете его с методом client.getToken(code), вы получаете refresh_token и access_token.

Я приложил обновленный и рабочий код, если кто-нибудь будет sh его использовать.

Я также должен упомянуть, что я добавил prompt: 'consent' в URL-адрес авторизации, чтобы вы всегда получали access_code вы можете использовать, чтобы получить refresh_token, когда кто-то повторно аутентифицируется (как если бы вы этого не сделали, тогда вызов client.getToken() не не возвращает refresh_token (часть того, что меня смущало в первую очередь).

const { google } = require('googleapis')
const scopes = [
  'https://www.googleapis.com/auth/spreadsheets.readonly',
  'https://www.googleapis.com/auth/userinfo.email',
  'https://www.googleapis.com/auth/drive.readonly'
]

module.exports = (env, mongo) => {
  const getBaseClient = () => {
    const { OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CALLBACK_URL } = env.credentials
    return new google.auth.OAuth2(
      OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CALLBACK_URL
    )
  }

  const getAuthedClient = async (authId) => {
    let tokens = await mongo.getAccessTokensForAuthUser(authId)

    const client = getBaseClient()
    client.setCredentials(tokens)

    if (client.isTokenExpiring()) {
      const { credentials } = await client.refreshAccessToken()
      tokens = { ...credentials, refresh_token: tokens.refresh_token }
      await mongo.setAccessTokensForAuthUser(authId, tokens)
      client.setCredentials(tokens)
    }

    return client
  }

  const generateAuthUrl = (userId) => {
    return getBaseClient().generateAuthUrl({
      access_type: 'offline',
      prompt: 'consent',
      scope: scopes,
      state: `userId=${userId}`
    })
  }

  const getUserInfo = async (accessCode) => {
    const auth = getBaseClient()
    const { tokens } = await auth.getToken(accessCode)
    auth.setCredentials(tokens)
    const { data } = await google.oauth2({ version: 'v2', auth }).userinfo.get({})
    return { ...data, tokens }
  }

  const listSheets = async (authId) => {
    const auth = await getAuthedClient(authId)
    let nextPageToken = null
    let results = []
    do {
      const { data } = await google
        .drive({ version: 'v3', auth })
        .files.list({
          q: 'mimeType = \'application/vnd.google-apps.spreadsheet\'',
          includeItemsFromAllDrives: true,
          supportsAllDrives: true,
          corpora: 'user',
          orderBy: 'name',
          pageToken: nextPageToken
        })
      nextPageToken = data.nextPageToken
      results = results.concat(data.files)
    } while (nextPageToken)
    return results
  }

  return {
    generateAuthUrl,
    getUserInfo,
    listSheets
  }
}
...