如何使用 firebase 函数刷新 google oauth2 上的令牌?

how to refresh token on google oauth2 using firebase functions?

我在 firebase 函数中使用 Google Oauth2 开发了一个集成来访问 Google 表格 API。集成工作正常,但我在确保刷新令牌 运行 正确时遇到问题。该函数在第一个令牌过期后停止工作。

发生这种情况时会出现以下错误:


Function execution started
Error: No refresh token is set.
    at OAuth2Client.refreshTokenNoCache (/workspace/node_modules/googleapis-common/node_modules/google-auth-library/build/src/auth/oauth2client.js:161:19)
    at OAuth2Client.refreshToken (/workspace/node_modules/googleapis-common/node_modules/google-auth-library/build/src/auth/oauth2client.js:142:25)
    at OAuth2Client.getRequestMetadataAsync (/workspace/node_modules/googleapis-common/node_modules/google-auth-library/build/src/auth/oauth2client.js:256:28)
    at OAuth2Client.requestAsync (/workspace/node_modules/googleapis-common/node_modules/google-auth-library/build/src/auth/oauth2client.js:329:34)
    at OAuth2Client.request (/workspace/node_modules/googleapis-common/node_modules/google-auth-library/build/src/auth/oauth2client.js:323:25)
    at createAPIRequestAsync (/workspace/node_modules/googleapis-common/build/src/apirequest.js:292:27)
    at Object.createAPIRequest (/workspace/node_modules/googleapis-common/build/src/apirequest.js:43:9)
    at Resource$Spreadsheets$Values.update (/workspace/node_modules/googleapis/build/src/apis/sheets/v4.js:601:37)
    at exports.loadStripeData.functions.runWith.https.onRequest (/workspace/index.js:176:32)
    at process._tickCallback (internal/process/next_tick.js:68:7)

我想确保令牌正确刷新并存储在 Firestore 上。

我做错了什么?

index.js:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {google} = require('googleapis');

const sheets = google.sheets('v4');
admin.initializeApp();

const CLIENT_ID     = 'CLIENT_ID';
const CLIENT_SECRET = 'CLIENT_SECRETT';
const REDIRECT_URL  = 'https://us-central1-MY-PROJECT.cloudfunctions.net/oauth2callback';
const SCOPES        = ['https://www.googleapis.com/auth/spreadsheets'];

oauth2Client.on('tokens', (tokens) => {
  if (tokens.refresh_token) {
    try {
      admin.firestore()
        .collection('oauth2')
        .doc('google')
        .set({
          tokens: tokens.refresh_token,
        });
    } catch (error) {
      console.error(JSON.stringify(error));
    }
  }
});

/*asks user permission to access his spreadsheets*/
exports.authenticate = functions.https.onRequest((req, res) => {
  const authorizeUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES.join(','),
  });

  res.send(`<html>click here: <a href="${authorizeUrl}">${authorizeUrl}</a></html>`)
});

/*callback function for when the user finishes authenticating*/
exports.oauth2callback = functions.https.onRequest(async(req, res) => {
  const code = req.query.code.toString() || '';

  try {
    await admin.firestore()
      .collection('oauth2')
      .doc('google')
      .set({
        code: decodeURIComponent(code)
      });
  } catch(error) {
    res.send(JSON.stringify(error))
  }

  res.send('auth successfully. You can close this tab');
});

/* get token from Firestone to execute function*/
async function oauth2Auth() {
  const doc = await admin.firestore()
    .collection('oauth2')
    .doc('google')
    .get();
  const credentials = doc.data();

  if (credentials.code !== undefined) {
    const response = await oauth2Client.getToken(credentials.code);
    credentials.tokens = response.tokens;
    delete credentials.code;

    try {
      await admin.firestore()
        .collection('oauth2')
        .doc('google')
        .set({
          tokens: credentials.tokens,
        })
    } catch (error) {
      console.error(error);
    }
  }
  oauth2Client.setCredentials(credentials.tokens);
}

/*function that requires google sheets api*/
exports.mainFunction = functions.https.onRequest(async(req, res) => {
  oauth2Auth();
  //do main function
});

终于发现问题了!

您只会在第一次请求授权时获得刷新令牌。所以如果你没有正确保存它,你必须再次请求许可。

求解:

将用户重定向到授权时 URL 添加以下参数以确保您获得刷新令牌:

access_type=offline&prompt=consent

保存刷新令牌:

oauth2Client.on('tokens', async(tokens:any) => {
    if (tokens.refresh_token) {
      try {
        const authorization = await oauth2Client.getToken(tokens.refresh_token);

        await admin.firestore()
          .collection('collectionName')
          .doc(docId)
          .update({
            token: authorization.tokens
          })
          
      } catch (error) {
        console.error(JSON.stringify(error));
      }
    }
  });