节点 Google 日历 API 快速入门重定向 url

Node Google Calendar API quickstart redirect url

我关注Node Google Calendar Quickstart

我正在尝试从日历中获取事件列表并使用 auth0 进行应用程序登录。在我的日历页面上,使用在 api 控制台中创建的 api 凭据,我 运行 来自快速入门的示例代码,发送了授权请求,我可以在终端中看到对授权代码的请求.但是当我按照授权流程允许我的应用程序访问我的 google 帐户时,一旦我设置了允许页面的权限,我就会将我重定向回 auth0 而不是带有授权代码的页面。这是我回来的一个旧项目,我已经阅读了有关刷新令牌的内容,但似乎无法弄清楚为什么我无法将授权码输入终端以授权我的应用程序。

我的credentials.json

{
"installed": {
"client_id": "xxxx.apps.googleusercontent.com",
"project_id": "quickstart-xxxx",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "xxxx",
"redirect_uris": [
"https://xxxx.auth0.com",
"https://xxxx.auth0.com/login/callback"
],
"javascript_origins": [
"https://xxxx.auth0.com"
]
}
}

router.js

router.get('/calendar', secured(), (req, res, next) => {
    const fs = require('fs');
    const readline = require('readline');
    const { google } = require('googleapis');


    // If modifying these scopes, delete token.json.
    const SCOPES = ['https://www.googleapis.com/auth/calendar'];
    // The file token.json stores the user's access and refresh tokens, and is
    // created automatically when the authorization flow completes for the first
    // time.
    const TOKEN_PATH = 'token.json';

    // Load client secrets from a local file.
    fs.readFile('credentials.json', (err, content) => {
        if (err) return console.log('Error loading client secret file:', err);
        // Authorize a client with credentials, then call the Google Calendar API.
        authorize(JSON.parse(content), listEvents);
    });

    /**
     * Create an OAuth2 client with the given credentials, and then execute the
     * given callback function.
     * @param {Object} credentials The authorization client credentials.
     * @param {function} callback The callback to call with the authorized client.
     */
    function authorize(credentials, callback) {
        // eslint-disable-next-line camelcase
        const { client_secret, client_id, redirect_uris } = credentials.installed;
        const oAuth2Client = new google.auth.OAuth2(
            client_id, client_secret, redirect_uris[0]);

        // Check if we have previously stored a token.
        fs.readFile(TOKEN_PATH, (err, token) => {
            if (err) return getAccessToken(oAuth2Client, callback);
            oAuth2Client.setCredentials(JSON.parse(token));
            callback(oAuth2Client);
        });
    }

    /**
     * Get and store new token after prompting for user authorization, and then
     * execute the given callback with the authorized OAuth2 client.
     * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
     * @param {getEventsCallback} callback The callback for the authorized client.
     */
    function getAccessToken(oAuth2Client, callback) {
        const authUrl = oAuth2Client.generateAuthUrl({
            access_type: 'offline',
            scope: SCOPES,
        });
        console.log('Authorize this app by visiting this url:', authUrl);
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });
        rl.question('Enter the code from that page here: ', (code) => {
            rl.close();


            oAuth2Client.getToken(code, (err, token) => {
                if (err) return console.error('Error retrieving access token', err);
                oAuth2Client.setCredentials(token);
                // Store the token to disk for later program executions
                fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
                    if (err) return console.error(err);
                    console.log('Token stored to', TOKEN_PATH);
                });
                callback(oAuth2Client);
            });
        });
    }

    const userProfile = req.user;
    if (userProfile.emails[0].value == process.env.GC_USER_EMAIL) {
        opCalendarLink = process.env.GOOGLE_CALENDAR_ID_GC;
    } else if (userProfile.emails[0].value == process.env.NODEMAILER_WTCT_PRODUCTION) {
        opCalendarLink = process.env.GOOGLE_CALENDAR_ID_GC + '&' + process.env.GOOGLE_CALENDAR_ID_AS + '&' + process.env.GOOGLE_CALENDAR_ID_AV + '&color=%23D6AE00&color=%3C995B08&color=%231F753C&showTitle=0';
    /*     opCalendarLink = process.env.GOOGLE_CALENDAR_ID_GC + '&' + process.env.GOOGLE_CALENDAR_ID_AS + '&' + process.env.GOOGLE_CALENDAR_ID_AV + '&color=%23D6AE00&color=%3C995B08&color=%231F753C&showTitle=0';
*/} else if (userProfile.emails[0].value == process.env.AS_USER_EMAIL) {
        opCalendarLink = process.env.GOOGLE_CALENDAR_ID_AS;
    } else if (userProfile.emails[0].value == process.env.AV_USER_EMAIL) {
        opCalendarLink = process.env.GOOGLE_CALENDAR_ID_AV;
    }// else (opCalendarLink = process.env.GOOGLE_CALENDAR_ID_WTCT);

    /**
     * Lists the next 10 events on the user's primary calendar.
     * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
     */
    async function listEvents(auth) {
        const calendar = google.calendar({ version: 'v3', auth });
        calendar.events.list({
            calendarId: opCalendarLink,
            timeMin: (new Date()).toISOString(),
            maxResults: 10,
            singleEvents: true,
            orderBy: 'startTime',
        }, (err, res) => {
            if (err) return console.log('The list 10 events calendar API returned an error: ' + err);
            events = res.data.items;
            if (events.length) {
                console.log('Upcoming 10 events:');
                events.map((event, i) => {
                    const start = event.start.dateTime || event.start.date;
                    console.log(`${start} - ${event.summary}`);
                });
            } else {
                console.log('No upcoming events found.');
            }
        });
    }
    // opCalendarLink = process.env.NODEMAILER_WTCT_PRODUCTION; // FOR TESTING PER OP CAL RENDERING
    // switch calendar link depending on OP logged in

    res.render('calendar', {
        viewTitle: 'Booking Calendar',
        eventList: events,
        opCalendarLink: opCalendarLink,
        userProfile: userProfile,
    });
});

