sql.postgres.auth
import sql.postgres.auth
sqldoes not re-export this module, so it is reached only by importing it directly.
The authentication exchanges a PostgreSQL server can ask for.
Which one happens is the server’s choice, made from its pg_hba.conf. A
modern server asks for SCRAM-SHA-256; older ones ask for MD5, which
PostgreSQL itself now discourages; and a server set to trust asks for
nothing at all.
Constants
SCRAM_SHA_256
sql.postgres.auth.SCRAM_SHA_256 = 'SCRAM-SHA-256'
The mechanism this adapter implements. Channel binding is not offered,
so the server sees plain SCRAM-SHA-256 rather than the -PLUS variant.
Functions
md5_response()
sql.postgres.auth.md5_response(user: string, password: string, salt) -> string
Builds the response to an MD5 password request.
The scheme is md5(md5(password + user) + salt), hex encoded, with a
literal md5 in front. It is weak and PostgreSQL says so; it is here
because servers still ask for it.
Parameters
user(string)password(string)salt(bytes) — The four bytes the server sent.
Returns string
nonce()
sql.postgres.auth.nonce() -> string
A fresh SCRAM nonce.
It has to be unpredictable: the nonce is what stops a recorded exchange
being replayed, so it comes from the system’s random source rather than
from rand().
Returns string
parse_scram()
sql.postgres.auth.parse_scram(message: string) -> dict
Splits a SCRAM message into its key=value parts.
Parameters
message(string)
Returns dict
offers_scram()
sql.postgres.auth.offers_scram(payload) -> bool
Whether the server offered SCRAM-SHA-256 among its mechanisms.
Parameters
payload(bytes) — The mechanism list, zero-terminated names.
Returns bool
client_first()
sql.postgres.auth.client_first(client_nonce: string) -> dict
The client’s opening SCRAM message.
The username is left empty, which is what PostgreSQL expects: it takes the user from the startup message and ignores this one.
Parameters
client_nonce(string)
Returns dict — { bare, full }, the second with the channel
binding header the exchange is signed over.
client_proof()
sql.postgres.auth.client_proof(password: string, client_nonce: string, client_first_bare: string, server_first: string) -> dict
Works out the client’s proof from the server’s challenge.
This is SCRAM as RFC 5802 defines it: the password is salted and iterated into a salted password, which yields a client key, whose hash the server already holds. Signing the whole exchange with that hash and returning the signature XORed with the client key proves the password without sending it.
Parameters
password(string)client_nonce(string) — The nonce sent in the first message.client_first_bare(string) — The first message without its header.server_first(string) — The server’s reply, verbatim.
Returns dict — { message, server_signature }, the message to
send and the signature the server’s own reply has to match.
Raises AuthenticationError if the server’s reply is malformed or
its nonce does not extend the client’s.
verify_server()
sql.postgres.auth.verify_server(server_final: string, expected: string)
Checks the server’s closing message really is from a server that knows the password.
Skipping this would leave the exchange one-way, proving the client to the server but not the server to the client.
Parameters
server_final(string)expected(string) — Fromclient_proof().
Raises AuthenticationError if the signature is absent or wrong.
2026, Richard Ore and Zuri contributors