Skip to content
This repository has been archived by the owner on Nov 5, 2019. It is now read-only.

Commit

Permalink
Support token_uri and revoke_uri in ServiceAccountCredentials (#510)
Browse files Browse the repository at this point in the history
This change adds support for custom authorization token
and revocation URIs in the various ServiceAccountCredentials
factory methods.
  • Loading branch information
thobrla authored and Jon Wayne Parrott committed Jun 7, 2016
1 parent f5ae963 commit b557440
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 64 deletions.
105 changes: 89 additions & 16 deletions oauth2client/service_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ class ServiceAccountCredentials(AssertionCredentials):
service account.
user_agent: string, (Optional) User agent to use when sending
request.
token_uri: string, URI for token endpoint. For convenience defaults
to Google's endpoints but any OAuth 2.0 provider can be
used.
revoke_uri: string, URI for revoke endpoint. For convenience defaults
to Google's endpoints but any OAuth 2.0 provider can be
used.
kwargs: dict, Extra key-value pairs (both strings) to send in the
payload body when making an assertion.
"""
Expand All @@ -106,10 +112,13 @@ def __init__(self,
private_key_id=None,
client_id=None,
user_agent=None,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
**kwargs):

super(ServiceAccountCredentials, self).__init__(
None, user_agent=user_agent)
None, user_agent=user_agent, token_uri=token_uri,
revoke_uri=revoke_uri)

self._service_account_email = service_account_email
self._signer = signer
Expand Down Expand Up @@ -145,14 +154,21 @@ def _to_json(self, strip, to_serialize=None):
strip, to_serialize=to_serialize)

@classmethod
def _from_parsed_json_keyfile(cls, keyfile_dict, scopes):
def _from_parsed_json_keyfile(cls, keyfile_dict, scopes,
token_uri=None, revoke_uri=None):
"""Helper for factory constructors from JSON keyfile.
Args:
keyfile_dict: dict-like object, The parsed dictionary-like object
containing the contents of the JSON keyfile.
scopes: List or string, Scopes to use when acquiring an
access token.
token_uri: string, URI for OAuth 2.0 provider token endpoint.
If unset and not present in keyfile_dict, defaults
to Google's endpoints.
revoke_uri: string, URI for OAuth 2.0 provider revoke endpoint.
If unset and not present in keyfile_dict, defaults
to Google's endpoints.
Returns:
ServiceAccountCredentials, a credentials object created from
Expand All @@ -172,22 +188,35 @@ def _from_parsed_json_keyfile(cls, keyfile_dict, scopes):
private_key_pkcs8_pem = keyfile_dict['private_key']
private_key_id = keyfile_dict['private_key_id']
client_id = keyfile_dict['client_id']
if not token_uri:
token_uri = keyfile_dict.get('token_uri', GOOGLE_TOKEN_URI)
if not revoke_uri:
revoke_uri = keyfile_dict.get('revoke_uri', GOOGLE_REVOKE_URI)

signer = crypt.Signer.from_string(private_key_pkcs8_pem)
credentials = cls(service_account_email, signer, scopes=scopes,
private_key_id=private_key_id,
client_id=client_id)
client_id=client_id, token_uri=token_uri,
revoke_uri=revoke_uri)
credentials._private_key_pkcs8_pem = private_key_pkcs8_pem
return credentials

@classmethod
def from_json_keyfile_name(cls, filename, scopes=''):
def from_json_keyfile_name(cls, filename, scopes='',
token_uri=None, revoke_uri=None):

"""Factory constructor from JSON keyfile by name.
Args:
filename: string, The location of the keyfile.
scopes: List or string, (Optional) Scopes to use when acquiring an
access token.
token_uri: string, URI for OAuth 2.0 provider token endpoint.
If unset and not present in the key file, defaults
to Google's endpoints.
revoke_uri: string, URI for OAuth 2.0 provider revoke endpoint.
If unset and not present in the key file, defaults
to Google's endpoints.
Returns:
ServiceAccountCredentials, a credentials object created from
Expand All @@ -200,17 +229,26 @@ def from_json_keyfile_name(cls, filename, scopes=''):
"""
with open(filename, 'r') as file_obj:
client_credentials = json.load(file_obj)
return cls._from_parsed_json_keyfile(client_credentials, scopes)
return cls._from_parsed_json_keyfile(client_credentials, scopes,
token_uri=token_uri,
revoke_uri=revoke_uri)

@classmethod
def from_json_keyfile_dict(cls, keyfile_dict, scopes=''):
def from_json_keyfile_dict(cls, keyfile_dict, scopes='',
token_uri=None, revoke_uri=None):
"""Factory constructor from parsed JSON keyfile.
Args:
keyfile_dict: dict-like object, The parsed dictionary-like object
containing the contents of the JSON keyfile.
scopes: List or string, (Optional) Scopes to use when acquiring an
access token.
token_uri: string, URI for OAuth 2.0 provider token endpoint.
If unset and not present in keyfile_dict, defaults
to Google's endpoints.
revoke_uri: string, URI for OAuth 2.0 provider revoke endpoint.
If unset and not present in keyfile_dict, defaults
to Google's endpoints.
Returns:
ServiceAccountCredentials, a credentials object created from
Expand All @@ -221,12 +259,16 @@ def from_json_keyfile_dict(cls, keyfile_dict, scopes=''):
KeyError, if one of the expected keys is not present in
the keyfile.
"""
return cls._from_parsed_json_keyfile(keyfile_dict, scopes)
return cls._from_parsed_json_keyfile(keyfile_dict, scopes,
token_uri=token_uri,
revoke_uri=revoke_uri)

