snowflakedb / snowflakedb/snowflake-connector-python

SNOW-4036712: OAuth Authorization Code flow replaces custom public PKCE client_id with LOCAL_APPLICATION

Open
#3,011 2 comments 0 reactions 1 assignee View on GitHub

@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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.