diff --git a/gcalcli/__init__.py b/gcalcli/__init__.py index 3486c10..8530d55 100644 --- a/gcalcli/__init__.py +++ b/gcalcli/__init__.py @@ -1,5 +1,3 @@ __program__ = 'gcalcli' __version__ = 'v4.3.0' __author__ = 'Eric Davis, Brian Hartvigsen, Joshua Crowgey' -__API_CLIENT_ID__ = '232867676714.apps.googleusercontent.com' -__API_CLIENT_SECRET__ = '3tZSxItw6_VnZMezQwC8lUqy' diff --git a/gcalcli/argparsers.py b/gcalcli/argparsers.py index df5e185..b682f5a 100644 --- a/gcalcli/argparsers.py +++ b/gcalcli/argparsers.py @@ -17,10 +17,10 @@ from .printer import valid_color_name PROGRAM_OPTIONS = { - '--client-id': {'default': gcalcli.__API_CLIENT_ID__, + '--client-id': {'default': None, 'type': str, 'help': 'API client_id'}, - '--client-secret': {'default': gcalcli.__API_CLIENT_SECRET__, + '--client-secret': {'default': None, 'type': str, 'help': 'API client_secret'}, '--config-folder': {'default': None, 'type': str, diff --git a/gcalcli/auth.py b/gcalcli/auth.py new file mode 100644 index 0000000..9c317f2 --- /dev/null +++ b/gcalcli/auth.py @@ -0,0 +1,26 @@ +from google.auth.transport.requests import Request +from google_auth_oauthlib.flow import InstalledAppFlow + + +def authenticate(client_id: str, client_secret: str): + flow = InstalledAppFlow.from_client_config( + client_config={ + "installed": { + "client_id": client_id, + "client_secret": client_secret, + "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", + "redirect_uris": ["http://localhost"], + } + }, + scopes=["https://www.googleapis.com/auth/calendar"], + ) + credentials = flow.run_local_server(open_browser=False) + return credentials + + +def refresh_if_expired(credentials) -> None: + if credentials.expired: + credentials.refresh(Request()) diff --git a/gcalcli/deprecations.py b/gcalcli/deprecations.py index 8ce20ee..d9d5434 100644 --- a/gcalcli/deprecations.py +++ b/gcalcli/deprecations.py @@ -2,8 +2,6 @@ import functools from typing import Any, Dict -import gcalcli - from .printer import Printer, valid_color_name printer = Printer() @@ -73,8 +71,8 @@ def __call__(self, parser, namespace, values, option_string=None): OPTIONS: Dict[str, Dict[str, Any]] = { 'program': { - "--client_id": {'default': gcalcli.__API_CLIENT_ID__}, - "--client_secret": {'default': gcalcli.__API_CLIENT_SECRET__}, + "--client_id": {'default': None}, + "--client_secret": {'default': None}, "--configFolder": {'default': None}, "--defaultCalendar": {'default': [], 'action': DeprecatedAppend} }, diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index abca69b..3f47b91 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -17,12 +17,10 @@ from dateutil.parser import parse from dateutil.relativedelta import relativedelta from dateutil.tz import tzlocal -from google_auth_oauthlib.flow import InstalledAppFlow # type: ignore from googleapiclient.discovery import build from googleapiclient.errors import HttpError -from google.auth.transport.requests import Request # type: ignore -from . import actions, ics, utils +from . import actions, auth, ics, utils from ._types import Cache, CalendarListEntry, Event from .actions import ACTIONS from .conflicts import ShowConflicts @@ -123,6 +121,7 @@ def _retry_with_backoff(self, method: googleapiclient.http.HttpRequest): def _google_auth(self): if not self.credentials: + # Try loading cached credentials if self.options['config_folder']: oauth_filepath = os.path.expanduser( '%s/oauth' % self.options['config_folder'] @@ -131,31 +130,40 @@ def _google_auth(self): oauth_filepath = os.path.expanduser('~/.gcalcli_oauth') if os.path.exists(oauth_filepath): with open(oauth_filepath, 'rb') as gcalcli_oauth: - credentials = pickle.load(gcalcli_oauth) - else: - flow = InstalledAppFlow.from_client_config( - client_config={ - "installed": { - "client_id": self.options['client_id'], - "client_secret": self.options['client_secret'], - "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", - "redirect_uris": ["http://localhost"] - } - }, - scopes=['https://www.googleapis.com/auth/calendar'] + self.credentials = pickle.load(gcalcli_oauth) + if not self.credentials: + # No cached credentials, start auth flow + self.printer.msg( + 'Not yet authenticated. Starting auth flow...\n', 'yellow') + self.printer.msg( + 'NOTE: See https://github.com/insanum/gcalcli for ' + 'help/troubleshooting.\n') + missing_info = [opt for opt in ['client_id', 'client_secret'] + if self.options.get(opt) is None] + if missing_info: + self.printer.msg( + f"You'll be asked for a {' and '.join(missing_info)} " + 'that you should have set up for yourself in Google ' + 'dev console.\n' ) - credentials = flow.run_local_server(open_browser=False) - with open(oauth_filepath, 'wb') as gcalcli_oauth: - pickle.dump(credentials, gcalcli_oauth) - - if credentials.expired: - credentials.refresh(Request()) - - self.credentials = credentials + client_id = self.options.get('client_id') + if client_id is None: + self.printer.msg('Client ID: ', 'magenta') + client_id = input() + client_secret = self.options.get('client_secret') + if client_secret is None: + self.printer.msg('Client Secret: ', 'magenta') + client_secret = input() + self.printer.msg( + 'Now click the link below and follow directions to ' + 'authenticate.\n', 'yellow') + self.printer.msg( + 'You will likely see a security warning page and need to ' + 'click "Advanced" and "Go to gcalcli (unsafe)" to proceed.\n') + self.credentials = auth.authenticate(client_id, client_secret) + with open(oauth_filepath, 'wb') as gcalcli_oauth: + pickle.dump(self.credentials, gcalcli_oauth) + auth.refresh_if_expired(self.credentials) return self.credentials def get_cal_service(self):