snowflakedb / snowflakedb/snowflake-connector-python
SNOW-4036712: OAuth Authorization Code flow replaces custom public PKCE client_id with LOCAL_APPLICATION
Open
@sfc-gh-snow-drivers-warsaw-dl is already working on this.
Since Aug 31, 2026.
bug
status-triage_done
- Dominant language
- Python
- Stars
- 730
- Forks
- 574
- Avg merge
- 5h 45m
- Merged PRs (30d)
- 16
Description
Python version
Python 3.12.12 (main, Jan 27 2026, 23:41:44) [Clang 21.1.4]
Operating system and processor architecture
macOS-26.6.2-arm64-arm-64bit
Installed packages
annotated-doc==0.0.5
annotated-types==0.8.0
anyio==4.14.2
asn1crypto==1.5.1
attrs==26.1.0
boto3==1.43.79
botocore==1.43.79
certifi==2026.7.22
cffi==2.1.1
charset-normalizer==3.5.1
click==8.4.2
cryptography==50.0.1
filelock==3.32.4
h11==0.16.0
httpcore==1.0.9
httpcore2==2.12.0
httpx==0.28.1
httpx-sse==0.4.3
httpx2==2.12.0
idna==3.19
keyring==25.7.0
mcp==2.1.1
mcp-types==2.1.1
packaging==26.3
pydantic==2.13.4
pyjwt==2.13.0
pyopenssl==26.4.0
pytest==9.1.1
python-dateutil==2.9.0.post0
requests==2.34.2
snowflake-connector-python==4.7.2
typing-extensions==4.16.0
urllib3==2.7.0
What did you do?
I configured Authorization Code OAuth with PKCE using a Snowflake custom OAuth integration registered as a public client. Public clients have a client_id but no client_secret.
Minimal reproducer:
from snowflake.connector.auth.oauth_code import AuthByOauthCode
requested_client_id = "custom-public-client-id"
auth = AuthByOauthCode(
application="reproducer",
client_id=requested_client_id,
client_secret=None,
authentication_url=(
"https://example.snowflakecomputing.com/oauth/authorize"
),
token_request_url=(
"https://example.snowflakecomputing.com/oauth/token-request"
),
redirect_uri="http://127.0.0.1:8730/",
scope="session:role:TEST_ROLE",
host="example.snowflakecomputing.com",
)
print("requested client_id:", requested_client_id)
print("effective client_id:", auth._client_id)
print("effective client_secret:", auth._client_secret)
Output:
requested client_id: custom-public-client-id
effective client_id: LOCAL_APPLICATION
effective client_secret: LOCAL_APPLICATION
`AuthByOauthCode._eligible_for_default_client_credentials()` considers the configuration eligible for default credentials whenever `client_secret` is None, even when a nonempty custom `client_id` was supplied.
Using a dummy nonempty secret is not a valid workaround because the connector transmits it through HTTP Basic authentication.
I reproduced the same behavior in connector versions 3.18.0 through 4.7.2.
Using client_secret="" instead fails with:
What did you expect to see?
For an Authorization Code + PKCE public client, the connector should:
- Preserve the supplied custom client_id.
- Permit an absent client secret.
- Include client_id in the token request form.
- Omit the HTTP Basic Authorization header.
Instead, the connector silently replaces both values with LOCAL_APPLICATION. This authenticates against the wrong Snowflake OAuth integration and can also reuse tokens cached for that integration.
Can you set logging to DEBUG and collect the logs?
2026-08-28 15:59:01,506 DEBUG snowflake.connector.auth.oauth_code chose oauth state: **********************************************************
2026-08-28 15:59:01,506 DEBUG snowflake.connector.auth.oauth_code oauth pkce is going to be used
requested client_id: custom-public-client-id
effective client_id: LOCAL_APPLICATION
effective client_secret: LOCAL_APPLICATION
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.