otto
otto

Reputation: 2033

raspberry pi python gspread OAuth 2 error

I have a script which should append a row in google spreedsheet every 2 minutes. When i start the script it works well but after a some time I get following error:

{
  "error": {
    "code": 401,
    "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
    "status": "UNAUTHENTICATED"
  }
}

Python Script

import gspread
from oauth2client.service_account import ServiceAccountCredentials
scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']

credentials = ServiceAccountCredentials.from_json_keyfile_name('auth.json', scope)
gc = gspread.authorize(credentials)

wks = gc.open('sheet').sheet1

while True:
    #here is some other code and 2 minutes delay
    wks.append_row(["test", "test2"])

Auth.json

{
  "type": "x",
  "project_id": "x",
  "private_key_id": "x",
  "private_key": "x",
  "client_email": "x",
  "client_id": "x",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "x"
}

Upvotes: 1

Views: 575

Answers (2)

verbage
verbage

Reputation: 51

Auth token timeout it NOT consistent in my experience--sometimes it is exactly an hour, but other times it can be significantly more (like several hours) or significantly less.

I have a project that sends data to Google Sheets every minute via a service account. When I first started it, my script was arranged like KT Work's script with the initial login authorization outside of the main loop. Needless to say, I started running into the 401 UNAUTHORIZED failures like everyone else.

So I added an additional gc.login() into the main loop with a counter to trigger it every 45 minutes. I'd still run into 401 UNAUTHORIZED failures so I decreased it to 30 minutes, then 20 minutes, then even just 10 minutes, but I would still see the same error at least once per day, and sometimes more.

I finally got rid of the counter, and just started executing gc.login() on every single iteration of the loop, i.e. every minute. While this generally seems to solve the 401 UNAUTHORIZED errors, it is not a panacea! Why? Because now I see occasional 503 UNAVAILABLE errors, which have the same effect of stopping the script.

So what is the final solution? Adding a simple watchdog script that executes the main script, and then restarts it if it fails. I've tried several examples, but I settled on a simple, elegant one I ran across a few months ago:

[https://www.alexkras.com/how-to-restart-python-script-after-exception-and-run-it-forever/]

So I have tailored my own "RunScriptForeverWatchdog.py" script as follows:

#!/usr/bin/python
from subprocess import Popen
import sys

filename = sys.argv[1]
while True:
    print("\n(Re-)Starting script:  " + filename)
    p = Popen("python " + filename, shell=True)
    p.wait()

Now I just execute this watchdog script with the main script as an argument, and every time the latter fails, it is immediately respawned by the former.

Upvotes: 0

lepture
lepture

Reputation: 2422

The token that Google issued is a short-live token which will expire at a short time. The ServiceAccountCredentials has a refresh method to fetch the new token. In your case, since it is called every 2 minutes, you can call gc.login() every 2 minutes.

while True:
    gc.login()

I'm not sure why it doesn't refresh by itself (it should). FYI, oauth2client is deprecated, consider using other things. Here is a tip on how to use it with Authlib: https://blog.authlib.org/2018/authlib-for-gspread

Upvotes: 1

Related Questions