Skip to content

Commit

Permalink
Merge branch 'master' into gae-gen2-apis
Browse files Browse the repository at this point in the history
  • Loading branch information
busunkim96 committed Jul 20, 2021
2 parents f2c42c0 + 343c264 commit d90e8fd
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 18 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,18 @@

[1]: https://pypi.org/project/google-auth/#history

### [1.33.1](https://www.github.com/googleapis/google-auth-library-python/compare/v1.33.0...v1.33.1) (2021-07-20)


### Bug Fixes

* fallback to source creds expiration in downscoped tokens ([#805](https://www.github.com/googleapis/google-auth-library-python/issues/805)) ([dfad661](https://www.github.com/googleapis/google-auth-library-python/commit/dfad66128c6ee7513e5565d39bc7b002055dd0d5))


### Reverts

* revert "feat: service account is able to use a private token endpoint ([#784](https://www.github.com/googleapis/google-auth-library-python/issues/784))" ([#808](https://www.github.com/googleapis/google-auth-library-python/issues/808)) ([d94e65c](https://www.github.com/googleapis/google-auth-library-python/commit/d94e65c0e441183403608d762b92b30b77e21eeb))

## [1.33.0](https://www.github.com/googleapis/google-auth-library-python/compare/v1.32.1...v1.33.0) (2021-07-14)


Expand Down
12 changes: 10 additions & 2 deletions google/auth/downscoped.py
Expand Up @@ -479,8 +479,16 @@ def refresh(self, request):
additional_options=self._credential_access_boundary.to_json(),
)
self.token = response_data.get("access_token")
lifetime = datetime.timedelta(seconds=response_data.get("expires_in"))
self.expiry = now + lifetime
# For downscoping CAB flow, the STS endpoint may not return the expiration
# field for some flows. The generated downscoped token should always have
# the same expiration time as the source credentials. When no expires_in
# field is returned in the response, we can just get the expiration time
# from the source credentials.
if response_data.get("expires_in"):
lifetime = datetime.timedelta(seconds=response_data.get("expires_in"))
self.expiry = now + lifetime
else:
self.expiry = self._source_credentials.expiry

@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
def with_quota_project(self, quota_project_id):
Expand Down
2 changes: 1 addition & 1 deletion google/auth/version.py
Expand Up @@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = "1.33.0"
__version__ = "1.33.1"
5 changes: 2 additions & 3 deletions google/oauth2/service_account.py
Expand Up @@ -80,7 +80,6 @@
from google.oauth2 import _client

_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"


class Credentials(
Expand Down Expand Up @@ -383,7 +382,7 @@ def _make_authorization_grant_assertion(self):
# The issuer must be the service account email.
"iss": self._service_account_email,
# The audience must be the auth token endpoint's URI
"aud": _GOOGLE_OAUTH2_TOKEN_ENDPOINT,
"aud": self._token_uri,
"scope": _helpers.scopes_to_string(self._scopes or ()),
}

Expand Down Expand Up @@ -644,7 +643,7 @@ def _make_authorization_grant_assertion(self):
# The issuer must be the service account email.
"iss": self.service_account_email,
# The audience must be the auth token endpoint's URI
"aud": _GOOGLE_OAUTH2_TOKEN_ENDPOINT,
"aud": self._token_uri,
# The target audience specifies which service the ID token is
# intended for.
"target_audience": self._target_audience,
Expand Down
4 changes: 2 additions & 2 deletions tests/oauth2/test_service_account.py
Expand Up @@ -167,7 +167,7 @@ def test__make_authorization_grant_assertion(self):
token = credentials._make_authorization_grant_assertion()
payload = jwt.decode(token, PUBLIC_CERT_BYTES)
assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
assert payload["aud"] == service_account._GOOGLE_OAUTH2_TOKEN_ENDPOINT
assert payload["aud"] == self.TOKEN_URI

def test__make_authorization_grant_assertion_scoped(self):
credentials = self.make_credentials()
Expand Down Expand Up @@ -440,7 +440,7 @@ def test__make_authorization_grant_assertion(self):
token = credentials._make_authorization_grant_assertion()
payload = jwt.decode(token, PUBLIC_CERT_BYTES)
assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
assert payload["aud"] == service_account._GOOGLE_OAUTH2_TOKEN_ENDPOINT
assert payload["aud"] == self.TOKEN_URI
assert payload["target_audience"] == self.TARGET_AUDIENCE

@mock.patch("google.oauth2._client.id_token_jwt_grant", autospec=True)
Expand Down
46 changes: 44 additions & 2 deletions tests/test_downscoped.py
Expand Up @@ -80,10 +80,11 @@


class SourceCredentials(credentials.Credentials):
def __init__(self, raise_error=False):
def __init__(self, raise_error=False, expires_in=3600):
super(SourceCredentials, self).__init__()
self._counter = 0
self._raise_error = raise_error
self._expires_in = expires_in

def refresh(self, request):
if self._raise_error:
Expand All @@ -93,7 +94,7 @@ def refresh(self, request):
now = _helpers.utcnow()
self._counter += 1
self.token = "ACCESS_TOKEN_{}".format(self._counter)
self.expiry = now + datetime.timedelta(seconds=3600)
self.expiry = now + datetime.timedelta(seconds=self._expires_in)


def make_availability_condition(expression, title=None, description=None):
Expand Down Expand Up @@ -539,6 +540,47 @@ def test_refresh(self, unused_utcnow):
# Confirm source credentials called with the same request instance.
wrapped_souce_cred_refresh.assert_called_with(request)

@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
def test_refresh_without_response_expires_in(self, unused_utcnow):
response = SUCCESS_RESPONSE.copy()
# Simulate the response is missing the expires_in field.
# The downscoped token expiration should match the source credentials
# expiration.
del response["expires_in"]
expected_expires_in = 1800
# Simulate the source credentials generates a token with 1800 second
# expiration time. The generated downscoped token should have the same
# expiration time.
source_credentials = SourceCredentials(expires_in=expected_expires_in)
expected_expiry = datetime.datetime.min + datetime.timedelta(
seconds=expected_expires_in
)
headers = {"Content-Type": "application/x-www-form-urlencoded"}
request_data = {
"grant_type": GRANT_TYPE,
"subject_token": "ACCESS_TOKEN_1",
"subject_token_type": SUBJECT_TOKEN_TYPE,
"requested_token_type": REQUESTED_TOKEN_TYPE,
"options": urllib.parse.quote(json.dumps(CREDENTIAL_ACCESS_BOUNDARY_JSON)),
}
request = self.make_mock_request(status=http_client.OK, data=response)
credentials = self.make_credentials(source_credentials=source_credentials)

# Spy on calls to source credentials refresh to confirm the expected request
# instance is used.
with mock.patch.object(
source_credentials, "refresh", wraps=source_credentials.refresh
) as wrapped_souce_cred_refresh:
credentials.refresh(request)

self.assert_request_kwargs(request.call_args[1], headers, request_data)
assert credentials.valid
assert credentials.expiry == expected_expiry
assert not credentials.expired
assert credentials.token == response["access_token"]
# Confirm source credentials called with the same request instance.
wrapped_souce_cred_refresh.assert_called_with(request)

def test_refresh_token_exchange_error(self):
request = self.make_mock_request(
status=http_client.BAD_REQUEST, data=ERROR_RESPONSE
Expand Down
10 changes: 2 additions & 8 deletions tests_async/oauth2/test_service_account_async.py
Expand Up @@ -152,10 +152,7 @@ def test__make_authorization_grant_assertion(self):
token = credentials._make_authorization_grant_assertion()
payload = jwt.decode(token, test_service_account.PUBLIC_CERT_BYTES)
assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
assert (
payload["aud"]
== service_account.service_account._GOOGLE_OAUTH2_TOKEN_ENDPOINT
)
assert payload["aud"] == self.TOKEN_URI

def test__make_authorization_grant_assertion_scoped(self):
credentials = self.make_credentials()
Expand Down Expand Up @@ -314,10 +311,7 @@ def test__make_authorization_grant_assertion(self):
token = credentials._make_authorization_grant_assertion()
payload = jwt.decode(token, test_service_account.PUBLIC_CERT_BYTES)
assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
assert (
payload["aud"]
== service_account.service_account._GOOGLE_OAUTH2_TOKEN_ENDPOINT
)
assert payload["aud"] == self.TOKEN_URI
assert payload["target_audience"] == self.TARGET_AUDIENCE

@mock.patch("google.oauth2._client_async.id_token_jwt_grant", autospec=True)
Expand Down

0 comments on commit d90e8fd

Please sign in to comment.