0
votes

I am following Node Google Calendar Quickstart

I'm trying to get a list of events from a calendar and using auth0 for app login. On my calendar page using api credentials created in api console I run the sample code from the quickstart, the authorization request is sent and I can see in the terminal the request for the auth code. But when I follow the auth flow to allow my app to access my google account, once I have set the permissions to allowed the page redirects me back to auth0 and not to the page with the auth code. This is an old project I came back to and I have read about refreshing tokens but can't seem to figure out why I cant get the auth code to input into the terminal to authorize my app.

my 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,
    });
});

I can login and out of the app using auth0 with no problem but when trying to authorize the calendar request I keep getting directed back to auth0 instead of the page with the code.

I have a feeling this is to do with the token being expired due to this being an old project I started up again but cant for the life of me figure out whey I can re-authorize my my app.

I have deleted the old token.json to allow a new one to be created but every time I try follow the link I get redirected to auth0. Any advise on why I keep getting redirected back to auth0 after authorizing the app? Can I put the code in manually? If so where?

If I re-enable my old token.json then I get invalid_grant hence trying to re-authorize.

1
Have you deleted token.json and tried again?Jose Vasquez
Thank you and yes I have a few times, although I rename it to .old - would that make a difference?ZADorkMan
As you mentioned this is an old project, if by "old" you mean more than 6 months, your refresh token has expired, you can take a look into the documentation. Correct me if I'm wrong, is the token.json no longer being created due to redirection to auth0? By auth0 you mean your main page or Google OAuth? The user authorizing has changed/recovered his password in the last months after your token?Jose Vasquez
thanks again, so yes indeed you are correct, this is an old project older than 6 months, and I have read about the problem but I am unsure of how to actually reauthorize now, and indeed yes it redirects to auth0 upon trying to set the google permission, I can see the app is added to the list of authorized apps in my google though but never reach the dialog box with the code I need.ZADorkMan
I use the node passport and passport-auth0 modules as my main login layer which works fine it seems but this one task, listing items from a calendar, uses googles Oauth and tries to get permissions but fails. Thank you for the link, I did come across that however struggle how to implement it into my code.ZADorkMan

1 Answers

1
votes

Answer

As previously discussed in the comments you have to change your credentials.json and make sure you have "urn:ietf:wg:oauth:2.0:oob" included in the redirect_uris (in the same order).

The value urn:ietf:wg:oauth:2.0:oob indicates that Google's authorization server should return the authorization code in the browser's title bar or the body. This option is useful if the client cannot listen on an HTTP port without significant modifications to the client.

If your application uses this value, it needs to determine when the browser has loaded a response from the authorization server. It then extracts the authorization code from the title of the page served in the browser or the authorization code should be returned in the url along with the page text prompting the user to copy the code and paste it in the application.

{
  "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"
    ]
  }
}

As alternative in case you can't reach the page desired. When you get redirected to another page you can access the token by going to the Developer Tools > Network and see the approval code as well.

Reference

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