diff options
Diffstat (limited to 'google')
-rw-r--r-- | google/auth/exceptions.py | 4 | ||||
-rw-r--r-- | google/oauth2/_credentials_async.py | 1 | ||||
-rw-r--r-- | google/oauth2/_reauth_async.py | 9 | ||||
-rw-r--r-- | google/oauth2/challenges.py | 28 | ||||
-rw-r--r-- | google/oauth2/credentials.py | 11 | ||||
-rw-r--r-- | google/oauth2/reauth.py | 9 |
6 files changed, 61 insertions, 1 deletions
diff --git a/google/auth/exceptions.py b/google/auth/exceptions.py index 57f181e..e9e7377 100644 --- a/google/auth/exceptions.py +++ b/google/auth/exceptions.py @@ -57,3 +57,7 @@ class ReauthFailError(RefreshError): super(ReauthFailError, self).__init__( "Reauthentication failed. {0}".format(message) ) + + +class ReauthSamlChallengeFailError(ReauthFailError): + """An exception for SAML reauth challenge failures.""" diff --git a/google/oauth2/_credentials_async.py b/google/oauth2/_credentials_async.py index b4878c5..e7b9637 100644 --- a/google/oauth2/_credentials_async.py +++ b/google/oauth2/_credentials_async.py @@ -75,6 +75,7 @@ class Credentials(oauth2_credentials.Credentials): self._client_secret, scopes=self._scopes, rapt_token=self._rapt_token, + enable_reauth_refresh=self._enable_reauth_refresh, ) self.token = access_token diff --git a/google/oauth2/_reauth_async.py b/google/oauth2/_reauth_async.py index 510578b..f74f50b 100644 --- a/google/oauth2/_reauth_async.py +++ b/google/oauth2/_reauth_async.py @@ -248,6 +248,7 @@ async def refresh_grant( client_secret, scopes=None, rapt_token=None, + enable_reauth_refresh=False, ): """Implements the reauthentication flow. @@ -265,6 +266,9 @@ async def refresh_grant( token has a wild card scope (e.g. 'https://www.googleapis.com/auth/any-api'). rapt_token (Optional(str)): The rapt token for reauth. + enable_reauth_refresh (Optional[bool]): Whether reauth refresh flow + should be used. The default value is False. This option is for + gcloud only, other users should use the default value. Returns: Tuple[str, Optional[str], Optional[datetime], Mapping[str, str], str]: The @@ -299,6 +303,11 @@ async def refresh_grant( == reauth._REAUTH_NEEDED_ERROR_RAPT_REQUIRED ) ): + if not enable_reauth_refresh: + raise exceptions.RefreshError( + "Reauthentication is needed. Please run `gcloud auth login --update-adc` to reauthenticate." + ) + rapt_token = await get_rapt_token( request, client_id, client_secret, refresh_token, token_uri, scopes=scopes ) diff --git a/google/oauth2/challenges.py b/google/oauth2/challenges.py index 7756a80..0baff62 100644 --- a/google/oauth2/challenges.py +++ b/google/oauth2/challenges.py @@ -25,6 +25,9 @@ from google.auth import exceptions REAUTH_ORIGIN = "https://accounts.google.com" +SAML_CHALLENGE_MESSAGE = ( + "Please run `gcloud auth login` to complete reauthentication with SAML." +) def get_user_password(text): @@ -148,7 +151,30 @@ class SecurityKeyChallenge(ReauthChallenge): return None +class SamlChallenge(ReauthChallenge): + """Challenge that asks the users to browse to their ID Providers. + + Currently SAML challenge is not supported. When obtaining the challenge + input, exception will be raised to instruct the users to run + `gcloud auth login` for reauthentication. + """ + + @property + def name(self): + return "SAML" + + @property + def is_locally_eligible(self): + return True + + def obtain_challenge_input(self, metadata): + # Magic Arch has not fully supported returning a proper dedirect URL + # for programmatic SAML users today. So we error our here and request + # users to use gcloud to complete a login. + raise exceptions.ReauthSamlChallengeFailError(SAML_CHALLENGE_MESSAGE) + + AVAILABLE_CHALLENGES = { challenge.name: challenge - for challenge in [SecurityKeyChallenge(), PasswordChallenge()] + for challenge in [SecurityKeyChallenge(), PasswordChallenge(), SamlChallenge()] } diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py index 98fd71b..e259f78 100644 --- a/google/oauth2/credentials.py +++ b/google/oauth2/credentials.py @@ -54,6 +54,9 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr credentials = credentials.with_quota_project('myproject-123) + Reauth is disabled by default. To enable reauth, set the + `enable_reauth_refresh` parameter to True in the constructor. Note that + reauth feature is intended for gcloud to use only. If reauth is enabled, `pyu2f` dependency has to be installed in order to use security key reauth feature. Dependency can be installed via `pip install pyu2f` or `pip install google-auth[reauth]`. @@ -73,6 +76,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr expiry=None, rapt_token=None, refresh_handler=None, + enable_reauth_refresh=False, ): """ Args: @@ -109,6 +113,8 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr refresh tokens are provided and tokens are obtained by calling some external process on demand. It is particularly useful for retrieving downscoped tokens from a token broker. + enable_reauth_refresh (Optional[bool]): Whether reauth refresh flow + should be used. This flag is for gcloud to use only. """ super(Credentials, self).__init__() self.token = token @@ -123,6 +129,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr self._quota_project_id = quota_project_id self._rapt_token = rapt_token self.refresh_handler = refresh_handler + self._enable_reauth_refresh = enable_reauth_refresh def __getstate__(self): """A __getstate__ method must exist for the __setstate__ to be called @@ -151,6 +158,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr self._client_secret = d.get("_client_secret") self._quota_project_id = d.get("_quota_project_id") self._rapt_token = d.get("_rapt_token") + self._enable_reauth_refresh = d.get("_enable_reauth_refresh") # The refresh_handler setter should be used to repopulate this. self._refresh_handler = None @@ -241,6 +249,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr default_scopes=self.default_scopes, quota_project_id=quota_project_id, rapt_token=self.rapt_token, + enable_reauth_refresh=self._enable_reauth_refresh, ) @_helpers.copy_docstring(credentials.Credentials) @@ -296,6 +305,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr self._client_secret, scopes=scopes, rapt_token=self._rapt_token, + enable_reauth_refresh=self._enable_reauth_refresh, ) self.token = access_token @@ -366,6 +376,7 @@ class Credentials(credentials.ReadOnlyScoped, credentials.CredentialsWithQuotaPr client_secret=info.get("client_secret"), quota_project_id=info.get("quota_project_id"), # may not exist expiry=expiry, + rapt_token=info.get("rapt_token"), # may not exist ) @classmethod diff --git a/google/oauth2/reauth.py b/google/oauth2/reauth.py index fc2629e..1e496d1 100644 --- a/google/oauth2/reauth.py +++ b/google/oauth2/reauth.py @@ -275,6 +275,7 @@ def refresh_grant( client_secret, scopes=None, rapt_token=None, + enable_reauth_refresh=False, ): """Implements the reauthentication flow. @@ -292,6 +293,9 @@ def refresh_grant( token has a wild card scope (e.g. 'https://www.googleapis.com/auth/any-api'). rapt_token (Optional(str)): The rapt token for reauth. + enable_reauth_refresh (Optional[bool]): Whether reauth refresh flow + should be used. The default value is False. This option is for + gcloud only, other users should use the default value. Returns: Tuple[str, Optional[str], Optional[datetime], Mapping[str, str], str]: The @@ -324,6 +328,11 @@ def refresh_grant( or response_data.get("error_subtype") == _REAUTH_NEEDED_ERROR_RAPT_REQUIRED ) ): + if not enable_reauth_refresh: + raise exceptions.RefreshError( + "Reauthentication is needed. Please run `gcloud auth login --update-adc` to reauthenticate." + ) + rapt_token = get_rapt_token( request, client_id, client_secret, refresh_token, token_uri, scopes=scopes ) |