Skip to content

Commit c7011b6

Browse files
feat: add metrics (part 3) (#1305)
This PR adds `x-goog-api-client` header to - access token and id token refresh requests, for compute engine credentials / user credentials / service account credentials / impersonated credentials - reauth start and continue requests - metadata server ping requests Previous PRs: Part 1: #1298 Part 2: #1303
1 parent 5f3dd94 commit c7011b6

File tree

13 files changed

+355
-56
lines changed

13 files changed

+355
-56
lines changed

google/auth/compute_engine/_metadata.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from google.auth import _helpers
3030
from google.auth import environment_vars
3131
from google.auth import exceptions
32+
from google.auth import metrics
3233

3334
_LOGGER = logging.getLogger(__name__)
3435

@@ -121,13 +122,13 @@ def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT, retry_count=3):
121122
# the metadata resolution was particularly slow. The latter case is
122123
# "unlikely".
123124
retries = 0
125+
headers = _METADATA_HEADERS.copy()
126+
headers[metrics.API_CLIENT_HEADER] = metrics.mds_ping()
127+
124128
while retries < retry_count:
125129
try:
126130
response = request(
127-
url=_METADATA_IP_ROOT,
128-
method="GET",
129-
headers=_METADATA_HEADERS,
130-
timeout=timeout,
131+
url=_METADATA_IP_ROOT, method="GET", headers=headers, timeout=timeout
131132
)
132133

133134
metadata_flavor = response.headers.get(_METADATA_FLAVOR_HEADER)
@@ -150,7 +151,13 @@ def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT, retry_count=3):
150151

151152