@classmethod
def _from_p12_keyfile_contents(cls, service_account_email,
private_key_pkcs12,
private_key_password=None, scopes=''):
private_key_password=None, scopes='',
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI):
"""Factory constructor from JSON keyfile.
Args:
Expand All @@ -237,6 +279,12 @@ def _from_p12_keyfile_contents(cls, service_account_email,
private key. Defaults to ``notasecret``.
scopes: List or string, (Optional) Scopes to use when acquiring an
access token.
token_uri: string, URI for token endpoint. For convenience defaults
to Google's endpoints but any OAuth 2.0 provider can be
used.
revoke_uri: string, URI for revoke endpoint. For convenience
defaults to Google's endpoints but any OAuth 2.0
provider can be used.
Returns:
ServiceAccountCredentials, a credentials object created from
Expand All @@ -252,14 +300,18 @@ def _from_p12_keyfile_contents(cls, service_account_email,
raise NotImplementedError(_PKCS12_ERROR)
signer = crypt.Signer.from_string(private_key_pkcs12,
private_key_password)
credentials = cls(service_account_email, signer, scopes=scopes)
credentials = cls(service_account_email, signer, scopes=scopes,
token_uri=token_uri, revoke_uri=revoke_uri)
credentials._private_key_pkcs12 = private_key_pkcs12
credentials._private_key_password = private_key_password
return credentials

@classmethod
def from_p12_keyfile(cls, service_account_email, filename,
private_key_password=None, scopes=''):
private_key_password=None, scopes='',
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI):

"""Factory constructor from JSON keyfile.
Args:
Expand All @@ -270,6 +322,12 @@ def from_p12_keyfile(cls, service_account_email, filename,
private key. Defaults to ``notasecret``.
scopes: List or string, (Optional) Scopes to use when acquiring an
access token.
token_uri: string, URI for token endpoint. For convenience defaults
to Google's endpoints but any OAuth 2.0 provider can be
used.
revoke_uri: string, URI for revoke endpoint. For convenience
defaults to Google's endpoints but any OAuth 2.0
provider can be used.
Returns:
ServiceAccountCredentials, a credentials object created from
Expand All @@ -283,11 +341,14 @@ def from_p12_keyfile(cls, service_account_email, filename,
private_key_pkcs12 = file_obj.read()
return cls._from_p12_keyfile_contents(
service_account_email, private_key_pkcs12,
private_key_password=private_key_password, scopes=scopes)
private_key_password=private_key_password, scopes=scopes,
token_uri=token_uri, revoke_uri=revoke_uri)

@classmethod
def from_p12_keyfile_buffer(cls, service_account_email, file_buffer,
private_key_password=None, scopes=''):
private_key_password=None, scopes='',
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI):
"""Factory constructor from JSON keyfile.
Args:
Expand All @@ -299,6 +360,12 @@ def from_p12_keyfile_buffer(cls, service_account_email, file_buffer,
private key. Defaults to ``notasecret``.
scopes: List or string, (Optional) Scopes to use when acquiring an
access token.
token_uri: string, URI for token endpoint. For convenience defaults
to Google's endpoints but any OAuth 2.0 provider can be
used.
revoke_uri: string, URI for revoke endpoint. For convenience
defaults to Google's endpoints but any OAuth 2.0
provider can be used.
Returns:
ServiceAccountCredentials, a credentials object created from
Expand All @@ -311,7 +378,8 @@ def from_p12_keyfile_buffer(cls, service_account_email, file_buffer,
private_key_pkcs12 = file_buffer.read()
return cls._from_p12_keyfile_contents(
service_account_email, private_key_pkcs12,
private_key_password=private_key_password, scopes=scopes)
private_key_password=private_key_password, scopes=scopes,
token_uri=token_uri, revoke_uri=revoke_uri)

def _generate_assertion(self):
"""Generate the assertion that will be used in the request."""
Expand Down Expand Up @@ -508,6 +576,8 @@ def __init__(self,
private_key_id=None,
client_id=None,
user_agent=None,
token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI,
additional_claims=None):
if additional_claims is None:
additional_claims = {}
Expand All @@ -517,6 +587,8 @@ def __init__(self,
private_key_id=private_key_id,
client_id=client_id,
user_agent=user_agent,
token_uri=token_uri,
revoke_uri=revoke_uri,
**additional_claims)

def authorize(self, http):
Expand Down Expand Up @@ -595,17 +667,18 @@ def create_scoped_required(self):
# JWTAccessCredentials are unscoped by definition
return True

def create_scoped(self, scopes):
def create_scoped(self, scopes, token_uri=GOOGLE_TOKEN_URI,
revoke_uri=GOOGLE_REVOKE_URI):
# Returns an OAuth2 credentials with the given scope
result = ServiceAccountCredentials(self._service_account_email,
self._signer,
scopes=scopes,
private_key_id=self._private_key_id,
client_id=self.client_id,
user_agent=self._user_agent,
token_uri=token_uri,
revoke_uri=revoke_uri,
**self._kwargs)
result.token_uri = self.token_uri
result.revoke_uri = self.revoke_uri
if self._private_key_pkcs8_pem is not None:
result._private_key_pkcs8_pem = self._private_key_pkcs8_pem
if self._private_key_pkcs12 is not None:
Expand Down
Loading

0 comments on commit b557440

Please sign in to comment.