我可以使用 auth0 登录和退出应用程序,没有任何问题,但是在尝试授权日历请求时,我总是被引导回 auth0,而不是带有代码的页面。

我觉得这与令牌过期有关,因为这是一个旧项目,我又开始了,但我终究无法弄清楚为什么我可以重新授权我的应用程序。

我删除了旧的 token.json 以允许创建一个新的,但每次我尝试遵循 link 时,我都会被重定向到 auth0。关于为什么在授权应用程序后我总是被重定向回 auth0 有什么建议吗?我可以手动输入代码吗?如果有,在哪里?

如果我重新启用我的旧 token.json 然后我得到 invalid_grant 因此尝试重新授权。

回答

正如之前在评论中讨论的那样,您必须更改 credentials.json 并确保 "urn:ietf:wg:oauth:2.0:oob" 包含在 redirect_uris(按相同的顺序)。

urn:ietf:wg:oauth:2.0:oob表示Google的授权服务器应该return浏览器标题栏或body中的授权码。如果客户端在未对客户端进行重大修改的情况下无法侦听 HTTP 端口,则此选项很有用。

如果您的应用程序使用此值,则需要确定浏览器何时加载了来自授权服务器的响应。然后它从浏览器中提供的页面标题中提取授权代码,或者授权代码应该 returned 在 url 中连同提示用户复制代码并粘贴的页面文本在申请中。

{
  "installed": {
    "client_id": "xxxx.apps.googleusercontent.com",
    "project_id": "quickstart-xxxx",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "xxxx",
    "redirect_uris": [
      "urn:ietf:wg:oauth:2.0:oob",
      "https://xxxx.auth0.com"
    ],
    "javascript_origins": [
      "https://xxxx.auth0.com"
    ]
  }
}

如果您无法访问所需的页面,可以作为替代方法。当您被重定向到另一个页面时,您可以通过转至 开发人员工具 > 网络 访问令牌并查看批准代码。

参考

Google Identity Platform > Send a request to Google's OAuth 2.0 server