Add downscoping to ouath2 credentials (#309)
diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py
index 4cb909c..b56e314 100644
--- a/google/oauth2/credentials.py
+++ b/google/oauth2/credentials.py
@@ -67,10 +67,13 @@
client_secret(str): The OAuth 2.0 client secret. Must be specified
for refresh, can be left as None if the token can not be
refreshed.
- scopes (Sequence[str]): The scopes that were originally used
- to obtain authorization. This is a purely informative parameter
- that can be used by :meth:`has_scopes`. OAuth 2.0 credentials
- can not request additional scopes after authorization.
+ scopes (Sequence[str]): The scopes used to obtain authorization.
+ This parameter is used by :meth:`has_scopes`. OAuth 2.0
+ credentials can not request additional scopes after
+ authorization. The scopes must be derivable from the refresh
+ token if refresh information is provided (e.g. The refresh
+ token scopes are a superset of this or contain a wild card
+ scope like 'https://www.googleapis.com/auth/any-api').
"""
super(Credentials, self).__init__()
self.token = token
@@ -133,13 +136,24 @@
access_token, refresh_token, expiry, grant_response = (
_client.refresh_grant(
request, self._token_uri, self._refresh_token, self._client_id,
- self._client_secret))
+ self._client_secret, self._scopes))
self.token = access_token
self.expiry = expiry
self._refresh_token = refresh_token
self._id_token = grant_response.get('id_token')
+ if self._scopes and 'scopes' in grant_response:
+ requested_scopes = frozenset(self._scopes)
+ granted_scopes = frozenset(grant_response['scopes'].split())
+ scopes_requested_but_not_granted = (
+ requested_scopes - granted_scopes)
+ if scopes_requested_but_not_granted:
+ raise exceptions.RefreshError(
+ 'Not all requested scopes were granted by the '
+ 'authorization server, missing scopes {}.'.format(
+ ', '.join(scopes_requested_but_not_granted)))
+
@classmethod
def from_authorized_user_info(cls, info, scopes=None):
"""Creates a Credentials instance from parsed authorized user info.