diff --git a/google_auth_oauthlib/flow.py b/google_auth_oauthlib/flow.py index 17b24e7..73d4804 100644 --- a/google_auth_oauthlib/flow.py +++ b/google_auth_oauthlib/flow.py @@ -52,7 +52,6 @@ import hashlib import json import logging -import warnings try: from secrets import SystemRandom @@ -70,11 +69,6 @@ _LOGGER = logging.getLogger(__name__) -_OOB_REDIRECT_URIS = [ - "urn:ietf:wg:oauth:2.0:oob", - "urn:ietf:wg:oauth:2.0:oob:auto", - "oob", -] class Flow(object): @@ -214,17 +208,8 @@ def redirect_uri(self): @redirect_uri.setter def redirect_uri(self, value): - if value in _OOB_REDIRECT_URIS: - warnings.warn( - "'{}' is an OOB redirect URI. The OAuth out-of-band (OOB) flow is deprecated. " - "New clients will be unable to use this flow starting on Feb 28, 2022. " - "This flow will be deprecated for all clients on Oct 3, 2022. " - "Migrate to an alternative flow. " - "See https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html?m=1#disallowed-oob".format( - value - ), - DeprecationWarning, - ) + """The OAuth 2.0 redirect URI. Pass-through to + ``self.oauth2session.redirect_uri``.""" self.oauth2session.redirect_uri = value def authorization_url(self, **kwargs): @@ -370,8 +355,6 @@ class InstalledAppFlow(Flow): https://github.com/googleapis/google-api-python-client/blob/main/docs/oauth-installed.md """ - _OOB_REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob" - _DEFAULT_AUTH_PROMPT_MESSAGE = ( "Please visit this URL to authorize this application: {url}" ) @@ -385,63 +368,6 @@ class InstalledAppFlow(Flow): "The authentication flow has completed. You may close this window." ) - def run_console( - self, - authorization_prompt_message=_DEFAULT_AUTH_PROMPT_MESSAGE, - authorization_code_message=_DEFAULT_AUTH_CODE_MESSAGE, - **kwargs - ): - """Run the flow using the console strategy. - - .. deprecated:: 0.5.0 - Use :meth:`run_local_server` instead. - - The OAuth out-of-band (OOB) flow is deprecated. New clients will be unable to - use this flow starting on Feb 28, 2022. This flow will be deprecated - for all clients on Oct 3, 2022. Migrate to an alternative flow. - - See https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html?m=1#disallowed-oob" - - The console strategy instructs the user to open the authorization URL - in their browser. Once the authorization is complete the authorization - server will give the user a code. The user then must copy & paste this - code into the application. The code is then exchanged for a token. - - Args: - authorization_prompt_message (str | None): The message to display to tell - the user to navigate to the authorization URL. If None or empty, - don't display anything. - authorization_code_message (str): The message to display when - prompting the user for the authorization code. - kwargs: Additional keyword arguments passed through to - :meth:`authorization_url`. - - Returns: - google.oauth2.credentials.Credentials: The OAuth 2.0 credentials - for the user. - """ - kwargs.setdefault("prompt", "consent") - warnings.warn( - "New clients will be unable to use `InstalledAppFlow.run_console` " - "starting on Feb 28, 2022. All clients will be unable to use this method starting on Oct 3, 2022. " - "Use `InstalledAppFlow.run_local_server` instead. For details on the OOB flow deprecation, " - "see https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html?m=1#disallowed-oob", - DeprecationWarning, - ) - - self.redirect_uri = self._OOB_REDIRECT_URI - - auth_url, _ = self.authorization_url(**kwargs) - - if authorization_prompt_message: - print(authorization_prompt_message.format(url=auth_url)) - - code = input(authorization_code_message) - - self.fetch_token(code=code) - - return self.credentials - def run_local_server( self, host="localhost", diff --git a/google_auth_oauthlib/interactive.py b/google_auth_oauthlib/interactive.py index 804d4fe..b1ed990 100644 --- a/google_auth_oauthlib/interactive.py +++ b/google_auth_oauthlib/interactive.py @@ -156,7 +156,6 @@ def get_user_credentials( "installed": { "client_id": client_id, "client_secret": client_secret, - "redirect_uris": ["urn:ietf:wg:oauth:2.0:oob"], "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", } diff --git a/google_auth_oauthlib/tool/__main__.py b/google_auth_oauthlib/tool/__main__.py index f13f101..db679a1 100644 --- a/google_auth_oauthlib/tool/__main__.py +++ b/google_auth_oauthlib/tool/__main__.py @@ -72,15 +72,7 @@ default=os.path.join(click.get_app_dir(APP_NAME), DEFAULT_CREDENTIALS_FILENAME), help="Path to store OAuth2 credentials.", ) -@click.option( - "--headless", - is_flag=True, - metavar="", - show_default=True, - default=False, - help="Run a console based flow.", -) -def main(client_secrets, scope, save, credentials, headless): +def main(client_secrets, scope, save, credentials): """Command-line tool for obtaining authorization and credentials from a user. This tool uses the OAuth 2.0 Authorization Code grant as described @@ -88,9 +80,7 @@ def main(client_secrets, scope, save, credentials, headless): https://tools.ietf.org/html/rfc6749#section-1.3.1 This tool is intended for assist developers in obtaining credentials - for testing applications where it may not be possible or easy to run a - complete OAuth 2.0 authorization flow, especially in the case of code - samples or embedded devices without input / display capabilities. + for testing applications or samples. This is not intended for production use where a combination of companion and on-device applications should complete the OAuth 2.0 @@ -102,10 +92,7 @@ def main(client_secrets, scope, save, credentials, headless): client_secrets, scopes=scope ) - if not headless: - creds = flow.run_local_server() - else: - creds = flow.run_console() + creds = flow.run_local_server() creds_data = { "token": creds.token, diff --git a/tests/unit/data/client_secrets.json b/tests/unit/data/client_secrets.json index 1baa499..f1ff8af 100644 --- a/tests/unit/data/client_secrets.json +++ b/tests/unit/data/client_secrets.json @@ -7,7 +7,6 @@ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_secret": "itsasecrettoeveryone", "redirect_uris": [ - "urn:ietf:wg:oauth:2.0:oob", "http://localhost" ] } diff --git a/tests/unit/test_flow.py b/tests/unit/test_flow.py index 8b5da64..d9c9f8b 100644 --- a/tests/unit/test_flow.py +++ b/tests/unit/test_flow.py @@ -58,20 +58,6 @@ def test_from_client_secrets_file_with_redirect_uri(self): == mock.sentinel.redirect_uri ) - def test_from_client_secrets_file_with_oob_redirect_uri(self): - with pytest.deprecated_call(): - instance = flow.Flow.from_client_secrets_file( - CLIENT_SECRETS_FILE, - scopes=mock.sentinel.scopes, - redirect_uri="urn:ietf:wg:oauth:2.0:oob", - ) - - assert ( - instance.redirect_uri - == instance.oauth2session.redirect_uri - == "urn:ietf:wg:oauth:2.0:oob" - ) - def test_from_client_config_installed(self): client_config = {"installed": CLIENT_SECRETS_INFO["web"]} instance = flow.Flow.from_client_config( @@ -296,25 +282,6 @@ def set_token(*args, **kwargs): with fetch_token_patch as fetch_token_mock: yield fetch_token_mock - @mock.patch("builtins.input", autospec=True) - def test_run_console(self, input_mock, instance, mock_fetch_token): - input_mock.return_value = mock.sentinel.code - instance.code_verifier = "amanaplanacanalpanama" - - with pytest.deprecated_call(): - credentials = instance.run_console() - - assert credentials.token == mock.sentinel.access_token - assert credentials._refresh_token == mock.sentinel.refresh_token - assert credentials.id_token == mock.sentinel.id_token - - mock_fetch_token.assert_called_with( - CLIENT_SECRETS_INFO["web"]["token_uri"], - client_secret=CLIENT_SECRETS_INFO["web"]["client_secret"], - code=mock.sentinel.code, - code_verifier="amanaplanacanalpanama", - ) - @pytest.mark.webtest @mock.patch("google_auth_oauthlib.flow.webbrowser", autospec=True) def test_run_local_server(self, webbrowser_mock, instance, mock_fetch_token, port): diff --git a/tests/unit/test_tool.py b/tests/unit/test_tool.py index 3eeb5c5..d76ee53 100644 --- a/tests/unit/test_tool.py +++ b/tests/unit/test_tool.py @@ -57,16 +57,6 @@ def local_server_mock(self, dummy_credentials): flow.return_value = dummy_credentials yield flow - @pytest.fixture - def console_mock(self, dummy_credentials): - run_console_patch = mock.patch.object( - google_auth_oauthlib.flow.InstalledAppFlow, "run_console", autospec=True - ) - - with run_console_patch as flow: - flow.return_value = dummy_credentials - yield flow - def test_help(self, runner): result = runner.invoke(cli.main, ["--help"]) assert not result.exception @@ -91,22 +81,6 @@ def test_defaults(self, runner, dummy_credentials, local_server_mock): assert creds.client_secret == dummy_credentials.client_secret assert creds.scopes == dummy_credentials.scopes - def test_headless(self, runner, dummy_credentials, console_mock): - result = runner.invoke( - cli.main, - [ - "--client-secrets", - CLIENT_SECRETS_FILE, - "--scope", - "somescope", - "--headless", - ], - ) - console_mock.assert_called_with(mock.ANY) - assert not result.exception - assert dummy_credentials.refresh_token in result.output - assert result.exit_code == 0 - def test_save_new_dir(self, runner, dummy_credentials, local_server_mock): credentials_tmpdir = tempfile.mkdtemp() credentials_path = os.path.join(