Skip to content
This repository was archived by the owner on Mar 6, 2026. It is now read-only.

Commit 15d5fa9

Browse files
feat: make load_credentials_from_file a public method (#530)
* feat: make load_credentials_from_file public and alllow scopes * test: update tests * fix: raise error for json with no type * test: fix test names * refactor: simplify control flow * fix: raise coverage * test: update test Co-authored-by: arithmetic1728 <58957152+arithmetic1728@users.noreply.github.com> Co-authored-by: Sijun Liu <sijunliu@google.com>
1 parent f30b45a commit 15d5fa9

3 files changed

Lines changed: 65 additions & 26 deletions

File tree

google/auth/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616

1717
import logging
1818

19-
from google.auth._default import default
19+
from google.auth._default import default, load_credentials_from_file
2020

2121

22-
__all__ = ["default"]
22+
__all__ = ["default", "load_credentials_from_file"]
2323

2424

2525
# Set default logging handler to avoid "No handler found" warnings.

google/auth/_default.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,17 @@ def _warn_about_problematic_credentials(credentials):
6969
warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
7070

7171

72-
def _load_credentials_from_file(filename):
73-
"""Loads credentials from a file.
72+
def load_credentials_from_file(filename, scopes=None):
73+
"""Loads Google credentials from a file.
7474
7575
The credentials file must be a service account key or stored authorized
7676
user credentials.
7777
7878
Args:
7979
filename (str): The full path to the credentials file.
80+
scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
81+
specified, the credentials will automatically be scoped if
82+
necessary.
8083
8184
Returns:
8285
Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
@@ -109,7 +112,9 @@ def _load_credentials_from_file(filename):
109112
from google.oauth2 import credentials
110113

111114
try:
112-
credentials = credentials.Credentials.from_authorized_user_info(info)
115+
credentials = credentials.Credentials.from_authorized_user_info(
116+
info, scopes=scopes
117+
)
113118
except ValueError as caught_exc:
114119
msg = "Failed to load authorized user credentials from {}".format(filename)
115120
new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
@@ -122,7 +127,9 @@ def _load_credentials_from_file(filename):
122127
from google.oauth2 import service_account
123128

124129
try:
125-
credentials = service_account.Credentials.from_service_account_info(info)
130+
credentials = service_account.Credentials.from_service_account_info(
131+
info, scopes=scopes
132+
)
126133
except ValueError as caught_exc:
127134
msg = "Failed to load service account credentials from {}".format(filename)
128135
new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
@@ -148,7 +155,7 @@ def _get_gcloud_sdk_credentials():
148155
if not os.path.isfile(credentials_filename):
149156
return None, None
150157

151-
credentials, project_id = _load_credentials_from_file(credentials_filename)
158+
credentials, project_id = load_credentials_from_file(credentials_filename)
152159

153160
if not project_id:
154161
project_id = _cloud_sdk.get_project_id()
@@ -162,7 +169,7 @@ def _get_explicit_environ_credentials():
162169
explicit_file = os.environ.get(environment_vars.CREDENTIALS)
163170

164171
if explicit_file is not None:
165-
credentials, project_id = _load_credentials_from_file(
172+
credentials, project_id = load_credentials_from_file(
166173
os.environ[environment_vars.CREDENTIALS]
167174
)
168175

tests/test__default.py

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,88 +43,120 @@
4343

4444
SERVICE_ACCOUNT_FILE = os.path.join(DATA_DIR, "service_account.json")
4545

46+
CLIENT_SECRETS_FILE = os.path.join(DATA_DIR, "client_secrets.json")
47+
4648
with open(SERVICE_ACCOUNT_FILE) as fh:
4749
SERVICE_ACCOUNT_FILE_DATA = json.load(fh)
4850

4951
LOAD_FILE_PATCH = mock.patch(
50-
"google.auth._default._load_credentials_from_file",
52+
"google.auth._default.load_credentials_from_file",
5153
return_value=(mock.sentinel.credentials, mock.sentinel.project_id),
5254
autospec=True,
5355
)
5456

5557

56-
def test__load_credentials_from_missing_file():
58+
def test_load_credentials_from_missing_file():
5759
with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
58-
_default._load_credentials_from_file("")
60+
_default.load_credentials_from_file("")
5961

6062
assert excinfo.match(r"not found")
6163

6264

63-
def test__load_credentials_from_file_invalid_json(tmpdir):
65+
def test_load_credentials_from_file_invalid_json(tmpdir):
6466
jsonfile = tmpdir.join("invalid.json")
6567
jsonfile.write("{")
6668

6769
with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
68-
_default._load_credentials_from_file(str(jsonfile))
70+
_default.load_credentials_from_file(str(jsonfile))
6971

7072
assert excinfo.match(r"not a valid json file")
7173

7274

73-
def test__load_credentials_from_file_invalid_type(tmpdir):
75+
def test_load_credentials_from_file_invalid_type(tmpdir):
7476
jsonfile = tmpdir.join("invalid.json")
7577
jsonfile.write(json.dumps({"type": "not-a-real-type"}))
7678

7779
with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
78-
_default._load_credentials_from_file(str(jsonfile))
80+
_default.load_credentials_from_file(str(jsonfile))
7981

8082
assert excinfo.match(r"does not have a valid type")
8183

8284

83-
def test__load_credentials_from_file_authorized_user():
84-
credentials, project_id = _default._load_credentials_from_file(AUTHORIZED_USER_FILE)
85+
def test_load_credentials_from_file_authorized_user():
86+
credentials, project_id = _default.load_credentials_from_file(AUTHORIZED_USER_FILE)
8587
assert isinstance(credentials, google.oauth2.credentials.Credentials)
8688
assert project_id is None
8789

8890

89-
def test__load_credentials_from_file_authorized_user_bad_format(tmpdir):
91+
def test_load_credentials_from_file_no_type(tmpdir):
92+
# use the client_secrets.json, which is valid json but not a
93+
# loadable credentials type
94+
with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
95+
_default.load_credentials_from_file(CLIENT_SECRETS_FILE)
96+
97+
assert excinfo.match(r"does not have a valid type")
98+
assert excinfo.match(r"Type is None")
99+
100+
101+
def test_load_credentials_from_file_authorized_user_bad_format(tmpdir):
90102
filename = tmpdir.join("authorized_user_bad.json")
91103
filename.write(json.dumps({"type": "authorized_user"}))
92104

93105
with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
94-
_default._load_credentials_from_file(str(filename))
106+
_default.load_credentials_from_file(str(filename))
95107

96108
assert excinfo.match(r"Failed to load authorized user")
97109
assert excinfo.match(r"missing fields")
98110

99111

100-
def test__load_credentials_from_file_authorized_user_cloud_sdk():
112+
def test_load_credentials_from_file_authorized_user_cloud_sdk():
101113
with pytest.warns(UserWarning, match="Cloud SDK"):
102-
credentials, project_id = _default._load_credentials_from_file(
114+
credentials, project_id = _default.load_credentials_from_file(
103115
AUTHORIZED_USER_CLOUD_SDK_FILE
104116
)
105117
assert isinstance(credentials, google.oauth2.credentials.Credentials)
106118
assert project_id is None
107119

108120
# No warning if the json file has quota project id.
109-
credentials, project_id = _default._load_credentials_from_file(
121+
credentials, project_id = _default.load_credentials_from_file(
110122
AUTHORIZED_USER_CLOUD_SDK_WITH_QUOTA_PROJECT_ID_FILE
111123
)
112124
assert isinstance(credentials, google.oauth2.credentials.Credentials)
113125
assert project_id is None
114126

115127

116-
def test__load_credentials_from_file_service_account():
117-
credentials, project_id = _default._load_credentials_from_file(SERVICE_ACCOUNT_FILE)
128+
def test_load_credentials_from_file_authorized_user_cloud_sdk_with_scopes():
129+
with pytest.warns(UserWarning, match="Cloud SDK"):
130+
credentials, project_id = _default.load_credentials_from_file(
131+
AUTHORIZED_USER_CLOUD_SDK_FILE,
132+
scopes=["https://www.google.com/calendar/feeds"],
133+
)
134+
assert isinstance(credentials, google.oauth2.credentials.Credentials)
135+
assert project_id is None
136+
assert credentials.scopes == ["https://www.google.com/calendar/feeds"]
137+
138+
139+
def test_load_credentials_from_file_service_account():
140+
credentials, project_id = _default.load_credentials_from_file(SERVICE_ACCOUNT_FILE)
141+
assert isinstance(credentials, service_account.Credentials)
142+
assert project_id == SERVICE_ACCOUNT_FILE_DATA["project_id"]
143+
144+
145+
def test_load_credentials_from_file_service_account_with_scopes():
146+
credentials, project_id = _default.load_credentials_from_file(
147+
SERVICE_ACCOUNT_FILE, scopes=["https://www.google.com/calendar/feeds"]
148+
)
118149
assert isinstance(credentials, service_account.Credentials)
119150
assert project_id == SERVICE_ACCOUNT_FILE_DATA["project_id"]
151+
assert credentials.scopes == ["https://www.google.com/calendar/feeds"]
120152

121153

122-
def test__load_credentials_from_file_service_account_bad_format(tmpdir):
154+
def test_load_credentials_from_file_service_account_bad_format(tmpdir):
123155
filename = tmpdir.join("serivce_account_bad.json")
124156
filename.write(json.dumps({"type": "service_account"}))
125157

126158
with pytest.raises(exceptions.DefaultCredentialsError) as excinfo:
127-
_default._load_credentials_from_file(str(filename))
159+
_default.load_credentials_from_file(str(filename))
128160

129161
assert excinfo.match(r"Failed to load service account")
130162
assert excinfo.match(r"missing fields")

0 commit comments

Comments
 (0)