def test_refresh_source_credentials(self, time_skew): credentials = self.make_credentials(lifetime=None) # Source credentials is refreshed only if it is expired within # _helpers.CLOCK_SKEW from now. We add a time_skew to the expiry, so # source credentials is refreshed only if time_skew <= 0. credentials._source_credentials.expiry = ( _helpers.utcnow() + _helpers.CLOCK_SKEW + datetime.timedelta(seconds=time_skew)) credentials._source_credentials.token = "Token" with mock.patch("google.oauth2.service_account.Credentials.refresh", autospec=True) as source_cred_refresh: expire_time = ( _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500)).isoformat("T") + "Z" response_body = {"accessToken": "token", "expireTime": expire_time} request = self.make_request(data=json.dumps(response_body), status=http_client.OK) credentials.refresh(request) assert credentials.valid assert not credentials.expired # Source credentials is refreshed only if it is expired within # _helpers.CLOCK_SKEW if time_skew > 0: source_cred_refresh.assert_not_called() else: source_cred_refresh.assert_called_once()
def test_decode_success_with_custom_clock_skew(token_factory): token = token_factory( claims={ "exp": _helpers.datetime_to_secs(_helpers.utcnow() + datetime.timedelta(seconds=2)), "iat": _helpers.datetime_to_secs(_helpers.utcnow() - datetime.timedelta(seconds=2)), }) jwt.decode(token, PUBLIC_CERT_BYTES, clock_skew_in_seconds=1)
def refresh(self, request): # Generate an access token from the source credentials. self._source_credentials.refresh(request) now = _helpers.utcnow() # Exchange the access token for a downscoped access token. response_data = self._sts_client.exchange_token( request=request, grant_type=_STS_GRANT_TYPE, subject_token=self._source_credentials.token, subject_token_type=_STS_SUBJECT_TOKEN_TYPE, requested_token_type=_STS_REQUESTED_TOKEN_TYPE, additional_options=self._credential_access_boundary.to_json(), ) self.token = response_data.get("access_token") # 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
def test_credentials_with_scopes_requested_refresh_success( self, unused_utcnow, refresh_grant): scopes = ["email", "profile"] default_scopes = ["https://www.googleapis.com/auth/cloud-platform"] token = "token" new_rapt_token = "new_rapt_token" expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) grant_response = { "id_token": mock.sentinel.id_token, "scope": "email profile" } refresh_grant.return_value = ( # Access token token, # New refresh token None, # Expiry, expiry, # Extra data grant_response, # rapt token new_rapt_token, ) request = mock.create_autospec(transport.Request) creds = credentials.Credentials( token=None, refresh_token=self.REFRESH_TOKEN, token_uri=self.TOKEN_URI, client_id=self.CLIENT_ID, client_secret=self.CLIENT_SECRET, scopes=scopes, default_scopes=default_scopes, rapt_token=self.RAPT_TOKEN, ) # Refresh credentials creds.refresh(request) # Check jwt grant call. refresh_grant.assert_called_with( request, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET, scopes, self.RAPT_TOKEN, ) # Check that the credentials have the token and expiry assert creds.token == token assert creds.expiry == expiry assert creds.id_token == mock.sentinel.id_token assert creds.has_scopes(scopes) assert creds.rapt_token == new_rapt_token # Check that the credentials are valid (have a token and are not # expired.) assert creds.valid
def mock_donor_credentials(): with mock.patch('google.oauth2._client.jwt_grant', autospec=True) as grant: grant.return_value = ( "source token", _helpers.utcnow() + datetime.timedelta(seconds=500), {}) yield grant
def test_refresh_failure_lifetime_specified(self, mock_donor_credentials): credentials = self.make_credentials(lifetime=500) token = 'token' expire_time = ( _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500)).isoformat('T') + 'Z' response_body = { "accessToken": token, "expireTime": expire_time } request = self.make_request( data=json.dumps(response_body), status=http_client.OK) credentials.refresh(request) with pytest.raises(exceptions.RefreshError) as excinfo: credentials.refresh(request) assert excinfo.match(impersonated_credentials._LIFETIME_ERROR) assert not credentials.valid assert credentials.expired
def refresh(self, request): data = self._get_token(request, self._scopes) seconds = data["expires_in"] token_expiry = _helpers.utcnow() + datetime.timedelta(seconds=seconds) self.token = data["access_token"] self.expiry = token_expiry
def test_refresh_jwt_not_used_for_domain_wide_delegation( self, self_signed_jwt_refresh, jwt_grant ): # Create a domain wide delegation credentials by setting the subject. credentials = service_account.Credentials( SIGNER, self.SERVICE_ACCOUNT_EMAIL, self.TOKEN_URI, always_use_jwt_access=True, subject="subject", ) credentials._create_self_signed_jwt("https://pubsub.googleapis.com") jwt_grant.return_value = ( "token", _helpers.utcnow() + datetime.timedelta(seconds=500), {}, ) request = mock.create_autospec(transport.Request, instance=True) # Refresh credentials credentials.refresh(request) # Make sure we are using jwt_grant and not self signed JWT refresh # method to obtain the token. assert jwt_grant.called assert not self_signed_jwt_refresh.called
def make_refresh_authorization_grant_assertion(token_info: TokenInfo, service_accout: ServiceAccount): """Create the OAuth 2.0 assertion. This assertion is used during the OAuth 2.0 grant to acquire an access token. Returns: bytes: The authorization grant assertion. """ now = _helpers.utcnow() lifetime = timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS) expiry = now + lifetime payload = { 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. 'iss': service_accout.credentials.service_account_email, # The audience must be the auth token endpoint's URI 'aud': token_info.aud, 'scope': token_info.scope, } # The subject can be a user email for domain-wide delegation. payload.setdefault('sub', token_info.uid) token = jwt.encode(service_accout.credentials._signer, payload) return token
def __init__(self, source_credentials, target_principal, target_scopes, delegates=None, lifetime=_DEFAULT_TOKEN_LIFETIME_SECS): """ Args: source_credentials (google.auth.Credentials): The source credential used as to acquire the impersonated credentials. target_principal (str): The service account to impersonate. target_scopes (Sequence[str]): Scopes to request during the authorization grant. delegates (Sequence[str]): The chained list of delegates required to grant the final access_token. If set, the sequence of identities must have "Service Account Token Creator" capability granted to the prceeding identity. For example, if set to [serviceAccountB, serviceAccountC], the source_credential must have the Token Creator role on serviceAccountB. serviceAccountB must have the Token Creator on serviceAccountC. Finally, C must have Token Creator on target_principal. If left unset, source_credential must have that role on target_principal. lifetime (int): Number of seconds the delegated credential should be valid for (upto 3600). """ super(Credentials, self).__init__() self._source_credentials = copy.copy(source_credentials) self._source_credentials._scopes = _IAM_SCOPE self._target_principal = target_principal self._target_scopes = target_scopes self._delegates = delegates self._lifetime = lifetime self.token = None self.expiry = _helpers.utcnow()
def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. This assertion is used during the OAuth 2.0 grant to acquire an access token. Returns: bytes: The authorization grant assertion. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS) expiry = now + lifetime payload = { 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. 'iss': self._service_account_email, # The audience must be the auth token endpoint's URI 'aud': self._token_uri, 'scope': _helpers.scopes_to_string(self._scopes or ()) } payload.update(self._additional_claims) # The subject can be a user email for domain-wide delegation. if self._subject: payload.setdefault('sub', self._subject) token = jwt.encode(self._signer, payload) return token
async def test_id_token_jwt_grant(): now = _helpers.utcnow() id_token_expiry = _helpers.datetime_to_secs(now) id_token = jwt.encode(test_client.SIGNER, { "exp": id_token_expiry }).decode("utf-8") request = make_request({"id_token": id_token, "extra": "data"}) token, expiry, extra_data = await _client.id_token_jwt_grant( request, "http://example.com", "assertion_value") # Check request call verify_request_params( request, { "grant_type": sync_client._JWT_GRANT_TYPE, "assertion": "assertion_value" }, ) # Check result assert token == id_token # JWT does not store microseconds now = now.replace(microsecond=0) assert expiry == now assert extra_data["extra"] == "data"
def test_with_quota_project_iam_endpoint_override( self, use_data_bytes, mock_donor_credentials ): credentials = self.make_credentials( lifetime=None, iam_endpoint_override=self.IAM_ENDPOINT_OVERRIDE ) token = "token" # iam_endpoint_override should be copied to created credentials. quota_project_creds = credentials.with_quota_project("project-foo") expire_time = ( _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) ).isoformat("T") + "Z" response_body = {"accessToken": token, "expireTime": expire_time} request = self.make_request( data=json.dumps(response_body), status=http_client.OK, use_data_bytes=use_data_bytes, ) quota_project_creds.refresh(request) assert quota_project_creds.valid assert not quota_project_creds.expired # Confirm override endpoint used. request_kwargs = request.call_args[1] assert request_kwargs["url"] == self.IAM_ENDPOINT_OVERRIDE
def get_service_account_token(request, service_account='default', scopes=None): """Get the OAuth 2.0 access token for a service account. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. service_account (str): The string 'default' or a service account email address. The determines which service account for which to acquire an access token. scopes (Sequence[str]): A list of OAuth scopes the token should contain. Defaults to cloud-platform if not provided. Returns: Union[str, datetime]: The access token and its expiration. Raises: google.auth.exceptions.TransportError: if an error occurred while retrieving metadata. """ path = 'instance/service-accounts/{0}/token'.format( urlparse.quote(service_account)) if scopes is not None: query = urlparse.urlencode({'scopes': ",".join(scopes)}) path = '{0}?{1}'.format(path, query) token_json = get(request, path) token_expiry = _helpers.utcnow() + datetime.timedelta( seconds=token_json["expires_in"]) return token_json["access_token"], token_expiry
def __init__( self, source_credentials, downscoped_options={}, ): """ Args: source_credentials (google.auth.Credentials): The source credential used as to acquire the downscoped credentials. access_boundary_rules (Sequence): JSON structure format for a list bound tokens { "accessBoundaryRules" : [ { "availableResource" : "//storage.googleapis.com/projects/_/buckets/bucketA", "availablePermissions": ["inRole:roles/storage.objectViewer"], "availabilityCondition" : { "title" : "obj-prefixes", "expression" : "resource.name.startsWith(\"projects/_/buckets/bucketA/objects/foo.txt\")" } } ] } """ super(Credentials, self).__init__() self._source_credentials = copy.copy(source_credentials) if not 'accessBoundary' in downscoped_options: raise exceptions.GoogleAuthError( "Provided access_boundary_rules must include accessBoundary dictionary key" ) self._downscoped_options = downscoped_options self.token = None self.expiry = _helpers.utcnow()
def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. This assertion is used during the OAuth 2.0 grant to acquire an ID token. Returns: bytes: The authorization grant assertion. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS) expiry = now + lifetime payload = { 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. 'iss': self.service_account_email, # The audience must be the auth token endpoint's URI 'aud': self._token_uri, # The target audience specifies which service the ID token is # intended for. 'target_audience': self._target_audience } payload.update(self._additional_claims) token = jwt.encode(self._signer, payload) return token
def refresh(self, request): scopes = self._scopes if self._scopes is not None else self._default_scopes if self._impersonated_credentials: self._impersonated_credentials.refresh(request) self.token = self._impersonated_credentials.token self.expiry = self._impersonated_credentials.expiry else: now = _helpers.utcnow() additional_options = None # Do not pass workforce_pool_user_project when client authentication # is used. The client ID is sufficient for determining the user project. if self._workforce_pool_user_project and not self._client_id: additional_options = { "userProject": self._workforce_pool_user_project } response_data = self._sts_client.exchange_token( request=request, grant_type=_STS_GRANT_TYPE, subject_token=self.retrieve_subject_token(request), subject_token_type=self._subject_token_type, audience=self._audience, scopes=scopes, requested_token_type=_STS_REQUESTED_TOKEN_TYPE, additional_options=additional_options, ) self.token = response_data.get("access_token") lifetime = datetime.timedelta( seconds=response_data.get("expires_in")) self.expiry = now + lifetime
def test_refresh_with_jwt_credentials(self, make_jwt): credentials = self.make_credentials() credentials._create_self_signed_jwt("https://pubsub.googleapis.com") request = mock.create_autospec(transport.Request, instance=True) token = "token" expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) make_jwt.return_value = (token, expiry) # Credentials should start as invalid assert not credentials.valid # before_request should cause a refresh credentials.before_request(request, "GET", "http://example.com?a=1#3", {}) # Credentials should now be valid. assert credentials.valid # Assert make_jwt was called assert make_jwt.called_once() assert credentials.token == token assert credentials.expiry == expiry
def test_refresh_success(self, now_mock, refresh_grant_mock): token = 'token' expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) refresh_grant_mock.return_value = ( # Access token token, # New refresh token None, # Expiry, expiry, # Extra data {}) request_mock = mock.Mock() # Refresh credentials self.credentials.refresh(request_mock) # Check jwt grant call. refresh_grant_mock.assert_called_with(request_mock, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET) # Check that the credentials have the token and expiry assert self.credentials.token == token assert self.credentials.expiry == expiry # Check that the credentials are valid (have a token and are not # expired) assert self.credentials.valid
def test_id_token_with_include_email( self, mock_donor_credentials, mock_authorizedsession_idtoken ): credentials = self.make_credentials(lifetime=None) token = "token" target_audience = "https://foo.bar" expire_time = ( _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=500) ).isoformat("T") + "Z" response_body = {"accessToken": token, "expireTime": expire_time} request = self.make_request( data=json.dumps(response_body), status=http_client.OK ) credentials.refresh(request) assert credentials.valid assert not credentials.expired id_creds = impersonated_credentials.IDTokenCredentials( credentials, target_audience=target_audience ) id_creds = id_creds.with_include_email(True) id_creds.refresh(request) assert id_creds.token == ID_TOKEN_DATA
def _verify_iat_and_exp(payload): """Verifies the ``iat`` (Issued At) and ``exp`` (Expires) claims in a token payload. Args: payload (Mapping[str, str]): The JWT payload. Raises: ValueError: if any checks failed. """ now = _helpers.datetime_to_secs(_helpers.utcnow()) # Make sure the iat and exp claims are present for key in ('iat', 'exp'): if key not in payload: raise ValueError( 'Token does not contain required claim {}'.format(key)) # Make sure the token wasn't issued in the future iat = payload['iat'] earliest = iat - _CLOCK_SKEW_SECS if now < earliest: raise ValueError('Token used too early, {} < {}'.format(now, iat)) # Make sure the token wasn't issue in the past exp = payload['exp'] latest = exp + _CLOCK_SKEW_SECS if latest < now: raise ValueError('Token expired, {} < {}'.format(latest, now))
def _make_jwt(self, audience=None): """Make a signed JWT. Args: audience (str): Overrides the instance's current audience claim. Returns: Tuple[bytes, datetime]: The encoded JWT and the expiration. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=self._token_lifetime) expiry = now + lifetime payload = { 'iss': self._issuer, 'sub': self._subject or self._issuer, 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), 'aud': audience or self._audience, } payload.update(self._additional_claims) jwt = encode(self._signer, payload) return jwt, expiry
def refresh(self, request): # pylint: disable=unused-argument token, ttl = app_identity.get_access_token( self._scopes, self._service_account_id) expiry = _helpers.utcnow() + datetime.timedelta(seconds=ttl) self.token, self.expiry = token, expiry
def _make_jwt_for_audience(self, audience): """Make a new JWT for the given audience. Args: audience (str): The intended audience. Returns: Tuple[bytes, datetime]: The encoded JWT and the expiration. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=self._token_lifetime) expiry = now + lifetime payload = { 'iss': self._issuer, 'sub': self._subject, 'iat': _helpers.datetime_to_secs(now), 'exp': _helpers.datetime_to_secs(expiry), 'aud': audience, } payload.update(self._additional_claims) jwt = encode(self._signer, payload) return jwt, expiry
def _verify_iat_and_exp(payload): """Verifies the ``iat`` (Issued At) and ``exp`` (Expires) claims in a token payload. Args: payload (Mapping[str, str]): The JWT payload. Raises: ValueError: if any checks failed. """ now = _helpers.datetime_to_secs(_helpers.utcnow()) # Make sure the iat and exp claims are present. for key in ('iat', 'exp'): if key not in payload: raise ValueError( 'Token does not contain required claim {}'.format(key)) # Make sure the token wasn't issued in the future. iat = payload['iat'] # Err on the side of accepting a token that is slightly early to account # for clock skew. earliest = iat - _helpers.CLOCK_SKEW_SECS if now < earliest: raise ValueError('Token used too early, {} < {}'.format(now, iat)) # Make sure the token wasn't issued in the past. exp = payload['exp'] # Err on the side of accepting a token that is slightly out of date # to account for clow skew. latest = exp + _helpers.CLOCK_SKEW_SECS if latest < now: raise ValueError('Token expired, {} < {}'.format(latest, now))
def refresh(self, request): # pylint: disable=unused-argument token, ttl = app_identity.get_access_token(self._scopes, self._service_account_id) expiry = _helpers.utcnow() + datetime.timedelta(seconds=ttl) self.token, self.expiry = token, expiry
def test_refresh_success(self, unused_utcnow, refresh_grant): token = 'token' expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) grant_response = {'id_token': mock.sentinel.id_token} refresh_grant.return_value = ( # Access token token, # New refresh token None, # Expiry, expiry, # Extra data grant_response) request = mock.create_autospec(transport.Request) credentials = self.make_credentials() # Refresh credentials credentials.refresh(request) # Check jwt grant call. refresh_grant.assert_called_with(request, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET, None) # Check that the credentials have the token and expiry assert credentials.token == token assert credentials.expiry == expiry assert credentials.id_token == mock.sentinel.id_token # Check that the credentials are valid (have a token and are not # expired) assert credentials.valid
def test_refresh_success(self, id_token_jwt_grant): credentials = self.make_credentials() token = "token" id_token_jwt_grant.return_value = ( token, _helpers.utcnow() + datetime.timedelta(seconds=500), {}, ) request = mock.create_autospec(transport.Request, instance=True) # Refresh credentials credentials.refresh(request) # Check jwt grant call. assert id_token_jwt_grant.called called_request, token_uri, assertion = id_token_jwt_grant.call_args[0] assert called_request == request assert token_uri == credentials._token_uri assert jwt.decode(assertion, PUBLIC_CERT_BYTES) # No further assertion done on the token, as there are separate tests # for checking the authorization grant assertion. # Check that the credentials have the token. assert credentials.token == token # Check that the credentials are valid (have a token and are not # expired) assert credentials.valid
def get_service_account_token(request, service_account="default", scopes=None): """Get the OAuth 2.0 access token for a service account. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. service_account (str): The string 'default' or a service account email address. The determines which service account for which to acquire an access token. scopes (Optional[Union[str, List[str]]]): Optional string or list of strings with auth scopes. Returns: Union[str, datetime]: The access token and its expiration. Raises: google.auth.exceptions.TransportError: if an error occurred while retrieving metadata. """ if scopes: if not isinstance(scopes, str): scopes = ",".join(scopes) params = {"scopes": scopes} else: params = None path = "instance/service-accounts/{0}/token".format(service_account) token_json = get(request, path, params=params) token_expiry = _helpers.utcnow() + datetime.timedelta( seconds=token_json["expires_in"] ) return token_json["access_token"], token_expiry
async def test_credentials_with_scopes_refresh_failure_raises_refresh_error( self, unused_utcnow, refresh_grant): scopes = ["email", "profile"] scopes_returned = ["email"] token = "token" expiry = _helpers.utcnow() + datetime.timedelta(seconds=500) grant_response = { "id_token": mock.sentinel.id_token, "scope": " ".join(scopes_returned), } rapt_token = "rapt_token" refresh_grant.return_value = ( # Access token token, # New refresh token None, # Expiry, expiry, # Extra data grant_response, # Rapt token rapt_token, ) request = mock.AsyncMock(spec=["transport.Request"]) creds = _credentials_async.Credentials( token=None, refresh_token=self.REFRESH_TOKEN, token_uri=self.TOKEN_URI, client_id=self.CLIENT_ID, client_secret=self.CLIENT_SECRET, scopes=scopes, rapt_token=None, ) # Refresh credentials with pytest.raises(exceptions.RefreshError, match="Not all requested scopes were granted"): await creds.refresh(request) # Check jwt grant call. refresh_grant.assert_called_with( request, self.TOKEN_URI, self.REFRESH_TOKEN, self.CLIENT_ID, self.CLIENT_SECRET, scopes, None, ) # Check that the credentials have the token and expiry assert creds.token == token assert creds.expiry == expiry assert creds.id_token == mock.sentinel.id_token assert creds.has_scopes(scopes) # Check that the credentials are valid (have a token and are not # expired.) assert creds.valid
def test_apply_impersonation_with_quota_project_id(self): expire_time = ( _helpers.utcnow().replace(microsecond=0) + datetime.timedelta(seconds=3600) ).isoformat("T") + "Z" # Service account impersonation response. impersonation_response = { "accessToken": "SA_ACCESS_TOKEN", "expireTime": expire_time, } # Initialize mock request to handle token exchange and service account # impersonation request. request = self.make_mock_request( status=http_client.OK, data=self.SUCCESS_RESPONSE.copy(), impersonation_status=http_client.OK, impersonation_data=impersonation_response, ) # Initialize credentials with service account impersonation. credentials = self.make_credentials( service_account_impersonation_url=self.SERVICE_ACCOUNT_IMPERSONATION_URL, scopes=self.SCOPES, quota_project_id=self.QUOTA_PROJECT_ID, ) headers = {"other": "header-value"} credentials.refresh(request) credentials.apply(headers) assert headers == { "other": "header-value", "authorization": "Bearer {}".format(impersonation_response["accessToken"]), "x-goog-user-project": self.QUOTA_PROJECT_ID, }
def _make_authorization_grant_assertion(self): """Create the OAuth 2.0 assertion. This assertion is used during the OAuth 2.0 grant to acquire an ID token. Returns: bytes: The authorization grant assertion. """ now = _helpers.utcnow() lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS) expiry = now + lifetime payload = { "iat": _helpers.datetime_to_secs(now), "exp": _helpers.datetime_to_secs(expiry), # The issuer must be the service account email. "iss": self.service_account_email, # The audience must be the auth token endpoint's URI "aud": self._token_uri, # The target audience specifies which service the ID token is # intended for. "target_audience": self._target_audience, } payload.update(self._additional_claims) token = jwt.encode(self._signer, payload) return token
def _verify_iat_and_exp(payload): """Verifies the ``iat`` (Issued At) and ``exp`` (Expires) claims in a token payload. Args: payload (Mapping[str, str]): The JWT payload. Raises: ValueError: if any checks failed. """ now = _helpers.datetime_to_secs(_helpers.utcnow()) # Make sure the iat and exp claims are present. for key in ("iat", "exp"): if key not in payload: raise ValueError( "Token does not contain required claim {}".format(key)) # Make sure the token wasn't issued in the future. iat = payload["iat"] # Err on the side of accepting a token that is slightly early to account # for clock skew. earliest = iat - _helpers.CLOCK_SKEW_SECS if now < earliest: raise ValueError("Token used too early, {} < {}".format(now, iat)) # Make sure the token wasn't issued in the past. exp = payload["exp"] # Err on the side of accepting a token that is slightly out of date # to account for clow skew. latest = exp + _helpers.CLOCK_SKEW_SECS if latest < now: raise ValueError("Token expired, {} < {}".format(latest, now))
def expired(self): """Checks if the credentials are expired. Note that credentials can be invalid but not expired becaue Credentials with :attr:`expiry` set to None is considered to never expire. """ if not self.expiry: return False # Remove 5 minutes from expiry to err on the side of reporting # expiration early so that we avoid the 401-refresh-retry loop. skewed_expiry = self.expiry - _helpers.CLOCK_SKEW return _helpers.utcnow() >= skewed_expiry
def _parse_expiry(response_data): """Parses the expiry field from a response into a datetime. Args: response_data (Mapping): The JSON-parsed response data. Returns: Optional[datetime]: The expiration or ``None`` if no expiration was specified. """ expires_in = response_data.get('expires_in', None) if expires_in is not None: return _helpers.utcnow() + datetime.timedelta( seconds=expires_in) else: return None
def _get_jwt_for_audience(self, audience): """Get a JWT For a given audience. If there is already an existing, non-expired token in the cache for the audience, that token is used. Otherwise, a new token will be created. Args: audience (str): The intended audience. Returns: bytes: The encoded JWT. """ token, expiry = self._cache.get(audience, (None, None)) if token is None or expiry < _helpers.utcnow(): token, expiry = self._make_jwt_for_audience(audience) self._cache[audience] = token, expiry return token
def get_service_account_token(request, service_account='default'): """Get the OAuth 2.0 access token for a service account. Args: request (google.auth.transport.Request): A callable used to make HTTP requests. service_account (str): The string 'default' or a service account email address. The determines which service account for which to acquire an access token. Returns: Union[str, datetime]: The access token and its expiration. Raises: google.auth.exceptions.TransportError: if an error occurred while retrieving metadata. """ token_json = get( request, 'instance/service-accounts/{0}/token'.format(service_account)) token_expiry = _helpers.utcnow() + datetime.timedelta( seconds=token_json['expires_in']) return token_json['access_token'], token_expiry
def expired(self): return _helpers.utcnow() >= self.expiry