簡體   English   中英

使用 google oauth2 節點庫,無法使用刷新令牌獲取新的訪問令牌

[英]Using google oauth2 node library, can't get new access token with refresh token

使用 google oauth2 庫,我可以在用戶第一次通過時成功地對其進行身份驗證,獲取他們的刷新令牌和首次訪問令牌。 在令牌過期之前,一切都按預期工作。

但是,當訪問令牌過期時,我需要獲取一個新的訪問令牌並使用現有的刷新令牌將這些令牌存儲在我的數據存儲中。 我知道文檔狀態令牌在過期時應該重新獲取,但是當我為每個調用創建一個新客戶端(以確保令牌不會在用戶之間重復使用)時,我認為客戶端在令牌之前被拆除有機會刷新自己。

檢查庫調用實際谷歌 api 的內容,我應該能夠通過調用client.refreshAccessToken()方法來獲取新的訪問令牌,這個調用的響應給了我invalid_grant Bad Request錯誤。 我已經比較了實際的 api 請求,此方法與谷歌 oauth2 操場上的請求進行了比較,這兩個調用是相同的——盡管他們要求刷新令牌有效而我的無效。

附件是我現在的代碼 請發送幫助 - 我沒有任何頭發可以拔掉!

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
  }
}

我解決了我自己的問題。

我將access_codesrefresh_tokens混為一談,並相信您從 auth url 收到的代碼是 refresh_token,存儲它並嘗試重用它以獲得更多access_tokens 這是錯誤的。 不要這樣做。

您從身份驗證 url 中獲取access_code ,當您第一次使用client.getToken(code)方法時,您會收到refresh_tokenaccess_token

如果有人希望使用它,我已經附上了更新的工作代碼。

我還應該提一下,我向身份驗證 url 添加了prompt: 'consent' ,以便您始終收到一個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
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM