Opened the same connection multiple times with client_store_temporary_credential=True and authenticator="externalbrowser", which then prompted multiple times for authentication.
Code to reproduce (note that this requires some environment variables):
import os
# need to set these up in advance
account = os.environ["SNOWFLAKE_ACCOUNT"]
user = os.environ["SNOWFLAKE_USER"]
role = os.environ["SNOWFLAKE_ROLE"]
warehouse = os.environ["SNOWFLAKE_WAREHOUSE"]
database = os.environ["SNOWFLAKE_DATABASE"]
config = {"account": account, "user": user, "role": role, "warehouse": warehouse, "database": database, "authenticator": "externalbrowser", "client_store_temporary_credential": True}
from snowflake.sqlalchemy import URL
url = URL(**config)
import sqlalchemy as sa
engine = sa.create_engine(url)
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(threadName)s %(filename)s:%(lineno)d - %(funcName)s() - %(levelname)s - %(message)s')
# prompts for auth
engine.connect()
# prompts for auth.. again?
engine.connect()
What did you expect to see?
I expected the credentials to be cached and to not be asked for authentication the second time.
Can you set logging to DEBUG and collect the logs?
First connect (second connect is basically the same):
Failing to write to the cache because the credential wasn't loaded:
2023-08-14 13:16:56,789 - MainThread _auth.py:516 - _write_temporary_credential() - DEBUG - no credential is given when try to store temporary credential
Root cause
This problem is caused by incorrect boolean handling in this part of the SnowflakeDialect:
https://github.com/snowflakedb/snowflake-sqlalchemy/blob/de68d864af14200643b74625db4480db87c92172/src/snowflake/sqlalchemy/snowdialect.py#L202-L233
Line 229 updates the properties based on the query parameters, but the query parameters at that point are converted by the URL class into strings, so they get set as strings as well. By adding this bit of hacky code after line 229, I got the credential caching working, but this would affect all boolean parameters so a more generic solution should be made:
opts["client_store_temporary_credential"] = (
opts.get("client_store_temporary_credential") == "True" or False
)
Please answer these questions before submitting your issue. Thanks!
What version of Python are you using?
Python 3.8.16 (default, Feb 6 2023, 12:05:53) [GCC 12.2.1 20230111]
What operating system and processor architecture are you using?
Linux-6.1.38-1-MANJARO-x86_64-with-glibc2.34
What are the component versions in the environment (
pip freeze
)?Using latest master version (de68d86) of this repository with it's defined dependencies:
What did you do?
Opened the same connection multiple times with
client_store_temporary_credential=True
andauthenticator="externalbrowser"
, which then prompted multiple times for authentication.Code to reproduce (note that this requires some environment variables):
What did you expect to see?
I expected the credentials to be cached and to not be asked for authentication the second time.
Can you set logging to DEBUG and collect the logs?
First connect (second connect is basically the same):
Notable log messages:
True
rather than a boolean:Root cause
This problem is caused by incorrect boolean handling in this part of the SnowflakeDialect: https://github.com/snowflakedb/snowflake-sqlalchemy/blob/de68d864af14200643b74625db4480db87c92172/src/snowflake/sqlalchemy/snowdialect.py#L202-L233 Line 229 updates the properties based on the query parameters, but the query parameters at that point are converted by the URL class into strings, so they get set as strings as well. By adding this bit of hacky code after line 229, I got the credential caching working, but this would affect all boolean parameters so a more generic solution should be made: