PostgreSQL 18 - SCRAM pass-through authentication for fdw connections

August 21, 2025

PostgreSQL 18 brings a nice improvement for folks using postgres_fdw or dblink_fdw: SCRAM pass-through authentication. You no longer need to store plain-text passwords in your USER MAPPING options when setting up a foreign server connection.

Here’s the commit that made it happen:

commit 761c79508e7fbc33c1b11754bdde4bd03ce9cbb3
Author: Peter Eisentraut <peter@eisentraut.org>
Date:   Wed Jan 15 17:55:18 2025 +0100

   postgres_fdw: SCRAM authentication pass-through

   This enables SCRAM authentication for postgres_fdw when connecting to
   a foreign server without having to store a plain-text password on user
   mapping options.

   This is done by saving the SCRAM ClientKey and ServeryKey from the
   client authentication and using those instead of the plain-text
   password for the server-side SCRAM exchange.  The new foreign-server
   or user-mapping option "use_scram_passthrough" enables this.

   Co-authored-by: Matheus Alcantara <mths.dev@pm.me>
   Co-authored-by: Peter Eisentraut <peter@eisentraut.org>
   Discussion: https://www.postgresql.org/message-id/flat/27b29a35-9b96-46a9-bc1a-914140869dac@gmail.com

As the commit message itself say, when the PostgreSQL server connects to a FOREIGN SERVER, if use_scram_passthrough is set, it uses the SCRAM keys from the original client connection instead of needing a plain-text password. It's more secure and avoids messy credential duplication.

To use this feature, make sure:

  • The foreign server requires scram-sha-256 auth (otherwise it'll just fail).
  • Only the "client side" (where you're using postgres_fdw or dblink_fdw) needs to be PostgreSQL 18+.
  • Both servers must have the same SCRAM secret for the user. That means literally the same hash, salt, and iteration count.
  • The initial connection from the client to the main server must also be using SCRAM (Hence “pass-through”: SCRAM must be used going in and out).

How to set it up with postgres_fdw

We're going to use two Postgres servers: one acting as the "incoming" (fdw client) and one as the "foreign" server.

Note that for these examples I'll use psql Postgres client.

1. Create the same user on both servers

CREATE USER example;

On the foreign server, create a sample table to query later:

CREATE TABLE fdw_table AS SELECT g as a, b+2 as b FROM generate_series(1,100) g(g);

Exit psql and log in again using the new created user and then set the passwords on both servers

\password

2. Update pg_hba.conf to require SCRAM

Both servers must be configured to enforce scram-sha-256:

local   all             all                                     scram-sha-256
host    all             all             127.0.0.1/32            scram-sha-256
host    all             all             ::1/128                 scram-sha-256

You can find the path to pg_hba.conf using:

SHOW hba_file;

3. Sync the encrypted password (SCRAM secret)

Grab the encrypted password from the incoming server:

SELECT rolpassword FROM pg_authid WHERE rolname = 'example';

Now set the exact same password (SCRAM hash) on the foreign server:

ALTER ROLE example PASSWORD 'scram-sha-256$...'; -- paste the whole thing

This step is crucial — the secrets must match exactly.

4. Set up postgres_fdw

On the incoming server:

CREATE EXTENSION IF NOT EXISTS postgres_fdw;

CREATE SERVER foreign_fdw
 FOREIGN DATA WRAPPER postgres_fdw
 OPTIONS (host 'localhost', dbname 'postgres', use_scram_passthrough 'true');

CREATE USER MAPPING FOR example
 SERVER foreign_fdw
 OPTIONS (user 'example');

Notice: No need to set a password in the mapping!

5. Import the foreign table

IMPORT FOREIGN SCHEMA public LIMIT TO (fdw_table)
 FROM SERVER foreign_fdw INTO public;

Now just run:

SELECT * FROM fdw_table;

Boom 💥 — we're querying across servers with SCRAM pass-through.

All setup steps are the same, but instead of importing tables, you'll call dblink_fdw() directly:

SELECT * FROM dblink('foreign_fdw', 'SELECT * FROM fdw_table')
 AS fdw_table(a int, b int);

Final Thoughts

SCRAM pass-through is a great feature for secure, credential-less connections between PostgreSQL servers. It's particularly useful in setups where you're federating access to multiple databases and don't want to juggle passwords in USER MAPPING.

Less boilerplate, more safety. That’s a win.

Share this