Skip to content

Commit 5f37bf9

Browse files
hiranya911Jon Wayne Parrott
authored andcommitted
Add google.oauth2.credentials.Credentials.from_authorized_user_file (#226)
1 parent 48564b3 commit 5f37bf9

File tree

4 files changed

+115
-19
lines changed

4 files changed

+115
-19
lines changed

packages/google-auth/google/auth/_cloud_sdk.py

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@
1818
import os
1919
import subprocess
2020

21-
import six
22-
2321
from google.auth import environment_vars
2422
import google.oauth2.credentials
2523

26-
# The Google OAuth 2.0 token endpoint. Used for authorized user credentials.
27-
_GOOGLE_OAUTH2_TOKEN_ENDPOINT = 'https://accounts.google.com/o/oauth2/token'
2824

2925
# The ~/.config subdirectory containing gcloud credentials.
3026
_CONFIG_DIRECTORY = 'gcloud'
@@ -94,20 +90,8 @@ def load_authorized_user_credentials(info):
9490
Raises:
9591
ValueError: if the info is in the wrong format or missing data.
9692
"""
97-
keys_needed = set(('refresh_token', 'client_id', 'client_secret'))
98-
missing = keys_needed.difference(six.iterkeys(info))
99-
100-
if missing:
101-
raise ValueError(
102-
'Authorized user info was not in the expected format, missing '
103-
'fields {}.'.format(', '.join(missing)))
104-
105-
return google.oauth2.credentials.Credentials(
106-
None, # No access token, must be refreshed.
107-
refresh_token=info['refresh_token'],
108-
token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT,
109-
client_id=info['client_id'],
110-
client_secret=info['client_secret'])
93+
return google.oauth2.credentials.Credentials.from_authorized_user_info(
94+
info)
11195

11296

11397
def get_project_id():

packages/google-auth/google/oauth2/credentials.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,20 @@
3131
.. _rfc6749 section 4.1: https://tools.ietf.org/html/rfc6749#section-4.1
3232
"""
3333

34+
import io
35+
import json
36+
37+
import six
38+
3439
from google.auth import _helpers
3540
from google.auth import credentials
3641
from google.oauth2 import _client
3742

3843

44+
# The Google OAuth 2.0 token endpoint. Used for authorized user credentials.
45+
_GOOGLE_OAUTH2_TOKEN_ENDPOINT = 'https://accounts.google.com/o/oauth2/token'
46+
47+
3948
class Credentials(credentials.ReadOnlyScoped, credentials.Credentials):
4049
"""Credentials using OAuth 2.0 access and refresh tokens."""
4150

@@ -120,3 +129,56 @@ def refresh(self, request):
120129
self.expiry = expiry
121130
self._refresh_token = refresh_token
122131
self._id_token = grant_response.get('id_token')
132+
133+
@classmethod
134+
def from_authorized_user_info(cls, info, scopes=None):
135+
"""Creates a Credentials instance from parsed authorized user info.
136+
137+
Args:
138+
info (Mapping[str, str]): The authorized user info in Google
139+
format.
140+
scopes (Sequence[str]): Optional list of scopes to include in the
141+
credentials.
142+
143+
Returns:
144+
google.oauth2.credentials.Credentials: The constructed
145+
credentials.
146+
147+
Raises:
148+
ValueError: If the info is not in the expected format.
149+
"""
150+
keys_needed = set(('refresh_token', 'client_id', 'client_secret'))
151+
missing = keys_needed.difference(six.iterkeys(info))
152+
153+
if missing:
154+
raise ValueError(
155+
'Authorized user info was not in the expected format, missing '
156+
'fields {}.'.format(', '.join(missing)))
157+
158+
return Credentials(
159+
None, # No access token, must be refreshed.
160+
refresh_token=info['refresh_token'],
161+
token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT,
162+
scopes=scopes,
163+
client_id=info['client_id'],
164+
client_secret=info['client_secret'])
165+
166+
@classmethod
167+
def from_authorized_user_file(cls, filename, scopes=None):
168+
"""Creates a Credentials instance from an authorized user json file.
169+
170+
Args:
171+
filename (str): The path to the authorized user json file.
172+
scopes (Sequence[str]): Optional list of scopes to include in the
173+
credentials.
174+
175+
Returns:
176+
google.oauth2.credentials.Credentials: The constructed
177+
credentials.
178+
179+
Raises:
180+
ValueError: If the file is not in the expected format.
181+
"""
182+
with io.open(filename, 'r', encoding='utf-8') as json_file:
183+
data = json.load(json_file)
184+
return cls.from_authorized_user_info(data, scopes)

packages/google-auth/tests/oauth2/test_credentials.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
# limitations under the License.
1414

1515
import datetime
16+
import json
17+
import os
1618

1719
import mock
1820

@@ -21,6 +23,14 @@
2123
from google.oauth2 import credentials
2224

2325

26+
DATA_DIR = os.path.join(os.path.dirname(__file__), '..', 'data')
27+
28+
AUTH_USER_JSON_FILE = os.path.join(DATA_DIR, 'authorized_user.json')
29+
30+
with open(AUTH_USER_JSON_FILE, 'r') as fh:
31+
AUTH_USER_INFO = json.load(fh)
32+
33+
2434
class TestCredentials(object):
2535
TOKEN_URI = 'https://example.com/oauth2/token'
2636
REFRESH_TOKEN = 'refresh_token'
@@ -84,3 +94,42 @@ def test_refresh_success(self, unused_utcnow, refresh_grant):
8494
# Check that the credentials are valid (have a token and are not
8595
# expired)
8696
assert credentials.valid
97+
98+
def test_from_authorized_user_info(self):
99+
info = AUTH_USER_INFO.copy()
100+
101+
creds = credentials.Credentials.from_authorized_user_info(info)
102+
assert creds.client_secret == info['client_secret']
103+
assert creds.client_id == info['client_id']
104+
assert creds.refresh_token == info['refresh_token']
105+
assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
106+
assert creds.scopes is None
107+
108+
scopes = ['email', 'profile']
109+
creds = credentials.Credentials.from_authorized_user_info(
110+
info, scopes)
111+
assert creds.client_secret == info['client_secret']
112+
assert creds.client_id == info['client_id']
113+
assert creds.refresh_token == info['refresh_token']
114+
assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
115+
assert creds.scopes == scopes
116+
117+
def test_from_authorized_user_file(self):
118+
info = AUTH_USER_INFO.copy()
119+
120+
creds = credentials.Credentials.from_authorized_user_file(
121+
AUTH_USER_JSON_FILE)
122+
assert creds.client_secret == info['client_secret']
123+
assert creds.client_id == info['client_id']
124+
assert creds.refresh_token == info['refresh_token']
125+
assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
126+
assert creds.scopes is None
127+
128+
scopes = ['email', 'profile']
129+
creds = credentials.Credentials.from_authorized_user_file(
130+
AUTH_USER_JSON_FILE, scopes)
131+
assert creds.client_secret == info['client_secret']
132+
assert creds.client_id == info['client_id']
133+
assert creds.refresh_token == info['refresh_token']
134+
assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
135+
assert creds.scopes == scopes

packages/google-auth/tests/test__cloud_sdk.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ def test_load_authorized_user_credentials():
145145
assert credentials._client_id == AUTHORIZED_USER_FILE_DATA['client_id']
146146
assert (credentials._client_secret ==
147147
AUTHORIZED_USER_FILE_DATA['client_secret'])
148-
assert credentials._token_uri == _cloud_sdk._GOOGLE_OAUTH2_TOKEN_ENDPOINT
148+
assert (credentials._token_uri ==
149+
google.oauth2.credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT)
149150

150151

151152
def test_load_authorized_user_credentials_bad_format():

0 commit comments

Comments
 (0)