insanum / gcalcli

Google Calendar Command Line Interface
MIT License
3.31k stars 311 forks source link

I have renew the token every 7 days #628

Open aristosv opened 2 years ago

aristosv commented 2 years ago

Every 7 days, I start getting the following error in my log files

Traceback (most recent call last):
 File "/usr/bin/gcalcli", line 11, in <module>
   load_entry_point('gcalcli==4.3.0', 'console_scripts', 'gcalcli')()
 File "/usr/lib/python3/dist-packages/gcalcli/cli.py", line 152, in main
   gcal.AgendaQuery(start=parsed_args.start, end=parsed_args.end)
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 1238, in AgendaQuery
   return self._display_queried_events(start, end)
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 1182, in _display_queried_events
   event_list = self._search_for_events(start, end, search)
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 1130, in _search_for_events
   self.get_cal_service()
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 158, in get_cal_service
   http=self._google_auth())
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 139, in _google_auth
   credentials = tools.run_flow(
 File "/usr/lib/python3/dist-packages/oauth2client/_helpers.py", line 133, in positional_wrapper
   return wrapped(*args, **kwargs)
 File "/usr/lib/python3/dist-packages/oauth2client/tools.py", line 240, in run_flow
   code = input('Enter verification code: ').strip()
EOFError: EOF when reading a line
sending next hour reminders
getting next day events
Traceback (most recent call last):
 File "/usr/bin/gcalcli", line 11, in <module>
   load_entry_point('gcalcli==4.3.0', 'console_scripts', 'gcalcli')()
 File "/usr/lib/python3/dist-packages/gcalcli/cli.py", line 152, in main
   gcal.AgendaQuery(start=parsed_args.start, end=parsed_args.end)
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 1238, in AgendaQuery
   return self._display_queried_events(start, end)
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 1182, in _display_queried_events
   event_list = self._search_for_events(start, end, search)
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 1130, in _search_for_events
   self.get_cal_service()
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 158, in get_cal_service
   http=self._google_auth())
 File "/usr/lib/python3/dist-packages/gcalcli/gcal.py", line 139, in _google_auth
   credentials = tools.run_flow(
 File "/usr/lib/python3/dist-packages/oauth2client/_helpers.py", line 133, in positional_wrapper
   return wrapped(*args, **kwargs)
 File "/usr/lib/python3/dist-packages/oauth2client/tools.py", line 240, in run_flow
   code = input('Enter verification code: ').strip()

Basically the error is code = input('Enter verification code: ').strip()

To fix it, I have to create a new OAuth2 token and run this command again gcalcli --noauth_local_webserver --client-id=$clientid --client-secret=$clientsecret --config-folder $parentdir/clients/$clientdir/auth agenda

And then it lasts for 7 more days, and I have to repeat the process again.

What I'm I doing wrong here?

aristosv commented 2 years ago

This is the actual error

oauth2client.client.HttpAccessTokenRefreshError: invalid_grant: Token has been expired or revoked.

For some reason the token expired

dequeckerp commented 2 years ago

I have the same issue:

In documentation https://developers.google.com/identity/protocols/oauth2#expiration there is written:

A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days.

So the app needs to be published, which required google verification. :-(

I have not found a solution yet.

shrx commented 2 years ago

Duplicate of https://github.com/insanum/gcalcli/issues/611 ?

ForteDexe commented 2 years ago

@aristosv @dequeckerp You guys can try this work around: image

If you are using KDE, add that url to kOrganizer, restart your system. Then use konsolekalendar in terminal.

aristosv commented 2 years ago

I don't see how that iCal link can be used with gcalcli command line options.

ajkessel commented 2 years ago

Per https://github.com/insanum/gcalcli/issues/611 , if we can figure out how to get gcalcli to pass a redirect_uri parameter to the Python Google OAuth2 module, it should be possible to publish the app without Google review and have longer-lasting tokens. I also filed an Ubuntu bug report at https://bugs.launchpad.net/ubuntu/+source/python-oauth2client/+bug/1983216 since it appears upstream is no longer maintaining the relevant library https://github.com/googleapis/oauth2client/issues/317

shrx commented 2 years ago

Does the replacement library google-auth support the required functionality?

ajkessel commented 2 years ago

I believe so. The deprecated Python module doesn't allow the code to specify redirect_uri. If we want to publish a web app in production rather than testing (thus extending the token expiration period), while foregoing Google verification, we need to be able to set redirect_uri. There's probably some way to hack around this in the existing gcalcli code but it wasn't something I could solve in a few minutes.

ferdinandyb commented 1 year ago

I've published my own google app I was using to connect and I haven't had issues since. It gave an ugly warning about not being verified when logging in, but you can say you don't care :)

NightMachinery commented 1 year ago

I've published my own google app I was using to connect and I haven't had issues since. It gave an ugly warning about not being verified when logging in, but you can say you don't care :)

How did you publish it? Doesn't it need to pass a manual review by Google?

ferdinandyb commented 1 year ago

On Sun Apr 16, 2023 at 14:55, Feraidoon Mehri wrote:

I've published my own google app I was using to connect and I haven't had issues since. It gave an ugly warning about not being verified when logging in, but you can say you don't care :)

How did you publish it? Doesn't it need to pass a manual review by Google?

No, it doesn't, unless you want to make that warning go away.

dbarnett commented 1 month ago

Likely fixed now that I merged #683. Please let me know if issues remain. Thanks!

ZaxonXP commented 1 week ago

Was this fix merged to release 4.4.0?

Today I wanted to use gcalcli, but got this error again:

Token has been expired or revoked

I followed the instructions how to create the token from the page here, but I got this error anyway. Is there something I can try to make it work for longer?

ZaxonXP commented 1 week ago

@dbarnett:

Here is the error I got:

Traceback (most recent call last):
  File "/usr/local/bin/gcalcli", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/cli.py", line 169, in main
    gcal.CalQuery(
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 1248, in CalQuery
    event_list = self._search_for_events(start, end, None)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 1096, in _search_for_events
    event_list.extend(
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 1041, in _GetAllEvents
    self.get_events()
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 183, in get_events
    return self.get_cal_service().events()
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 178, in get_cal_service
    credentials=self._google_auth())
                ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 171, in _google_auth
    auth.refresh_if_expired(self.credentials)
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/auth.py", line 26, in refresh_if_expired
    credentials.refresh(Request())
  File "/usr/local/lib/python3.11/dist-packages/google/oauth2/credentials.py", line 431, in refresh
    ) = reauth.refresh_grant(
        ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/google/oauth2/reauth.py", line 366, in refresh_grant
    _client._handle_error_response(response_data, retryable_error)
  File "/usr/local/lib/python3.11/dist-packages/google/oauth2/_client.py", line 68, in _handle_error_response
    raise exceptions.RefreshError(
google.auth.exceptions.RefreshError: ('invalid_grant: Token has been expired or revoked.', {'error': 'invalid_grant', 'error_description': 'Token has been expired or revoked.'})
dbarnett commented 1 week ago

Could you peek in your ~/.gcalcli_oauth file with a text editor and see if you can find "232867676714.apps.googleusercontent.com" in there, or a different ID before the .apps.…? (It may be a binary file but I think the "apps.googleusercontent.com" part should still be visible if you tell your text editor to open it anyway.)

Also when this happens, does renewing with the same client_id/client_secret work, or do you have to update anything else?

ZaxonXP commented 1 week ago

Yes, the .apps.googleusercontent.com string is present in the oauth file.

By renewing you mean to do:

gcalcli --client-id=<ID>.apps.googleusercontent.com init

or do something elese? I tried this command, but gcalcli says that I should use one of the following commands (invalid choice error).

dbarnett commented 1 week ago

But is the ID in there "232867676714", was my question.

By renewing you mean to do …

I think on 4.4.0 init isn't a valid command, you'd need to use list or whatever placeholder command, but I was more asking what exactly you do when it tells you the token is expired to get it running for another 7 days.

ZaxonXP commented 1 week ago

No, there is no such string in the ID. Should I post it here?

If I do list command, then it list my calendar. If I do the calw then I get this error. But with any other option I cannot triger the init.

dbarnett commented 1 week ago

No, there is no such string in the ID. Should I post it here?

Nah, if it's not the old default then it would have to be one you set up. Just wanted to double check.

If I do the calw then I get this error.

Ah, weird! I did feel like I saw auth treated differently for calm/calw vs. list once a while back (https://github.com/insanum/gcalcli/issues/691#issue-2483533199) but since I posted that I haven't seen any further weirdness like that. I wonder if there's some difference in the scopes used or something...

So IIUC you're saying that after you see this error...

  1. gcalcli list still works fine
  2. calw/calm fail with the RefreshError you posted above
  3. to fix that you have to run gcalcli --client-id=xxx calw to refresh the authentication

?

FYI I'll probably add a gcalcli util inspect-auth command to simplify some of this auth troubleshooting in the future, but hopefully we can isolate the problem on your current install.

dbarnett commented 1 week ago

I wonder if there's some difference in the scopes used or something...

Looks like if there is a missing scope here it'd be https://www.googleapis.com/auth/calendar.events, which is needed for listing events but not for listing calendars. I looked through the code and didn't see anything else specialized for calw/calm vs. list... ultimately one does self.get_cal_service().events() where the other does self.get_cal_service().calendarList().list(). But on my system calw works fine without any explicit calendar.events scope being attached to my token.

I'll have to look into whether calendar scope is supposed to imply calendar.events with the same prefix or how that works for OAuth scopes. I see we only request the calendar scope explicitly in the auth flow.

ZaxonXP commented 1 week ago

I ran the following:

user@trekstor:~$ gcalcli --config-folder=/home/user/.config/gcalcli/new --client-id=797412254427-ulo8vk53ea8arjimabkahqjo8emte84j.apps.googleusercontent.com calw
Not yet authenticated. Starting auth flow...
NOTE: See https://github.com/insanum/gcalcli/blob/HEAD/docs/api-auth.md for help/troubleshooting.
You'll be asked for a client_secret that you should have set up for yourself in Google dev console.
Client Secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Now click the link below and follow directions to authenticate.
You will likely see a security warning page and need to click "Advanced" and "Go to gcalcli (unsafe)" to proceed.
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=797412254427-ulo8vk53ea8arjimabkahqjo8emte84j.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar&state=WsmhBeYCLqXXRRTjI43O27J7IBSoYk&access_type=offline
Traceback (most recent call last):
  File "/usr/local/bin/gcalcli", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/cli.py", line 142, in main
    gcal = GoogleCalendarInterface(
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 73, in __init__
    self._get_cached()
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 219, in _get_cached
    self.get_cal_service().calendarList().list(
    ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 178, in get_cal_service
    credentials=self._google_auth())
                ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/gcal.py", line 167, in _google_auth
    self.credentials = auth.authenticate(client_id, client_secret)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/gcalcli/auth.py", line 20, in authenticate
    credentials = flow.run_local_server(open_browser=False)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/google_auth_oauthlib/flow.py", line 458, in run_local_server
    self.fetch_token(
  File "/usr/local/lib/python3.11/dist-packages/google_auth_oauthlib/flow.py", line 285, in fetch_token
    return self.oauth2session.fetch_token(self.client_config["token_uri"], **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/requests_oauthlib/oauth2_session.py", line 406, in fetch_token
    self._client.parse_request_body_response(r.text, scope=self.scope)
  File "/usr/lib/python3/dist-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 427, in parse_request_body_response
    self.token = parse_token_response(body, scope=scope)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/oauthlib/oauth2/rfc6749/parameters.py", line 441, in parse_token_response
    validate_token_parameters(params)
  File "/usr/lib/python3/dist-packages/oauthlib/oauth2/rfc6749/parameters.py", line 448, in validate_token_parameters
    raise_from_error(params.get('error'), params)
  File "/usr/lib/python3/dist-packages/oauthlib/oauth2/rfc6749/errors.py", line 399, in raise_from_error
    raise cls(**kwargs)
oauthlib.oauth2.rfc6749.errors.InvalidClientError: (invalid_client) Unauthorized

But why?

dbarnett commented 1 week ago

Agreed that's weird. The errors are coming from deep in google_auth_oauthlib deps so there's not a lot of self-contained troubleshooting info, but it does seem like for one reason or another gcalcli really doesn't like something about this 797412254427-ulo8vk53ea8arjimabkahqjo8emte84j.apps.googleusercontent.com client. That's something you got from a project you set up yourself in Google Cloud Console, right? Maybe if you check on the Google end there might be some kind of advisory message about the project? Have you tried creating a fresh "project" to see if the same issues repro with a new client?

ZaxonXP commented 1 week ago

I created a clean project with OAuth and for now it is working. Let's see it in a week if it start behaving wrongly.

dbarnett commented 1 week ago

Sounds good, and in the meantime if you want to swap out your oauth files and work on narrowing down the issue I'm game, but that might also be easier after I push a new release w/ a util inspect-auth command. At the very least it'd be nice to improve the error message output and behavior in some of these failure cases, if we can make sense of the different errors getting thrown and what they're supposed to mean.

dbarnett commented 1 week ago

I wonder if there's some difference in the scopes used or something...

Looks like if there is a missing scope here it'd be https://www.googleapis.com/auth/calendar.events, which is needed for listing events but not for listing calendars.

Alright, the scopes shouldn't be the problem from my read of the API docs. I noticed it says "This request allows authorization with at least one of the following scopes:", and likewise every other method that looks for the calendar.events scope also accepts calendar.

I suspect the only reason calw/calm seemed to be acting any different was because of the cache: https://github.com/insanum/gcalcli/issues/622#issuecomment-2378125839.

So I can't find anything else to make more robust "just in case", hoping that indeed the only problem was something in the OAuth client, and if it keeps misbehaving I've implemented the util inspect-auth command (#780) to make troubleshooting easier.