Skip to content

Commit 3d0d783

Browse files
SNOW-721779 fix MFA token cache logic (#1391)
1 parent a313240 commit 3d0d783

File tree

4 files changed

+64
-4
lines changed

4 files changed

+64
-4
lines changed

src/snowflake/connector/auth/_auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ def post_request_wrapper(self, url, headers, body):
450450
self._rest._connection._update_parameters(session_parameters)
451451
return session_parameters
452452

453-
def _read_temporary_credential(self, host, user, cred_type):
453+
def _read_temporary_credential(self, host, user, cred_type) -> str | None:
454454
cred = None
455455
if IS_MACOS or IS_WINDOWS:
456456
if not installed_keyring:

src/snowflake/connector/connection.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,10 +808,19 @@ def __open_connection(self):
808808
elif self._authenticator == OAUTH_AUTHENTICATOR:
809809
self.auth_class = AuthByOAuth(oauth_token=self._token)
810810
elif self._authenticator == USR_PWD_MFA_AUTHENTICATOR:
811-
self.auth_class = AuthByUsrPwdMfa(password=self._password)
812811
self._session_parameters[PARAMETER_CLIENT_REQUEST_MFA_TOKEN] = (
813812
self._client_request_mfa_token if IS_LINUX else True
814813
)
814+
if self._session_parameters[PARAMETER_CLIENT_REQUEST_MFA_TOKEN]:
815+
auth.read_temporary_credentials(
816+
self.host,
817+
self.user,
818+
self._session_parameters,
819+
)
820+
self.auth_class = AuthByUsrPwdMfa(
821+
password=self._password,
822+
mfa_token=self.rest.mfa_token,
823+
)
815824
else:
816825
# okta URL, e.g., https://<account>.okta.com/
817826
self.auth_class = AuthByOkta(application=self.application)

src/snowflake/connector/network.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,11 +403,11 @@ def id_token(self, value):
403403
self._id_token = value
404404

405405
@property
406-
def mfa_token(self):
406+
def mfa_token(self) -> str | None:
407407
return getattr(self, "_mfa_token", None)
408408

409409
@mfa_token.setter
410-
def mfa_token(self, value):
410+
def mfa_token(self, value: str) -> None:
411411
self._mfa_token = value
412412

413413
def close(self):

test/unit/test_auth_mfa.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#
2+
# Copyright (c) 2012-2021 Snowflake Computing Inc. All rights reserved.
3+
#
4+
5+
from unittest import mock
6+
7+
from snowflake.connector import connect
8+
9+
10+
def test_mfa_token_cache():
11+
with mock.patch(
12+
"snowflake.connector.network.SnowflakeRestful.fetch",
13+
):
14+
with mock.patch(
15+
"snowflake.connector.auth._auth.Auth._write_temporary_credential",
16+
) as save_mock:
17+
with connect(
18+
account="account",
19+
user="user",
20+
password="password",
21+
authenticator="username_password_mfa",
22+
client_store_temporary_credential=True,
23+
client_request_mfa_token=True,
24+
):
25+
assert save_mock.called
26+
with mock.patch(
27+
"snowflake.connector.network.SnowflakeRestful.fetch",
28+
return_value={
29+
"data": {
30+
"token": "abcd",
31+
"masterToken": "defg",
32+
},
33+
"success": True,
34+
},
35+
):
36+
with mock.patch(
37+
"snowflake.connector.cursor.SnowflakeCursor._init_result_and_meta",
38+
):
39+
with mock.patch(
40+
"snowflake.connector.auth._auth.Auth._read_temporary_credential",
41+
return_value=None,
42+
) as load_mock:
43+
with connect(
44+
account="account",
45+
user="user",
46+
password="password",
47+
authenticator="username_password_mfa",
48+
client_store_temporary_credential=True,
49+
client_request_mfa_token=True,
50+
):
51+
assert load_mock.called

0 commit comments

Comments
 (0)