152153
def get(
153-
request, path, root=_METADATA_ROOT, params=None, recursive=False, retry_count=5
154+
request,
155+
path,
156+
root=_METADATA_ROOT,
157+
params=None,
158+
recursive=False,
159+
retry_count=5,
160+
headers=None,
154161
):
155162
"""Fetch a resource from the metadata server.
156163
@@ -167,6 +174,7 @@ def get(
167174
details.
168175
retry_count (int): How many times to attempt connecting to metadata
169176
server using above timeout.
177+
headers (Optional[Mapping[str, str]]): Headers for the request.
170178
171179
Returns:
172180
Union[Mapping, str]: If the metadata server returns JSON, a mapping of
@@ -180,6 +188,10 @@ def get(
180188
base_url = urlparse.urljoin(root, path)
181189
query_params = {} if params is None else params
182190

191+
headers_to_use = _METADATA_HEADERS.copy()
192+
if headers:
193+
headers_to_use.update(headers)
194+
183195
if recursive:
184196
query_params["recursive"] = "true"
185197

@@ -188,7 +200,7 @@ def get(
188200
retries = 0
189201
while retries < retry_count:
190202
try:
191-
response = request(url=url, method="GET", headers=_METADATA_HEADERS)
203+
response = request(url=url, method="GET", headers=headers_to_use)
192204
break
193205

194206
except exceptions.TransportError as e:
@@ -300,8 +312,12 @@ def get_service_account_token(request, service_account="default", scopes=None):
300312
else:
301313
params = None
302314

315+
metrics_header = {
316+
metrics.API_CLIENT_HEADER: metrics.token_request_access_token_mds()
317+
}
318+
303319
path = "instance/service-accounts/{0}/token".format(service_account)
304-
token_json = get(request, path, params=params)
320+
token_json = get(request, path, params=params, headers=metrics_header)
305321
token_expiry = _helpers.utcnow() + datetime.timedelta(
306322
seconds=token_json["expires_in"]
307323
)

google/auth/compute_engine/credentials.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,12 @@ def _call_metadata_identity_endpoint(self, request):
378378
try:
379379
path = "instance/service-accounts/default/identity"
380380
params = {"audience": self._target_audience, "format": "full"}
381-
id_token = _metadata.get(request, path, params=params)
381+
metrics_header = {
382+
metrics.API_CLIENT_HEADER: metrics.token_request_id_token_mds()
383+
}
384+
id_token = _metadata.get(
385+
request, path, params=params, headers=metrics_header
386+
)
382387
except exceptions.TransportError as caught_exc:
383388
new_exc = exceptions.RefreshError(caught_exc)
384389
six.raise_from(new_exc, caught_exc)

google/auth/impersonated_credentials.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,10 @@ def _update_token(self, request):
265265
"lifetime": str(self._lifetime) + "s",
266266
}
267267

268-
headers = {"Content-Type": "application/json"}
268+
headers = {
269+
"Content-Type": "application/json",
270+
metrics.API_CLIENT_HEADER: metrics.token_request_access_token_impersonate(),
271+
}
269272

270273
# Apply the source credentials authentication info.
271274
self._source_credentials.apply(headers)
@@ -426,7 +429,10 @@ def refresh(self, request):
426429
"includeEmail": self._include_email,
427430
}
428431

429-
headers = {"Content-Type": "application/json"}
432+
headers = {
433+
"Content-Type": "application/json",
434+
metrics.API_CLIENT_HEADER: metrics.token_request_id_token_impersonate(),
435+
}
430436

431437
authed_session = AuthorizedSession(
432438
self._target_credentials._source_credentials, auth_request=request

google/oauth2/_client.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from google.auth import _helpers
3535
from google.auth import exceptions
3636
from google.auth import jwt
37+
from google.auth import metrics
3738
from google.auth import transport
3839

3940
_URLENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"
@@ -146,6 +147,7 @@ def _token_endpoint_request_no_throw(
146147
access_token=None,
147148
use_json=False,
148149
can_retry=True,
150+
headers=None,
149151
**kwargs
150152
):
151153
"""Makes a request to the OAuth 2.0 authorization server's token endpoint.
@@ -161,6 +163,7 @@ def _token_endpoint_request_no_throw(
161163
use_json (Optional(bool)): Use urlencoded format or json format for the
162164
content type. The default value is False.
163165
can_retry (bool): Enable or disable request retry behavior.
166+
headers (Optional[Mapping[str, str]]): The headers for the request.
164167
kwargs: Additional arguments passed on to the request method. The
165168
kwargs will be passed to `requests.request` method, see:
166169
https://docs.python-requests.org/en/latest/api/#requests.request.
@@ -176,18 +179,21 @@ def _token_endpoint_request_no_throw(
176179
is retryable.
177180
"""
178181
if use_json:
179-
headers = {"Content-Type": _JSON_CONTENT_TYPE}
182+
headers_to_use = {"Content-Type": _JSON_CONTENT_TYPE}
180183
body = json.dumps(body).encode("utf-8")
181184
else:
182-
headers = {"Content-Type": _URLENCODED_CONTENT_TYPE}
185+
headers_to_use = {"Content-Type": _URLENCODED_CONTENT_TYPE}
183186
body = urllib.parse.urlencode(body).encode("utf-8")
184187

185188
if access_token:
186-
headers["Authorization"] = "Bearer {}".format(access_token)
189+
headers_to_use["Authorization"] = "Bearer {}".format(access_token)
190+
191+
if headers:
192+
headers_to_use.update(headers)
187193

188194
def _perform_request():
189195
response = request(
190-
method="POST", url=token_uri, headers=headers, body=body, **kwargs
196+
method="POST", url=token_uri, headers=headers_to_use, body=body, **kwargs
191197
)
192198
response_body = (
193199
response.data.decode("utf-8")
@@ -231,6 +237,7 @@ def _token_endpoint_request(
231237
access_token=None,
232238
use_json=False,
233239
can_retry=True,
240+
headers=None,
234241
**kwargs
235242
):
236243
"""Makes a request to the OAuth 2.0 authorization server's token endpoint.
@@ -245,6 +252,7 @@ def _token_endpoint_request(
245252
use_json (Optional(bool)): Use urlencoded format or json format for the
246253
content type. The default value is False.
247254
can_retry (bool): Enable or disable request retry behavior.
255+
headers (Optional[Mapping[str, str]]): The headers for the request.
248256
kwargs: Additional arguments passed on to the request method. The
249257
kwargs will be passed to `requests.request` method, see:
250258
https://docs.python-requests.org/en/latest/api/#requests.request.
@@ -268,6 +276,7 @@ def _token_endpoint_request(
268276
access_token=access_token,
269277
use_json=use_json,
270278
can_retry=can_retry,
279+
headers=headers,
271280
**kwargs
272281
)
273282
if not response_status_ok:
@@ -301,7 +310,13 @@ def jwt_grant(request, token_uri, assertion, can_retry=True):
301310
body = {"assertion": assertion, "grant_type": _JWT_GRANT_TYPE}
302311

303312
response_data = _token_endpoint_request(
304-
request, token_uri, body, can_retry=can_retry
313+
request,
314+
token_uri,
315+
body,
316+
can_retry=can_retry,
317+
headers={
318+
metrics.API_CLIENT_HEADER: metrics.token_request_access_token_sa_assertion()
319+
},
305320
)
306321

307322
try:
@@ -384,7 +399,13 @@ def id_token_jwt_grant(request, token_uri, assertion, can_retry=True):
384399
body = {"assertion": assertion, "grant_type": _JWT_GRANT_TYPE}
385400

386401
response_data = _token_endpoint_request(
387-
request, token_uri, body, can_retry=can_retry
402+
request,
403+
token_uri,
404+
body,
405+
can_retry=can_retry,
406+
headers={
407+
metrics.API_CLIENT_HEADER: metrics.token_request_id_token_sa_assertion()
408+
},
388409
)
389410

390411
try:

google/oauth2/reauth.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from six.moves import range
3838

3939
from google.auth import exceptions
40+
from google.auth import metrics
4041
from google.oauth2 import _client
4142
from google.oauth2 import challenges
4243

@@ -94,9 +95,15 @@ def _get_challenges(
9495
body = {"supportedChallengeTypes": supported_challenge_types}
9596
if requested_scopes:
9697
body["oauthScopesForDomainPolicyLookup"] = requested_scopes
98+
metrics_header = {metrics.API_CLIENT_HEADER: metrics.reauth_start()}
9799

98100
return _client._token_endpoint_request(
99-
request, _REAUTH_API + ":start", body, access_token=access_token, use_json=True
101+
request,
102+
_REAUTH_API + ":start",
103+
body,
104+
access_token=access_token,
105+
use_json=True,
106+
headers=metrics_header,
100107
)
101108

102109

@@ -123,13 +130,15 @@ def _send_challenge_result(
123130
"action": "RESPOND",
124131
"proposalResponse": client_input,
125132
}
133+
metrics_header = {metrics.API_CLIENT_HEADER: metrics.reauth_continue()}
126134

127135
return _client._token_endpoint_request(
128136
request,
129137
_REAUTH_API + "/{}:continue".format(session_id),
130138
body,
131139
access_token=access_token,
132140
use_json=True,
141+
headers=metrics_header,
133142
)
134143

135144

@@ -320,9 +329,10 @@ def refresh_grant(
320329
body["scope"] = " ".join(scopes)
321330
if rapt_token:
322331
body["rapt"] = rapt_token
332+
metrics_header = {metrics.API_CLIENT_HEADER: metrics.token_request_user()}
323333

324334
response_status_ok, response_data, retryable_error = _client._token_endpoint_request_no_throw(
325-
request, token_uri, body
335+
request, token_uri, body, headers=metrics_header
326336
)
327337
if (
328338
not response_status_ok
@@ -345,7 +355,9 @@ def refresh_grant(
345355
response_status_ok,
346356
response_data,
347357
retryable_error,
348-
) = _client._token_endpoint_request_no_throw(request, token_uri, body)
358+
) = _client._token_endpoint_request_no_throw(
359+
request, token_uri, body, headers=metrics_header
360+
)
349361

350362
if not response_status_ok:
351363
_client._handle_error_response(response_data, retryable_error)

0 commit comments

Comments
 (0)