gmailpush
gmailpush copied to clipboard
How to automatically refresh the access token?
My application has a status of 'Testing' and the consent screen is configured for an external user type, causing the token to expire in 7 days.
How to automatically refresh the access token on the next API call after it expires?
Currently, I'm manually generating the access token with the following code:
require('dotenv').config();
const readline = require('readline');
const {google} = require('googleapis');
function getToken() {
const auth = new google.auth.OAuth2(
process.env.CLIENT_ID,
process.env.CLIENT_SECRET,
'urn:ietf:wg:oauth:2.0:oob'
);
const authUrl = auth.generateAuthUrl({
access_type: 'offline',
scope: ['https://www.googleapis.com/auth/gmail.readonly'],
});
console.log('Authorize this app by visiting this url:');
console.log(authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (authCode) => {
rl.close();
auth.getToken(authCode, (err, token) => {
if (err) {
return console.log('Error retrieving access token', err);
}
console.log('Token:');
console.log(token);
});
});
}
getToken();
Can you please advice? thanks
I see your API requires the token object.
const messages = await gmailpush.getMessages({
notification: req.body,
token,
...
Is this the only auth method supported in your code? Can service account
be also supported?
@byeokim If this is not possible at the moment due to your limited time for upgrading the API, then could you perhaps show some code example on how to call, for example, the refreshAccessToken()
function to save all returned credentials such as refresh_token
, access_token
to a JSON file, so when the API calls gmailpush.getNewMessage
I can get those credentials updated in the JSON file?
Did you mean renewing mailbox watch because though access_token
expires soon refresh_token
can be used instead as long as you want unless some expiration conditions are met? If that's the case you might want to use some 3rd party scheduler module such as node-schedule to extend seven-days watch expiration as presented here.
I've added the following
fs.readFile('./gmailpush_history.json')
.then((result) => {
const prevHistories = JSON.parse(result);
const prevHistory = prevHistories.find((prevHistory) => prevHistory.emailAddress === email);
schedule.scheduleJob(new Date(prevHistory.watchExpiration - 1000 * 60 * 30), async () => {
prevHistory.watchExpiration = await gmailpush._refreshWatch();
fs.writeFile('./gmailpush_history.json', JSON.stringify(prevHistories));
});
});
https://github.com/jzvi12/gmail-push-to-discord/blob/master/index.js#L53-L61
I've checked that it updated the watchExpiration
value in the gmailpush_history.json
generated file when I got a new email to "extend" it by 1 day... but after testing... on the 8th+ day, I was not getting notifications even though I was getting emails each day.
Thoughts?
I don't have ideas why you are not getting notifications. But looking into your code I found that my suggestion was flawed. Instead of setting up node-schedule
only when email is received, here is a code to extend watch expirations of all users in gmailpush_history.json
every day on 00:00 GMT. Note that googleapis
is added.
// From https://github.com/jzvi12/gmail-push-to-discord/blob/master/index.js
require("dotenv").config();
const jsonToken = require("./token.json");
const Gmailpush = require("gmailpush");
const express = require("express");
const app = express();
const schedule = require('node-schedule');
const fs = require('fs').promises;
const { WebhookClient } = require("discord.js");
const webhook = new WebhookClient({ url: process.env.WEBHOOK_URL });
// Added dependency
const { google } = require("googleapis");
// Initialize with OAuth2 config and Pub/Sub topic
const gmailpush = new Gmailpush({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
pubsubTopic: process.env.TOPIC_URL,
});
const users = [
{
email: process.env.EMAIL,
token: {
access_token: jsonToken.access_token,
refresh_token: jsonToken.refresh_token,
scope: jsonToken.scope,
token_type: jsonToken.token_type,
expiry_date: jsonToken.expiry_date,
},
},
];
const job = schedule.scheduleJob("0 0 * * *", async () => {
try {
const gmailpushHistoryJson = await fs.readFile("gmailpush_history.json");
const prevHistories = JSON.parse(gmailpushHistoryJson);
await Promise.all(
prevHistories.map(async (prevHistory) => {
try {
const { token } = users.find(
(user) => user.email === prevHistory.emailAddress
);
gmailpush._api.auth.setCredentials(token);
gmailpush._api.gmail = google.gmail({
version: 'v1',
auth: gmailpush._api.auth,
});
await gmailpush._api.gmail.users.watch({
userId: prevHistory.emailAddress,
requestBody: {
topicName: gmailpush._api.pubsubTopic,
},
});
} catch (err) {
console.error(err);
}
})
);
} catch (err) {
console.error(err);
}
});
app.post(
// Use URL set as Pub/Sub Subscription endpoint
"/pubsub",
express.json(),
async (req, res) => {
res.sendStatus(200);
const email = gmailpush.getEmailAddress(req.body);
const token = users.find((user) => user.email === email).token;
const { subject } = await gmailpush
.getNewMessage({
notification: req.body,
token,
})
.then((message) => {
if (message === null) {
return {};
}
if (!message.labelIds.includes(process.env.EMAIL_LABEL)) {
return {};
}
/*
fs.readFile('./gmailpush_history.json')
.then((result) => {
const prevHistories = JSON.parse(result);
const prevHistory = prevHistories.find((prevHistory) => prevHistory.emailAddress === email);
schedule.scheduleJob(new Date(prevHistory.watchExpiration - 1000 * 60 * 30), async () => {
prevHistory.watchExpiration = await gmailpush._refreshWatch();
fs.writeFile('./gmailpush_history.json', JSON.stringify(prevHistories));
});
});
*/
return message;
});
if (subject) {
webhook
.send({ content: subject })
.then((message) => console.log(`Sent message: ${message.content}`))
.catch(console.error);
} else {
console.log(
"Not sending message: Email Subject does not match label ID."
);
}
}
);
app.listen(3002, () => {
console.log("Server listening on port 3002...");
});
Unfortunately, it works for a while then I have to manually renew token. Any plans for your API to accept using service account credential that is easier to implement API to work server to server?