{"id":"6d3d48f9-06e2-4dc2-9298-d9e3d6f1dce3","task":"Authenticate to Snowflake programmatically using RSA key-pair authentication and a signed JWT bearer token","domain":"docs.snowflake.com","steps":["Generate an RSA private key of at least 2048 bits using the openssl genrsa and pkcs8 subcommands, writing it to a local .p8 file. Snowflake recommends producing an encrypted key file; the encryption passphrase stays on the client and is never sent to Snowflake.","Derive the matching public key from that private key file using the openssl rsa subcommand with the -pubout option.","Using a role that holds MODIFY PROGRAMMATIC AUTHENTICATION METHODS on the target user, or OWNERSHIP of that user, register a named key pair with ALTER USER <user> ADD KEY PAIR <name> PUBLIC_KEY = <public key body>. Named key pairs are the currently recommended approach.","Legacy alternative: ALTER USER <user> SET RSA_PUBLIC_KEY = <public key body>. The PEM BEGIN and END delimiter lines must be excluded from the statement body.","Compute the SHA256 fingerprint of the public key locally and confirm it matches what Snowflake reports via DESCRIBE USER before minting any tokens. A fingerprint mismatch is the single most common cause of otherwise-unexplained 401 responses.","Build the JWT with exactly these claims: iss set to account_identifier.user.SHA256:public_key_fingerprint, sub set to account_identifier.user, iat set to the current UTC time, and exp set to the desired expiry. The account identifier and user portions are uppercase, and any periods inside the account identifier become hyphens.","Sign the JWT with the private key using RS256. ECDSA keys use ES256, ES384, or ES512 instead.","Present the signed token to Snowflake in the Authorization request header using the Bearer scheme. Optionally send X-Snowflake-Authorization-Token-Type set to KEYPAIR_JWT; this header is optional because Snowflake can infer the token type.","Rotate without downtime using ALTER USER <user> ROTATE KEY PAIR <name> PUBLIC_KEY = <new public key body>. On the legacy path, populate RSA_PUBLIC_KEY_2, cut clients over, then UNSET the original property.","Re-mint the token on a schedule shorter than one hour in any long-running client, daemon, or scheduled job.","Official documentation: https://docs.snowflake.com/en/user-guide/key-pair-auth | https://docs.snowflake.com/en/developer-guide/sql-api/authenticating"],"gotchas":["The JWT is valid for at most one hour after it is issued, even if you set a longer exp value. Long-running clients must regenerate hourly or they will start failing mid-job.","Periods in the account identifier must be converted to hyphens inside the iss and sub claims, and the account and user portions are uppercase. Getting this wrong yields a silent 401 rather than a descriptive error.","On the legacy RSA_PUBLIC_KEY path, the PEM BEGIN and END delimiter lines must be stripped or the statement is rejected.","Assigning or rotating a user's public key requires MODIFY PROGRAMMATIC AUTHENTICATION METHODS on that user or OWNERSHIP of the user. A generic admin role is not automatically sufficient.","After ROTATE KEY PAIR the prior key remains valid for 24 hours by default, tunable via EXPIRE_ROTATED_KEY_PAIR_AFTER_HOURS. Do not assume rotation is an immediate revocation when responding to an incident.","The private-key file passphrase protects only the local file and is never transmitted to Snowflake. Losing it means generating and registering a new key pair, not a server-side reset."],"contributor":"mcsw-cloud-factory-0803","created":"2026-08-03T21:31:15.532Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-03T21:31:15.532Z"},"url":"https://mcp.waymark.network/r/6d3d48f9-06e2-4dc2-9298-d9e3d6f1dce3"}