Snowflake is removing single-factor passwords in three phases. In the last one, rolled out account by
account between August and October 2026, users that are not people can no longer sign in with a
password at all, and the old LEGACY_SERVICE users become SERVICE users. A dbt project that runs on
a schedule with a user and password stops working on the day the change reaches your account. Key-pair
authentication is the usual replacement.
Copy and paste
1. Generate the keys. The first command asks for a passphrase that protects the private key:
openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 aes-256-cbc -inform PEM -out rsa_key.p8
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
2. Copy the public key without its header and footer:
grep -v "PUBLIC KEY" rsa_key.pub | tr -d '\n'
3. Create the service user and attach the key (as a role that owns the user, for example
USERADMIN):
create user if not exists DBT_SERVICE
type = service
default_role = TRANSFORMER
default_warehouse = TRANSFORMING;
alter user DBT_SERVICE set rsa_public_key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC...';
grant role TRANSFORMER to user DBT_SERVICE;
4. Point profiles.yml at the private key:
shop:
target: prod
outputs:
prod:
type: snowflake
account: myorg-myaccount
user: DBT_SERVICE
role: TRANSFORMER
private_key_path: /secure/keys/rsa_key.p8
private_key_passphrase: "{{ env_var('DBT_ENV_SECRET_KEY_PASSPHRASE') }}"
database: ANALYTICS
warehouse: TRANSFORMING
schema: ANALYTICS
threads: 8
5. Test the connection:
export DBT_ENV_SECRET_KEY_PASSPHRASE='your passphrase'
dbt debug
The last line should read Connection test: [OK connection ok].
The key in a variable instead of a file
Where files are awkward, as in some CI systems, put the content of the private key in a secret and use
private_key instead of private_key_path:
private_key: "{{ env_var('DBT_ENV_SECRET_PRIVATE_KEY') }}"
private_key_passphrase: "{{ env_var('DBT_ENV_SECRET_KEY_PASSPHRASE') }}"
Use one or the other: with both, dbt stops with Cannot specify both private_key and private_key_path.
Variables whose name starts with DBT_ENV_SECRET_ are kept out of the logs; see
how to use environment variables in profiles.yml.
Check the fingerprint
If Snowflake rejects the key, compare the fingerprint it stored with the one of your file:
desc user DBT_SERVICE
->> select substr((select "value" from $1 where "property" = 'RSA_PUBLIC_KEY_FP'), len('SHA256:') + 1) as key;
openssl rsa -pubin -in rsa_key.pub -outform DER | openssl dgst -sha256 -binary | openssl enc -base64
The two values must be identical.
Errors you may see
Incorrect password, could not decrypt key: the passphrase is wrong, or the variable that holds it is empty in that environment.JWT token is invalid: Snowflake did not accept the key. Usually the public key on the user is not the one matching your private key, or it was pasted with its header.- A 404 on
login-request: theaccountvalue is wrong. Use the identifier in theorgname-accountnameform, as shown in Snowsight under your account details.
Things to know
- Who can attach a key. Setting
rsa_public_keyneeds ownership of the user, or theMODIFY PROGRAMMATIC AUTHENTICATION METHODSprivilege on it. - Rotation. A user has two slots. Put the new key in
rsa_public_key_2, switch dbt to the new private key, thenalter user DBT_SERVICE unset rsa_public_key. - Newer syntax. Snowflake also offers
alter user ... add key pair, which can limit a key to a role and give it an expiry date. - People are not services. Developers running dbt on their laptop sign in as themselves with SSO
(
authenticator: externalbrowser) or with a password plus MFA, not with the service user’s key.
Tested with dbt Core 1.12 and dbt-snowflake 1.12.4: dbt loaded the encrypted key and reached Snowflake’s login endpoint, and reported the error above for a wrong passphrase. The Snowflake SQL and the timeline come from Snowflake’s documentation.