Conversation
PostgreSQL 18 added the `oauth` HBA method, which authenticates a connection with an OAuth 2.0 bearer token over the SASL OAUTHBEARER mechanism (RFC 7628) instead of a password. `authenticate_sasl` recognised only the SCRAM mechanisms, so such a server was refused with "unsupported SASL mechanism". The caller supplies the token through `Config::token_provider`, a closure called once per connection attempt so that an expiring token can be refreshed without rebuilding the config. Obtaining, caching and renewing tokens stays out of scope, as it is in libpq when a custom flow is installed: there is no discovery and no device authorization flow. No token is requested unless the server asks for OAUTHBEARER. A successful exchange is a single round trip. The bearer token travels in the SASL initial client response and the server replies with `AuthenticationOk`; OAUTHBEARER carries no additional data on success, so there is no `AuthenticationSASLFinal` message. On failure the server sends a challenge holding a JSON error document, which RFC 7628 Sec. 3.2.3 requires the client to answer with a lone kvsep byte - sending it is what gets the server to report the failure as its own `ErrorResponse` instead of the client giving up with an unexpected-message error. OAUTHBEARER defines no channel binding and the server rejects the `p` specifier, so the gs2 header is always `n,,` and `channel_binding=require` is refused. Picks up the approach from rust-postgres#1325 by @JoHaHu. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 29, 2026
Closed
geoHeil
marked this pull request as draft
August 29, 2026 09:20
`cargo clippy --all --all-targets` fails on master with `RUSTFLAGS=-Dwarnings`:
error: this lifetime isn't used in the type
--> tokio-postgres/tests/test/types/mod.rs:51:24
|
51 | T: PartialEq + for<'a> FromSqlOwned + ToSql + Sync,
| ^^
`FromSqlOwned` is declared as `pub trait FromSqlOwned: for<'a> FromSql<'a> {}`,
so it already carries the higher-ranked bound internally and takes no lifetime
parameter of its own. `for<'a> FromSqlOwned` therefore binds a lifetime nothing
uses, and removing the binder is behaviour-preserving by construction rather than
by inspection.
Not a new defect. `extra_unused_lifetimes` learned to see through an HRTB in a
clippy released after master's last CI run, and the workflow pins
`dtolnay/rust-toolchain@stable` rather than a version, so the next push to touch
CI meets the newer lint. Any PR opened now is red before it changes anything.
Reproduced with CI's own invocation and toolchain, clippy 0.1.98:
RUSTFLAGS=-Dwarnings cargo clippy --all --all-targets
without this change: exit=101
with this change: exit=0
`cargo fmt --all -- --check` clean, and `cargo test -p tokio-postgres --test
test --no-run` still builds the target.
Author
CI is green now — one commit, and it is not in the OAUTHBEARER change
|
geoHeil
marked this pull request as ready for review
August 29, 2026 11:38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1377.
PostgreSQL 18 added the
oauthHBA method, which authenticates a connection with an OAuth 2.0 bearer token over SASLOAUTHBEARER(RFC 7628) instead of a password.authenticate_saslmatched only the SCRAM mechanisms, so such a server was refused withunsupported SASL mechanism.Scope
Token-first only. The caller supplies a token, the crate performs the SASL exchange. No discovery, no device authorization flow, no conversation with an identity provider — the same line
node-postgresdraws, and the same thing libpq does when an application installs a custom flow. No token is requested unless the server asks forOAUTHBEARER.Verified in Pg18 docker postgres.
FYI: Clippy was failing on some files in CI - I have fixed this (inlined here) to have a green CI