I am developing for a personal embedded device (the Visionect e-ink display) that can run some node.js code to display tasks from my Google Tasks. It works fine but the token expires every so often. How do I handle the token expiry? I cannot show the login screen ofcourse on this e-ink display. How can I permanently have code that works till the user (in this case there is only 1 user - me) revokes the access token?
I looked at this answer but I am not sure it answers my question: Google API Authentication Tasks API
Here is my code:
/*
credentials.json:
{
"installed": {
"client_id": "XXXXXXX.apps.googleusercontent.com",
"project_id": "XXXXXXX",
"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": "XXXXXXX",
"redirect_uris": [
"http://localhost"
]
}
}
*/
import fs from 'fs'
import path from 'path'
import process from 'process'
import {authenticate} from '@google-cloud/local-auth'
const SCOPES = ['https://www.googleapis.com/auth/tasks.readonly']
const CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json')
let myauth = undefined
if (!myauth) {
await authenticate({scopes: SCOPES, keyfilePath: CREDENTIALS_PATH})
.then(client => {
const keys = JSON.parse(fs.readFileSync(CREDENTIALS_PATH))
const key = keys.installed || keys.web
myauth = {
type: 'authorized_user',
client_id: key.client_id,
client_secret: key.client_secret,
refresh_token: client.credentials.refresh_token,
}
console.log(myauth)
})
}
import {google} from 'googleapis'
google.options({auth: google.auth.fromJSON({
type: "authorized_user",
client_id: myauth.client_id
client_secret: myauth.client_secret,
refresh_token: myauth.refresh_token
)})
// Method to fetch tasks from last n days
const tasks = (maxDueDays) => {
const fetchTaskList = (taskList) => taskApi.tasks
.list({
tasklist: taskList.id,
showCompleted: false,
dueMax: dayjs().add(maxDueDays, 'days').format()
})
.then(res => res.data.items.map(task => Object.assign(task, {taskList: taskList.title})))
return taskApi
.tasklists.list()
.then(res => Promise.all(res.data.items.map(fetchTaskList)))
.then(tasks => tasks.flat())
}
tasks(7).then(res => console.log(res))
After a bit (I poll once a hour), I often get:
ERROR 400: {
error: 'invalid_grant',
error_description: 'Token has been expired or revoked.'
}
Here are some other Github issues where people encountered my problem:
- https://github.com/googleapis/google-api-nodejs-client/issues/1551
- https://github.com/googleapis/google-api-nodejs-client/issues/1907
- https://github.com/googleapis/google-api-nodejs-client/issues/2494
- https://github.com/googleapis/google-api-nodejs-client/issues/2350
- https://github.com/googleapis/google-api-nodejs-client/issues/2834
- https://github.com/googleapis/google-api-nodejs-client/issues/2886
- https://github.com/googleapis/google-api-nodejs-client/issues/1609
- https://github.com/googleapis/google-api-nodejs-client/issues/1450
- https://github.com/googleapis/google-api-nodejs-client/issues/517
- https://github.com/googleapis/google-api-nodejs-client/issues/42
Does Google provide a "personal API key" which let's me just read my own data bypassing the oauth dance since this app is only intended for my personal use? If not, is there a programmatic way to handle token expiry which does not involve opening a browser since this is running on an embedded headless device? Google's own sample code tutorial does not really handle token expiry!