From 23b33c2d2d0f18398b021069c643ba281e7ec946 Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Fri, 3 Oct 2025 11:11:14 -0700 Subject: [PATCH 01/11] improved user and dev experience for expired tokens. --- ...ged_auth_state__on_disk_legacy__api_key.py | 2 +- planet/auth.py | 62 +++++++++++++++++-- pyproject.toml | 2 +- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/examples/auth-session-management/app_managed_auth_state__on_disk_legacy__api_key.py b/examples/auth-session-management/app_managed_auth_state__on_disk_legacy__api_key.py index 7c0761069..572b7bb00 100644 --- a/examples/auth-session-management/app_managed_auth_state__on_disk_legacy__api_key.py +++ b/examples/auth-session-management/app_managed_auth_state__on_disk_legacy__api_key.py @@ -7,7 +7,7 @@ def example_main(): # specified file that was created with older versions of the SDK plsdk_auth = planet.Auth.from_file("legacy_api_key_file.json") - # Explicit login is not required for API key use. The above sufficient. + # Explicit login is not required for API key use. The above is sufficient. # plsdk_auth.user_login() # Create a Planet SDK object that uses the loaded auth session diff --git a/planet/auth.py b/planet/auth.py index 688f0d64a..aff200a0e 100644 --- a/planet/auth.py +++ b/planet/auth.py @@ -21,7 +21,6 @@ import typing import warnings import httpx -from typing import List from .auth_builtins import _ProductionEnv, _OIDC_AUTH_CLIENT_CONFIG__USER_SKEL, _OIDC_AUTH_CLIENT_CONFIG__M2M_SKEL import planet_auth @@ -132,7 +131,7 @@ def from_profile( def from_oauth_user_auth_code( client_id: str, callback_url: str, - requested_scopes: typing.Optional[List[str]] = None, + requested_scopes: typing.Optional[typing.List[str]] = None, save_state_to_storage: bool = True, profile_name: typing.Optional[str] = None, storage_provider: typing.Optional[ @@ -193,7 +192,7 @@ def from_oauth_user_auth_code( @staticmethod def from_oauth_user_device_code( client_id: str, - requested_scopes: typing.Optional[List[str]] = None, + requested_scopes: typing.Optional[typing.List[str]] = None, save_state_to_storage: bool = True, profile_name: typing.Optional[str] = None, storage_provider: typing.Optional[ @@ -255,7 +254,7 @@ def from_oauth_user_device_code( def from_oauth_m2m( client_id: str, client_secret: str, - requested_scopes: typing.Optional[List[str]] = None, + requested_scopes: typing.Optional[typing.List[str]] = None, save_state_to_storage: bool = True, profile_name: typing.Optional[str] = None, storage_provider: typing.Optional[ @@ -459,6 +458,54 @@ def is_initialized(self) -> bool: properly configured. """ + @abc.abstractmethod + def ensure_initialized( + self, allow_open_browser: typing.Optional[bool] = False, allow_tty_prompt: typing.Optional[bool] = False + ) -> None: + """ + Do everything necessary to ensure the auth context is ready for use, + while still biasing towards just-in-time operations and not making + unnecessary network requests or prompts for user interaction. + + This can be more complex than it sounds given the variations in + possible session state. Client may be initialized with active + sessions, initialized with stale but still valid sessions, + initialized with invalid or expired sessions, or completely + uninitialized. The process taken to ensure client readiness with + as little user disruption as possible is as follows: + + 1. If the client has been logged in and has a non-expired + session, the client will be considered ready without prompting + the user or probing the network. This will not require user + interaction. + 2. If the client has not been logged in and is an M2M client, + the client will be considered ready without prompting + the user or probing the network. This will not require + user interaction. Login will be delayed until it is required. + 3. If the client has been logged in and has an expired access token, + the network will be probed to attempt a refresh of the session. + This should not require user interaction. If refresh fails, + the user will be prompted to perform a fresh login, requiring + user interaction. + 4. If the client has never been logged in and is user interactive + client (verses an M2M client), a user interactive login will be initiated. + + There still may be conditions where we believe we are + ready, but requests will still ultimately fail. Saved secrets for M2M + clients could be wrong, or the user could be denied by the API. + + When a user interactive login is required, the client must specify + whether a local web browser may be opened and/or whether the TTY + may be used to prompt the user. + + If the auth context cannot be made ready, an exception will be thrown. + + Parameters: + allow_open_browser: specify whether login is permitted to open + a browser window. + allow_tty_prompt: specify whether login is permitted to request + input from the terminal. + """ class APIKeyAuthException(PlanetError): """exceptions thrown by APIKeyAuth""" @@ -496,5 +543,12 @@ def device_user_login_complete(self, login_initialization_info: dict): def is_initialized(self) -> bool: return self._plauth.request_authenticator_is_ready() + def ensure_initialized( + self, allow_open_browser: typing.Optional[bool] = False, allow_tty_prompt: typing.Optional[bool] = False + ) -> None: + return self._plauth.ensure_request_authenticator_is_ready( + allow_open_browser=allow_open_browser, + allow_tty_prompt=allow_tty_prompt, + ) AuthType = Auth diff --git a/pyproject.toml b/pyproject.toml index 5627772fb..04837eca5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ "pyjwt>=2.1", "tqdm>=4.56", "typing-extensions", - "planet-auth>=2.1.0", + "planet-auth>=2.3.0", ] readme = "README.md" requires-python = ">=3.10" From 8b8df6cead45f0f7e5401e3f6314648623ecc464 Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Mon, 6 Oct 2025 09:09:43 -0700 Subject: [PATCH 02/11] subcommands should provide the click context when creating CLI sessions. This enables the session to pick-up options set by the root command. --- planet/cli/destinations.py | 2 +- planet/cli/features.py | 2 +- planet/cli/mosaics.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/planet/cli/destinations.py b/planet/cli/destinations.py index ee185fa3b..139378a9a 100644 --- a/planet/cli/destinations.py +++ b/planet/cli/destinations.py @@ -11,7 +11,7 @@ @asynccontextmanager async def destinations_client(ctx): - async with CliSession() as sess: + async with CliSession(ctx) as sess: cl = DestinationsClient(sess, base_url=ctx.obj['BASE_URL']) yield cl diff --git a/planet/cli/features.py b/planet/cli/features.py index 0c97c2314..6de62afd3 100644 --- a/planet/cli/features.py +++ b/planet/cli/features.py @@ -14,7 +14,7 @@ @asynccontextmanager async def features_client(ctx): - async with CliSession() as sess: + async with CliSession(ctx) as sess: cl = FeaturesClient(sess, base_url=ctx.obj['BASE_URL']) yield cl diff --git a/planet/cli/mosaics.py b/planet/cli/mosaics.py index 8254da449..ce25fa0ef 100644 --- a/planet/cli/mosaics.py +++ b/planet/cli/mosaics.py @@ -13,7 +13,7 @@ @asynccontextmanager async def client(ctx): - async with CliSession() as sess: + async with CliSession(ctx) as sess: cl = MosaicsClient(sess, base_url=ctx.obj['BASE_URL']) yield cl From 09ca7521c396693efcaa16b94ddf99800e26cca3 Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Tue, 7 Oct 2025 19:30:48 -0700 Subject: [PATCH 03/11] update comments --- ...storage__oauth_user_authcode__with_browser.py | 9 +++++---- ..._shared__oauth_user_authcode__with_browser.py | 9 +++++---- .../app_managed_auth_state__using_sdk_app_id.py | 3 +-- .../cli_managed_auth_state__explicit.py | 4 ++++ planet/auth.py | 16 +++++++++++----- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/examples/auth-session-management/app_managed_auth_state__app_custom_storage__oauth_user_authcode__with_browser.py b/examples/auth-session-management/app_managed_auth_state__app_custom_storage__oauth_user_authcode__with_browser.py index cdbfe6577..cc251cfe6 100644 --- a/examples/auth-session-management/app_managed_auth_state__app_custom_storage__oauth_user_authcode__with_browser.py +++ b/examples/auth-session-management/app_managed_auth_state__app_custom_storage__oauth_user_authcode__with_browser.py @@ -91,10 +91,11 @@ def example_main(): storage_provider=DemoStorageProvider(), ) - # In contrast to an in-memory only application that must initialize a login every - # time, an app with persistent storage can skip this when it is not needed. - if not plsdk_auth.is_initialized(): - plsdk_auth.user_login(allow_open_browser=True) + # In contrast to in-memory applications that must initialize a login every + # time, an app with persistent storage can skip user prompts when they + # are not needed. + # This helper will prompt the user only when it is necessary. + plsdk_auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True) # Create a Planet SDK object that uses the loaded auth session. sess = planet.Session(plsdk_auth) diff --git a/examples/auth-session-management/app_managed_auth_state__on_disk_cli_shared__oauth_user_authcode__with_browser.py b/examples/auth-session-management/app_managed_auth_state__on_disk_cli_shared__oauth_user_authcode__with_browser.py index 4beeb5a28..c4ad526d5 100644 --- a/examples/auth-session-management/app_managed_auth_state__on_disk_cli_shared__oauth_user_authcode__with_browser.py +++ b/examples/auth-session-management/app_managed_auth_state__on_disk_cli_shared__oauth_user_authcode__with_browser.py @@ -18,10 +18,11 @@ def example_main(): save_state_to_storage=True, ) - # In contrast to an in-memory only application that must initialize a login every - # time, an app with persistent storage can skip this when it is not needed. - if not plsdk_auth.is_initialized(): - plsdk_auth.user_login(allow_open_browser=True) + # In contrast to in-memory applications that must initialize a login every + # time, an app with persistent storage can skip user prompts when they + # are not needed. + # This helper will prompt the user only when it is necessary. + plsdk_auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True) # Create a Planet SDK object that uses the loaded auth session. sess = planet.Session(plsdk_auth) diff --git a/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py b/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py index afdc8fd96..7401b4edf 100644 --- a/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py +++ b/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py @@ -12,8 +12,7 @@ def example_main(): plsdk_auth = planet.Auth.from_profile("planet-user", save_state_to_storage=False) - if not plsdk_auth.is_initialized(): - plsdk_auth.user_login(allow_open_browser=True, allow_tty_prompt=True) + plsdk_auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True) # Create a Planet SDK object that uses the loaded auth session. sess = planet.Session(plsdk_auth) diff --git a/examples/auth-session-management/cli_managed_auth_state__explicit.py b/examples/auth-session-management/cli_managed_auth_state__explicit.py index 2ad7b8c42..deb2328e8 100644 --- a/examples/auth-session-management/cli_managed_auth_state__explicit.py +++ b/examples/auth-session-management/cli_managed_auth_state__explicit.py @@ -15,6 +15,10 @@ def example_main(): ) sys.exit(99) + # Alternatively, an application can call this, which will attempt to + # initialize the session if it is not already initialized. + # plsdk_auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True) + # Create a Planet SDK object that uses the loaded auth session. sess = planet.Session(plsdk_auth) pl = planet.Planet(sess) diff --git a/planet/auth.py b/planet/auth.py index aff200a0e..2db8db6e4 100644 --- a/planet/auth.py +++ b/planet/auth.py @@ -455,7 +455,12 @@ def is_initialized(self) -> bool: user based sessions, this means that a login has been performed or saved login session data has been located. For M2M and API Key sessions, this should be true if keys or secrets have been - properly configured. + properly configured. The network will not be probed, and the user + will not be prompted by this method. + + Expired sessions or invalid credentials will not be detected. + See `ensure_initialized()` for a method that will check the validity + of sessions. """ @abc.abstractmethod @@ -468,7 +473,7 @@ def ensure_initialized( unnecessary network requests or prompts for user interaction. This can be more complex than it sounds given the variations in - possible session state. Client may be initialized with active + possible session state. Clients may be initialized with active sessions, initialized with stale but still valid sessions, initialized with invalid or expired sessions, or completely uninitialized. The process taken to ensure client readiness with @@ -488,7 +493,8 @@ def ensure_initialized( the user will be prompted to perform a fresh login, requiring user interaction. 4. If the client has never been logged in and is user interactive - client (verses an M2M client), a user interactive login will be initiated. + client (verses an M2M client), a user interactive login will be + initiated. There still may be conditions where we believe we are ready, but requests will still ultimately fail. Saved secrets for M2M @@ -498,11 +504,11 @@ def ensure_initialized( whether a local web browser may be opened and/or whether the TTY may be used to prompt the user. - If the auth context cannot be made ready, an exception will be thrown. + If the auth context cannot be made ready, an exception will be raised. Parameters: allow_open_browser: specify whether login is permitted to open - a browser window. + a local browser window. allow_tty_prompt: specify whether login is permitted to request input from the terminal. """ From ffa52682f58c166e2c57fc39e967bbfe6368b021 Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Wed, 8 Oct 2025 10:13:01 -0700 Subject: [PATCH 04/11] depend on dev branch for dev. --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 04837eca5..f8d2ed4ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,8 @@ dependencies = [ "pyjwt>=2.1", "tqdm>=4.56", "typing-extensions", - "planet-auth>=2.3.0", +# "planet-auth>=2.3.0", + "planet-auth @ https://github.com/planetlabs/planet-auth-python/archive/refs/heads/carl/CG-1904--improve-expired-session-ux.zip", ] readme = "README.md" requires-python = ">=3.10" From bb140e0496c5e4c9746e97ae2ee22d1ebcf3593d Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Wed, 8 Oct 2025 10:19:22 -0700 Subject: [PATCH 05/11] update example --- .../app_managed_auth_state__using_sdk_app_id.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py b/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py index 7401b4edf..4a692ca48 100644 --- a/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py +++ b/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py @@ -10,7 +10,7 @@ def example_main(): # is false, the state will only be persistent in memory and the # user will need to login each time the application is run. plsdk_auth = planet.Auth.from_profile("planet-user", - save_state_to_storage=False) + save_state_to_storage=True) plsdk_auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True) From 3a54b3996a4081d4ef551158e3deea5db8c61f69 Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Wed, 8 Oct 2025 10:19:42 -0700 Subject: [PATCH 06/11] linting --- planet/auth.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/planet/auth.py b/planet/auth.py index 2db8db6e4..0a9a51c9e 100644 --- a/planet/auth.py +++ b/planet/auth.py @@ -513,6 +513,7 @@ def ensure_initialized( input from the terminal. """ + class APIKeyAuthException(PlanetError): """exceptions thrown by APIKeyAuth""" pass @@ -557,4 +558,5 @@ def ensure_initialized( allow_tty_prompt=allow_tty_prompt, ) + AuthType = Auth From 0c61e912a878cc29e477477275d7ac050e197762 Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Wed, 8 Oct 2025 10:24:56 -0700 Subject: [PATCH 07/11] linting and formatting --- ...p_custom_storage__oauth_user_authcode__with_browser.py | 3 ++- ..._disk_cli_shared__oauth_user_authcode__with_browser.py | 3 ++- .../app_managed_auth_state__using_sdk_app_id.py | 3 ++- planet/auth.py | 8 ++++++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/auth-session-management/app_managed_auth_state__app_custom_storage__oauth_user_authcode__with_browser.py b/examples/auth-session-management/app_managed_auth_state__app_custom_storage__oauth_user_authcode__with_browser.py index cc251cfe6..edfcb30aa 100644 --- a/examples/auth-session-management/app_managed_auth_state__app_custom_storage__oauth_user_authcode__with_browser.py +++ b/examples/auth-session-management/app_managed_auth_state__app_custom_storage__oauth_user_authcode__with_browser.py @@ -95,7 +95,8 @@ def example_main(): # time, an app with persistent storage can skip user prompts when they # are not needed. # This helper will prompt the user only when it is necessary. - plsdk_auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True) + plsdk_auth.ensure_initialized(allow_open_browser=True, + allow_tty_prompt=True) # Create a Planet SDK object that uses the loaded auth session. sess = planet.Session(plsdk_auth) diff --git a/examples/auth-session-management/app_managed_auth_state__on_disk_cli_shared__oauth_user_authcode__with_browser.py b/examples/auth-session-management/app_managed_auth_state__on_disk_cli_shared__oauth_user_authcode__with_browser.py index c4ad526d5..0067ffa28 100644 --- a/examples/auth-session-management/app_managed_auth_state__on_disk_cli_shared__oauth_user_authcode__with_browser.py +++ b/examples/auth-session-management/app_managed_auth_state__on_disk_cli_shared__oauth_user_authcode__with_browser.py @@ -22,7 +22,8 @@ def example_main(): # time, an app with persistent storage can skip user prompts when they # are not needed. # This helper will prompt the user only when it is necessary. - plsdk_auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True) + plsdk_auth.ensure_initialized(allow_open_browser=True, + allow_tty_prompt=True) # Create a Planet SDK object that uses the loaded auth session. sess = planet.Session(plsdk_auth) diff --git a/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py b/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py index 4a692ca48..63109fb23 100644 --- a/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py +++ b/examples/auth-session-management/app_managed_auth_state__using_sdk_app_id.py @@ -12,7 +12,8 @@ def example_main(): plsdk_auth = planet.Auth.from_profile("planet-user", save_state_to_storage=True) - plsdk_auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True) + plsdk_auth.ensure_initialized(allow_open_browser=True, + allow_tty_prompt=True) # Create a Planet SDK object that uses the loaded auth session. sess = planet.Session(plsdk_auth) diff --git a/planet/auth.py b/planet/auth.py index 0a9a51c9e..523d788c8 100644 --- a/planet/auth.py +++ b/planet/auth.py @@ -465,7 +465,9 @@ def is_initialized(self) -> bool: @abc.abstractmethod def ensure_initialized( - self, allow_open_browser: typing.Optional[bool] = False, allow_tty_prompt: typing.Optional[bool] = False + self, + allow_open_browser: typing.Optional[bool] = False, + allow_tty_prompt: typing.Optional[bool] = False, ) -> None: """ Do everything necessary to ensure the auth context is ready for use, @@ -551,7 +553,9 @@ def is_initialized(self) -> bool: return self._plauth.request_authenticator_is_ready() def ensure_initialized( - self, allow_open_browser: typing.Optional[bool] = False, allow_tty_prompt: typing.Optional[bool] = False + self, + allow_open_browser: typing.Optional[bool] = False, + allow_tty_prompt: typing.Optional[bool] = False, ) -> None: return self._plauth.ensure_request_authenticator_is_ready( allow_open_browser=allow_open_browser, From 268b9716811008bb66e97e64856ea6c72303bd93 Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Wed, 8 Oct 2025 10:25:17 -0700 Subject: [PATCH 08/11] dev dependencies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f8d2ed4ed..9cad33472 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ dependencies = [ "tqdm>=4.56", "typing-extensions", # "planet-auth>=2.3.0", - "planet-auth @ https://github.com/planetlabs/planet-auth-python/archive/refs/heads/carl/CG-1904--improve-expired-session-ux.zip", + "planet-auth @ git+https://github.com/planetlabs/planet-auth-python.git@73176aa20c55ada7ec8cf9928b720726355a5d3d#egg=planet-auth", ] readme = "README.md" requires-python = ">=3.10" From 307f6e06b620c1d3c42f2002755e52bc5f8e540e Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Wed, 8 Oct 2025 13:04:18 -0700 Subject: [PATCH 09/11] update dependencies --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9cad33472..5989ea18c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,8 +14,7 @@ dependencies = [ "pyjwt>=2.1", "tqdm>=4.56", "typing-extensions", -# "planet-auth>=2.3.0", - "planet-auth @ git+https://github.com/planetlabs/planet-auth-python.git@73176aa20c55ada7ec8cf9928b720726355a5d3d#egg=planet-auth", + "planet-auth>=2.3.0a1759946524", ] readme = "README.md" requires-python = ">=3.10" From 79c5eabaf15192ffd1fd1526e83737f04e4215ee Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Mon, 20 Oct 2025 09:13:36 -0700 Subject: [PATCH 10/11] update requirements --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5989ea18c..04837eca5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ "pyjwt>=2.1", "tqdm>=4.56", "typing-extensions", - "planet-auth>=2.3.0a1759946524", + "planet-auth>=2.3.0", ] readme = "README.md" requires-python = ">=3.10" From 9cebbdc3716c5ddeffdbccbe76ca1d6b3ec433f4 Mon Sep 17 00:00:00 2001 From: "Carl A. Adams" Date: Mon, 20 Oct 2025 09:21:39 -0700 Subject: [PATCH 11/11] small doc string edits. --- planet/auth.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/planet/auth.py b/planet/auth.py index 523d788c8..07149c634 100644 --- a/planet/auth.py +++ b/planet/auth.py @@ -494,17 +494,19 @@ def ensure_initialized( This should not require user interaction. If refresh fails, the user will be prompted to perform a fresh login, requiring user interaction. - 4. If the client has never been logged in and is user interactive + 4. If the client has never been logged in and is a user interactive client (verses an M2M client), a user interactive login will be initiated. There still may be conditions where we believe we are ready, but requests will still ultimately fail. Saved secrets for M2M - clients could be wrong, or the user could be denied by the API. + clients could be wrong, or the user could be denied by API access + rules that are independent of session authentication. When a user interactive login is required, the client must specify whether a local web browser may be opened and/or whether the TTY - may be used to prompt the user. + may be used to prompt the user. What is appropriate will depend + on the nature of the application using the Planet SDK. If the auth context cannot be made ready, an exception will be raised.