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()
Example #2
0
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
Example #7
0
    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
Example #8
0
    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
Example #9
0
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
Example #12
0
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
Example #14
0
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()
Example #16
0
    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
Example #17
0
 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
Example #18
0
    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
Example #19
0
    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
Example #21
0
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 __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()
Example #23
0
    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
Example #24
0
    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
Example #25
0
    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
Example #26
0
    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
Example #27
0
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))
Example #28
0
    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
Example #29
0
    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
Example #30
0
    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
    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
Example #32
0
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
Example #34
0
    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
Example #36
0
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
Example #38
0
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
Example #39
0
    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
Example #40
0
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