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!
Set your app to production in google cloud console under the oauth2 consent screen and your refresh token will stop expiring