I'm developing a basic application that simply reads the emails from a specific gmail account (say [email protected]) . On first run the app is granted permission to read emails of [email protected] . And an access_token, expiry_date (1 hour) and refresh_token etc are saved to 'token.json' file.
On subsequent runs even after the access token expires, I do NOT see a refresh token request being made, yet the App is able to fetch and read emails from [email protected].
The app is run from command line as 'node app.js' and it fetches the emails with a specific label and prints the content of email on console.
The method authorize() is the first one called every time the app is run. The getNewToken() is called only on first run and creates a 'token.json' file, when the user [email protected] grants permission to App to read its emails.
Here is the relevant code for this simple app:
const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
const TOKEN_PATH = 'token.json';
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
function authorize(credentials, callback) {
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 getNewToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
function getNewToken(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);
});
});
}
function startApp(){
fs.readFile('gmailCredentials.json', (err, content) => {
if (err)
return console.log('Error loading client secret file:', err);
authorize(JSON.parse(content), function(auth){
checkForMailsWithLabel(auth, strLabel, function(err, res){
console.log("auth holds: " + util.inspect(auth) );
// this prints the same accessToken and expiration as the one we have in 'token.json'
// Even though the token is expired, the app is still able to read the emails
});
});
});
}
The code you see here is almost the exact copy of the sample they have on github: https://github.com/gsuitedevs/node-samples/blob/master/gmail/quickstart/index.js
If it matters, I have not completed the verification process with google for this App yet. Since I'm testing with my own email account, the untrusted warning at the time of granting permission is not a problem at the moment.
My question/assumption was that googleapi library would check to see if the access token has expired and if it has then it would request another token automatically and write it to the 'token.json' file.
But as best as I can tell from running this code, the 'token.json' file gets created on first run. And even if the expiry_date (1 hour) has expired, google library does not request another token. I say this because :
- I don't see any update to 'token.json'
- Even after a successfully fetch emails, The token that would have been used for fetch holds the same access token and expiration.
I could write code to check if expiry_date has passed and maybe force a refresh of access_token. But that doesn't explain why my app is able to fetch emails with the expired token.
I would rather use the recommended method and let the library handle the token if it is supposed to.
Please advice, what am I missing here.