snowflake-sql-api uses Snowflake's keypair (JWT) authentication. Each request
carries a short-lived RS256 JWT signed by your RSA private key; Snowflake
verifies it against the public key registered on your user. There is no password
and no browser SSO flow.
Unencrypted private key (simplest):
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pubPassphrase-encrypted private key (recommended for shared environments):
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pubKeep rsa_key.p8 secret. Never commit it: the project's .gitignore already
excludes *.p8, *.pem, and *private_key*, and a pre-commit detect-private-key
hook is a second line of defence.
Take the body of rsa_key.pub (everything between the BEGIN/END lines, with
the newlines removed) and set it on the user. Run this as a role with the
privilege to alter the user (e.g. SECURITYADMIN):
ALTER USER my_user SET RSA_PUBLIC_KEY='MIIBIjANBgkqh...rest of the public key...';Verify it took:
DESCRIBE USER my_user; -- look for RSA_PUBLIC_KEY_FP (the fingerprint)The client computes the same SHA256:<base64> fingerprint from your private key
and puts it in the JWT issuer claim, so this fingerprint must match.
From a key file:
from snowflake_sql_api import SnowflakeClient
client = SnowflakeClient(
account="myorg-myaccount",
user="MY_USER",
private_key_path="/path/to/rsa_key.p8",
)From in-memory PEM bytes (e.g. fetched from a secrets manager):
client = SnowflakeClient(
account="myorg-myaccount",
user="MY_USER",
private_key=pem_bytes, # bytes
private_key_passphrase="my-passphrase", # only if the key is encrypted
)Or entirely from the environment (see getting-started.md for the variable list):
client = SnowflakeClient.from_env()This is the single most common keypair failure. Two account forms are derived differently, and the client handles both for you:
- The JWT claim account (issuer/subject) must drop any region/cloud suffix
and be uppercased:
xy12345.ap-southeast-2becomesXY12345. Leaving the region in makes the JWT invalid. - The API host keeps the full account (the region routes the request):
xy12345.ap-southeast-2becomesxy12345.ap-southeast-2.snowflakecomputing.com.
You pass the full account locator (whatever Snowflake shows you, region included)
and the library splits it correctly. The org-account dash form
(myorg-myaccount) has no dot, so it is preserved as-is.
If your account uses PrivateLink or a non-standard host, pass host= explicitly
to bypass host derivation (the claim account is still derived from account).
Tokens are signed with a one-hour lifetime (Snowflake's cap) and cached, with a small renewal margin so an in-flight request never races expiry. You do not need to manage tokens yourself.
A SnowflakeAuthError (HTTP 401) almost always means one of:
- the public key is not registered, or does not match the private key;
- the account locator carried a region into the claim (not possible via this client unless you bypass it);
- significant clock skew between your host and Snowflake;
- an encrypted key supplied without (or with the wrong) passphrase, which raises
a
SnowflakeConfigErrorbefore any request.
See troubleshooting.md for more.